Principles of Software Construction: Objects, Design, and...
Transcript of Principles of Software Construction: Objects, Design, and...
117-214
PrinciplesofSoftwareConstruction:Objects,Design,andConcurrency
Invariants,immutability,andtesting
JoshBloch CharlieGarrodDaryaMelicher
217-214
Administrivia
• Homework4adueThursdayat11:59p.m.– Mandatorydesignreviewmeetingbeforethehomeworkdeadline
• PAvoterregistrationdeadline:Tuesday,October9th– https://www.pavoterservices.pa.gov/pages/VoterRegistrationApplication.aspx
317-214
Unfinishedbusiness
417-214
AsimplesolutiontoHW2– Mainclass
517-214
HowdoweturnHW2intoHW3?
617-214
Lessons(practical)
• Chooselowlevelabstractionsthatmakehigherleveltaskseasy• Whenyouwanttorepresentafixedsetofvaluesknownat
compiletime,considerenums• Ifusersneedtoextendthesetconsideremulatedextensibleenum• Bittwiddlingshouldbepartofeveryprogrammerstoolkit
– Don’toveruseit…– Butdoconsiderit,especiallywhenyouneedhighperformance
717-214
Lessons(philosophical)
• Goodhabitsmatter– “Thewaytowriteaperfectprogramistomakeyourselfaperfect
programmerandthenjustprogramnaturally.”– WattsS.Humphrey,1994
• Don’tjusthackitupandsayyou’llfixitlater– Youprobablywon’t– butyouwillgetintothehabitofjusthackingitup– Alsoit’swaymore fun toworkonnice,well-structuredcode
• Evensmalldesigndecisionsmatter– Ifyourcodeisgettingugly,gobacktothedrawingboard– “Aweekofcodingcanoftensaveawholehourofthought.”
• Striveforclarity– It’snotenoughtobemerelycorrect;aimforclearlycorrect
817-214
Outline
• Classinvariantsanddefensivecopying• Immutability• Testingandcoverage• Testingforcomplexenvironments
917-214
Classinvariants
• Criticalpropertiesofthefieldsofanobject• Establishedbytheconstructor• Maintainedbypublicmethodinvocations
– Maybeinvalidatedtemporarilyduringmethodexecution
1017-214
Safelanguagesandrobustprograms
• UnlikeC/C++,Javalanguagesafe– Immunetobufferoverruns,wildpointers,etc.
• Makesitpossibletowriterobust classes– Correctnessdoesn’tdependonothermodules– Eveninsafelanguage,requiresprogrammereffort
1117-214
Defensiveprogramming
• Assumeclientswilltrytodestroyinvariants– Mayactuallybetrue(malicioushackers)– Morelikely:honestmistakes
• Ensureclassinvariantssurviveanyinputs– Defensivecopying– Minimizingmutability
1217-214
Thisclassisnotrobust
public final class Period {private final Date start, end; // Invariant: start <= end
/*** @throws IllegalArgumentException if start > end* @throws NullPointerException if start or end is null*/public Period(Date start, Date end) {
if (start.after(end))throw new IllegalArgumentException(start + " > " + end);
this.start = start;this.end = end;
}
public Date start() { return start; }public Date end() { return end; }... // Remainder omitted
}
1317-214
Theproblem:DateismutableObsoleteasofJava8;sadlynotdeprecatedeveninJava11
// Attack the internals of a Period instanceDate start = new Date(); // (The current time)Date end = new Date(); // " " "Period p = new Period(start, end);end.setYear(78); // Modifies internals of p!
1417-214
Thesolution:defensivecopying
// Repaired constructor - defensively copies parameterspublic Period(Date start, Date end) {
this.start = new Date(start.getTime());this.end = new Date(end.getTime());if (this.start.after(this.end))
throw new IllegalArgumentException(start + " > "+ end);}
1517-214
Afewimportantdetails
• Copiesmadebeforecheckingparameters• Validitycheckperformedoncopies• Eliminateswindowofvulnerabilitybetweenvaliditycheck©• ThwartsmultithreadedTOCTOUattack
– Time-Of-Check-To-Time-Of-U
// BROKEN - Permits multithreaded attack!public Period(Date start, Date end) {
if (start.after(end))throw new IllegalArgumentException(start + " > " + end);
// Window of vulnerabilitythis.start = new Date(start.getTime());this.end = new Date(end.getTime());
}
1617-214
Anotherimportantdetail
• Usedconstructor,notclone,tomakecopies– NecessarybecauseDateclassisnonfinal– Attackercouldimplementmalicioussubclass
• Recordsreferencetoeachextantinstance• Providesattackerwithaccesstoinstancelist
• Butwhousesclone,anyway?[EJItem11]
1717-214
Unfortunately,constructorsareonlyhalfthebattle
// Accessor attack on internals of PeriodPeriod p = new Period(new Date(), new Date());Date d = p.end();p.end.setYear(78); // Modifies internals of p!
1817-214
Thesolution:moredefensivecopying
// Repaired accessors - defensively copy fieldspublic Date start() {
return new Date(start.getTime());}public Date end() {
return new Date(end.getTime());}
NowPeriodclassisrobust!
1917-214
Summary
• Don’tincorporatemutableparametersintoobject;makedefensivecopies
• Returndefensivecopiesofmutablefields…• Orreturnunmodifiable viewofmutablefields• Reallesson– useimmutable components
– Eliminatestheneedfordefensivecopying
2017-214
Outline
• Classinvariantsanddefensivecopying• Immutability• Testingandcoverage• Testingforcomplexenvironments
2117-214
Immutableclasses
• Classwhoseinstancescannotbemodified• Examples:String,Integer,BigInteger,Instant• How,why,andwhentousethem
2217-214
Howtowriteanimmutableclass
• Don’tprovideanymutators• Ensurethatnomethodsmaybeoverridden• Makeallfieldsfinal• Makeallfieldsprivate• Ensuresecurityofanymutablecomponents
2317-214
public final class Complex {private final double re, im;
public Complex(double re, double im) {this.re = re;this.im = im;
}
// Getters without corresponding setterspublic double realPart() { return re; }public double imaginaryPart() { return im; }
// minus, times, dividedBy similar to addpublic Complex plus(Complex c) {
return new Complex(re + c.re, im + c.im);}
Immutableclassexample
2417-214
@Override public boolean equals(Object o) {if (!(o instanceof Complex)) return false;Complex c = (Complex) o;return Double.compare(re, c.re) == 0 &&
Double.compare(im, c.im) == 0;}
@Override public int hashCode() {return 31 * Double.hashCode(re) + Double.hashCode(im);
}
@Override public String toString() {return String.format("%d + %di", re, im)";
}}
Immutableclassexample(cont.)Nothinginterestinghere
2517-214
Distinguishingcharacteristic
• Returnnewinstanceinsteadofmodifying• Functionalprogramming• Mayseemunnaturalatfirst• Manyadvantages
2617-214
Advantages
• Simplicity• InherentlyThread-Safe• Canbesharedfreely• Noneedfordefensivecopies• Excellentbuildingblocks
2717-214
Majordisadvantage
• Separateinstanceforeachdistinctvalue• Creatingtheseinstancescanbecostly
BigInteger moby = ...; // A million bits longmoby = moby.flipBit(0); // Ouch!
• Problemmagnifiedformultistepoperations– Well-designedimmutableclassesprovidecommonmultistepoperations
• e.g.,myBigInteger.modPow(exponent, modulus)– Alternative:mutablecompanionclass
• e.g.,StringBuilder forString
2817-214
Whentomakeclassesimmutable
• Always,unlessthere'sagoodreasonnotto• Alwaysmakesmall“valueclasses”immutable!
– Examples:Color,PhoneNumber,Unit– Date andPoint weremistakes!– Expertsoftenuselong insteadofDate
2917-214
Whentomakeclassesmutable
• Classrepresentsentitywhosestatechanges– Real-world- BankAccount,TrafficLight– Abstract- Iterator, Matcher, Collection– Processclasses- Thread,Timer
• Ifclassmustbemutable,minimizemutability– Constructorsshouldfullyinitializeinstance– Avoidreinitializemethods
3017-214
Outline
• ClassInvariants• Immutability• Testingandcoverage• Testingforcomplexenvironments
3117-214
Whydowetest?
3217-214
Testingdecisions
• Whotests?– Developerswhowrotethecode– QualityAssuranceTeamandTechnicalWriters– Customers
• Whentotest?– Beforeandduringdevelopment– Aftermilestones– Beforeshipping– Aftershipping
3317-214
Testdrivendevelopment
• Writetestsbeforecode• Neverwritecodewithoutafailingtest• Codeuntilthefailingtestpasses
3417-214
Whyusetestdrivendevelopment?
• Forcesyoutothinkaboutinterfacesearly• Higherproductquality
– Bettercodewithfewerdefects
• Highertestsuitequality• Higherproductivity• It’sfuntowatchtestspass
3517-214
TDDinpractice
• EmpiricalstudiesonTDDshow:– Mayrequiremoreeffort– Mayimprovequalityandsavetime
• SelectiveuseofTDDisbest• AlwaysuseTDDforbugreports
– Regressiontests
3617-214
Howmuchtesting?
• Yougenerallycannottestallinputs– Toomany– usuallyinfinite
• Butwhenitworks,exhaustivetestingisbest!
3717-214
Whatmakesagoodtestsuite?
• Provideshighconfidencethatcodeiscorrect• Short,clear,andnon-repetitious
– Moredifficultfortestsuitesthanregularcode– Realistically,testsuiteswilllookworse
• Canbefuntowriteifapproachedinthisspirit
3817-214
Nextbestthingtoexhaustivetesting:randominputs
• Alsoknowasfuzztesting,torturetesting• Try“random”inputs,asmanyasyoucan
– Chooseinputstotickleinterestingcases– Knowledgeofimplementationhelpshere
• Seedrandomnumbergeneratorsotestsrepeatable
3917-214
Black-boxtesting
• Lookatspecifications,notcode• Testrepresentativecases• Testboundaryconditions• Testinvalid(exception)cases• Don’ttestunspecifiedcases
4017-214
White-boxtesting
• Lookatspecificationsand code• Writeteststo:
– Checkinterestingimplementationcases– Maximizebranchcoverage
4117-214
Codecoveragemetrics
• Methodcoverage– coarse• Branchcoverage– fine• Pathcoverage– toofine
– Costishigh,valueislow– (Relatedtocyclomatic complexity)
4217-214
Coveragemetrics:usefulbutdangerous
• Cangivefalsesenseofsecurity• Examplesofwhatcoverageanalysiscouldmiss
– Datavalues– Concurrencyissues– raceconditions,etc.– Usabilityproblems– Customerrequirementsissues
• Highbranchcoverageisnot sufficient
4317-214
Testsuites– idealandreal
• Idealtestsuiteswould– Uncoverallerrorsincode– Test“non-functional”attributessuchasperformanceandsecurity– Minimumsizeandcomplexity
• RealtestSuites– Uncoversomeportionoferrorsincode– Haveerrorsoftheirown– Arenonethelesspriceless
4417-214
Outline
• Classinvariants• Immutability• Testingandcoverage• Testingforcomplexenvironments
4517-214
Problemswhentestingsomeapps
• User-facingapplications– Usersclick,drag,etc.,andinterpretoutput– Timingissues
• Testingagainstbiginfrastructure– Databases,webservices,etc.
• Realworldeffects– Printing,mailingdocuments,etc.
• Collectivelycomprisethe testenvironment
4617-214
Example– Tiramisuapp
• Mobilerouteplanningapp• AndroidUI• BackenduseslivePATdata
4717-214
Anotherexample
• 3rdpartyFacebook apps• Androiduserinterface• BackendusesFacebook data
4817-214
Testinginrealenvironments
Code FacebookAndroidclient
void buttonClicked() {render(getFriends());
}
List<Friend> getFriends() {Connection c = http.getConnection();FacebookApi api = new FacebookApi(c);List<Node> persons = api.getFriends("john");for (Node person1 : persons) {
for (Node person2 : persons) {…}
}return result;
}
4917-214
EliminatingAndroiddependency
Code FacebookTestdriver
@Test void testGetFriends() {... // A Junit test
}
List<Friend> getFriends() {Connection c = http.getConnection();FacebookApi api = new FacebookApi(c);List<Node> persons = api.getFriends("john");for (Node person1 : persons) {
for (Node person2 : persons) {…}
}return result;
}
5017-214
Thatwon’tquitework
• GUIapplicationsprocessmany thousands ofevents• Solution:automatedGUItestingframeworks
– AllowstreamsofGUIeventstobecaptured,replayed
• Thesetoolsaresometimescalledrobots
5117-214
EliminatingFacebook dependency
Code MockFacebook
@Test void testGetFriends() {... // A Junit test
}
List<Friend> getFriends() {FacebookApi api = new MockFacebook(c);List<Node> persons = api.getFriends("john");for (Node person1 : persons) {
for (Node person2 : persons) {…}
}return result;
}
Testdriver
5217-214
Thatwon’tquitework!
• Changingproductioncodefortestingunacceptable• Problemcausedbyconstructor incode• Insteadofconstructor,usespecialfactorythatallowsalternative
implementations• Usetoolstofacilitatethissortoftesting
– Dependencyinjectiontools,e.g.,Dagger,Guice,Spring– MockobjectframeworkssuchasMockito
5317-214
Faultinjection
• Mockscanemulatefailuressuchastimeouts• Allowsyoutoverifytherobustnessofsystemagainstfaultsthat
youcan’tgenerateatwill
Code MockFacebookTestdriver
5417-214
Advantagesofusingmocks
• Testcodelocallywithoutlargeenvironment• Enabledeterministictests(insomecases)• Enablefaultinjection• Canspeeduptestexecution
– e.g.,avoidslowdatabaseaccess
• Cansimulatefunctionalitynotyetimplemented• Enabletestautomation
5517-214
DesignImplications
• Thinkabouttestabilitywhenwritingcode• Whenamockmaybeappropriate,designforit• Hidesubsystemsbehindaninterfaces• Usefactories,notconstructorstoinstantiate• Useappropriatetools
– Dependencyinjectionormockingframeworks
5617-214
MoreTestingin15-313FoundationsofSoftwareEngineering
• Manualtesting• Securitytesting,penetrationtesting• Fuzztestingforreliability• Usabilitytesting• GUI/Webtesting• Regressiontesting• Differentialtesting• Stress/soaktesting
5717-214
Conclusion
• Tomaintainclassinvariants– Minimizemutability– Makedefensivecopieswhererequired
• Interfacetestingiscritical– Designinterfacestofacilitatetesting– Writecreativetestsuitesthatmaximizepower-to-weightratio– Coveragetoolscanhelpgaugetestsuitequality
• Testingappswithcomplexenvironmentsrequiresaddedeffort