Principles of Software Construction: Objects, Design, and …ckaestne/15214/s2017/slides/... ·...
Transcript of Principles of Software Construction: Objects, Design, and …ckaestne/15214/s2017/slides/... ·...
115-214
SchoolofComputerScience
PrinciplesofSoftwareConstruction:Objects,Design,andConcurrencyAPIDesign
ChristianKaestner BogdanVasilescu
ManyslidesstolenwithpermissionfromJoshBloch(thanks!)
215-214
Administrivia
• Homework4cduetonight• Homework4bfeedbackavailablesoon• Homework5releasedtomorrow–Workinteams
315-214
Part1:DesignataClassLevel
DesignforChange:InformationHiding,
Contracts,DesignPatterns,UnitTesting
DesignforReuse:Inheritance,Delegation,
Immutability,LSP,DesignPatterns
Part2:Designing(Sub)systems
UnderstandingtheProblem
ResponsibilityAssignment,DesignPatterns,GUIvsCore,
DesignCaseStudies
TestingSubsystems
DesignforReuseatScale:FrameworksandAPIs
Part3:DesigningConcurrent
Systems
ConcurrencyPrimitives,Synchronization
DesigningAbstractionsforConcurrency
DistributedSystemsinaNutshell
IntrotoJava
Git,CIStaticAnalysis
GUIsUML MoreGit
GUIsPerformance
Design
415-214
Agenda
• IntroductiontoAPIs:ApplicationProgrammingInterfaces
• AnAPIdesignprocess• Keydesignprinciple:Informationhiding• Concreteadviceforuser-centereddesign
515-214
Learninggoals• UnderstandandbeabletodiscussthesimilaritiesanddifferencesbetweenAPIdesignandregularsoftwaredesign– Relationshipbetweenlibraries,frameworksandAPIdesign
– Informationhidingasakeydesignprinciple• Acknowledge,andplanforfailuresasafundamentallimitationonadesignprocess
• Givenaproblemdomainwithusecases,beabletoplanacoherentdesignprocessforanAPIforthoseusecases,e.g.,"RuleofThrees"
615-214
API:ApplicationProgrammingInterface
• AnAPIdefinestheboundarybetweencomponents/modulesinaprogrammaticsystem
715-214
API:ApplicationProgrammingInterface
• AnAPIdefinestheboundarybetweencomponents/modulesinaprogrammaticsystem
815-214
API:ApplicationProgrammingInterface
• AnAPIdefinestheboundarybetweencomponents/modulesinaprogrammaticsystem
915-214
API:ApplicationProgrammingInterface
• AnAPIdefinestheboundarybetweencomponents/modulesinaprogrammaticsystem
1015-214
LibrariesandframeworksbothdefineAPIs
Library
Framework
public MyWidget extends JContainer {
ublic MyWidget(int param) {/ setup internals, without rendering}
/ render component on first view and resizingprotected void paintComponent(Graphics g) {// draw a red box on his componentDimension d = getSize();g.setColor(Color.red);
g.drawRect(0, 0, d.getWidth(), d.getHeight()); }}
public MyWidget extends JContainer {
ublic MyWidget(int param) {/ setup internals, without rendering}
/ render component on first view and resizingprotected void paintComponent(Graphics g) {// draw a red box on his componentDimension d = getSize();g.setColor(Color.red);
g.drawRect(0, 0, d.getWidth(), d.getHeight()); }}
your code
your code
API
API
1115-214
WhyisAPIdesignimportant?
• APIscanbeamongyourgreatestassets– Usersinvestheavily:acquiring,writing,learning– Costtostop usinganAPIcanbeprohibitive– SuccessfulpublicAPIscaptureusers
• Canalsobeamongyourgreatestliabilities– BadAPIcancauseunendingstreamofsupportcalls– Caninhibitabilitytomoveforward
1215-214
PublicAPIsareforever
Yourcode
Yourcolleague
Anothercolleague
SomebodyonthewebSomebodyonthewebSomebodyonthewebSomebodyonthewebSomebodyonthewebSomebodyonthewebSomebodyonthewebSomebodyontheweb
1315-214
PublicAPIsareforever
Eclipse(IBM)
JDTPlugin(IBM)
CDTPlugin(IBM)
UMLPlugin(thirdparty)
SomebodyonthewebSomebodyonthewebSomebodyonthewebSomebodyonthewebSomebodyonthewebSomebodyonthewebthirdpartyplugin
1415-214
Evolutionaryproblems:Public(used)APIsareforever
• "Onechancetogetitright"• Canonlyaddfeaturestolibrary• Cannot:– removemethodfromlibrary– changecontractinlibrary– changeplugininterfaceofframework
• DeprecationofAPIsasweakworkaround
awt.Component,deprecated since Java 1.1still included in 7.0
1515-214
GoodvsBadAPIs
• Lotsofreuse– includingfromyourself
• Lotsofusers/customers• Userbuy-inandlock-in
• Lostproductivity,inefficientreuse
• Maintenanceandcustomersupportliability
1615-214
CharacteristicsofagoodAPI
• Easytolearn• Easytouse,evenwithoutdocumentation• Hardtomisuse• Easytoreadandmaintaincodethatusesit• Sufficientlypowerfultosatisfyrequirements• Easytoevolve• Appropriatetoaudience
1715-214
Outlinefortoday
• TheProcessofAPIDesign• Keydesignprinciple:Informationhiding• Concreteadviceforuser-centereddesign
1815-214
AnAPIdesignprocess
• DefinethescopeoftheAPI– Collectuse-casestories,definerequirements– Beskeptical
• Distinguishtruerequirementsfromso-calledsolutions• "Whenindoubt,leaveitout."
• Draftaspecification,gatherfeedback,revise,andrepeat– Keepitsimple,short
• Codeearly,codeoften– Writeclientcode beforeyouimplementtheAPI
1915-214
PlanwithUseCases
• ThinkabouthowtheAPImightbeused?– e.g.,getthecurrenttime,computethedifferencebetweentwotimes,getthecurrenttimeinTokyo,getnextweek'sdateusingaMayacalendar,…
• Whattasksshoulditaccomplish?• Shouldallthetasksbesupported?– Ifindoubt,leaveitout!
• HowwouldyousolvethetaskswiththeAPI?
2015-214
Respecttheruleofthree
• ViaWillTracz (viaJoshBloch),ConfessionsofaUsedProgramSalesman:
Write3implementationsofeachabstractclassorinterfacebeforerelease
– "Ifyouwriteone,itprobablywon'tsupportanother."
– "Ifyouwritetwo,itwillsupportmorewithdifficulty."
– "Ifyouwritethree,itwillworkfine."
2115-214
Outline
• TheProcessofAPIDesign• Keydesignprinciple:Informationhiding• Concreteadviceforuser-centereddesign
2215-214
Whichonedoyouprefer?public class Point {
public double x;public double y;
}vs.
public class Point {private double x;private double y;public double getX() { /* … */ }public double getY() { /* … */ }
}
2315-214
Keydesignprinciple:Informationhiding
• "Whenindoubt,leaveitout.”
• ImplementationdetailsinAPIsareharmful– Confuseusers– Inhibitfreedomtochangeimplementation
2415-214
Keydesignprinciple:Informationhiding
• Makeclasses,membersasprivateaspossible– Youcanaddfeatures,butneverremoveorchangethebehavioralcontractforanexistingfeature
• Publicclassesshouldhavenopublicfields(withtheexceptionofconstants)
• Minimizecoupling– Allowsmodulestobe,understood,used,built,tested,debugged,andoptimizedindependently
2515-214
ApplyingInformationHiding:Fieldsvs Getter/SetterFunctions
public class Point {public double x;public double y;
}vs.
public class Point {private double x;private double y;public double getX() { /* … */ }public double getY() { /* … */ }
}
2615-214
Whichonedoyouprefer?
public class Rectangle {public Rectangle(Point e, Point f) …
}
vs.public class Rectangle {public Rectangle(PolarPoint e, PolarPoint f) …
}
2715-214
ApplyingInformationhiding:Interfacevs.ClassTypes
public class Rectangle {public Rectangle(Point e, Point f) …
}
vs.public class Rectangle {public Rectangle(PolarPoint e, PolarPoint f) …
}
2815-214
Still…
public class Rectangle {public Rectangle(Point e, Point f) …
}…Point p1 = new PolarPoint(…);Point p2 = new PolarPoint(…);Rectangle r = new Rectangle(p1, p2);
2915-214
Still…
public class Rectangle {public Rectangle(Point e, Point f) …
}…Point p1 = new PolarPoint(…);Point p2 = new PolarPoint(…);Rectangle r = new Rectangle(p1, p2);…Point p3 = new PolarPoint(…);Point p4 = new PolarPoint(…);Rectangle r2 = new Rectangle(p3, p4);…
3015-214
public class Rectangle {public Rectangle(Point e, Point f) …
}…Point p1 = PointFactory.Construct(…); // new PolarPoint(…); insidePoint p2 = PointFactory.Construct(…); // new PolarPoint(…); insideRectangle r = new Rectangle(p1, p2);
ApplyingInformationhiding:Factories
3115-214
ApplyingInformationhiding:Factories
• Considerimplementingafactorymethodinsteadofaconstructor
• Factorymethodsprovideadditionalflexibility– Canbeoverridden– Canreturninstanceofanysubtype;hidesdynamictypeofobject
– Canhaveadescriptivemethodname
3215-214
ApplyingInformationHiding:HideClientBoilerplateCode• Generallydoneviacut-and-paste• Ugly,annoying,anderror-prone
import org.w3c.dom.*;import java.io.*;import javax.xml.transform.*;import javax.xml.transform.dom.*;import javax.xml.transform.stream.*;
/** DOM code to write an XML document to a specified output stream. */static final void writeDoc(Document doc, OutputStream out)throws IOException{
try {Transformer t = TransformerFactory.newInstance().newTransformer();t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doc.getDoctype().getSystemId());t.transform(new DOMSource(doc), new StreamResult(out)); // Does actual writing
} catch(TransformerException e) {throw new AssertionError(e); // Can’t happen!
}}
3315-214
TheexceptionhierarchyinJava
Throwable
Exception
RuntimeException IOException
EOFException
FileNotFoundException
NullPointerException
IndexOutOfBoundsException
ClassNotFoundException… …
. . .
Object
Recall unchecked
checked
3415-214
ApplyingInformationHiding:HideClientBoilerplateCode• Generallydoneviacut-and-paste• Ugly,annoying,anderror-prone
import org.w3c.dom.*;import java.io.*;import javax.xml.transform.*;import javax.xml.transform.dom.*;import javax.xml.transform.stream.*;
/** DOM code to write an XML document to a specified output stream. */static final void writeDoc(Document doc, OutputStream out)throws IOException{
try {Transformer t = TransformerFactory.newInstance().newTransformer();t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doc.getDoctype().getSystemId());t.transform(new DOMSource(doc), new StreamResult(out)); // Does actual writing
} catch(TransformerException e) {throw new AssertionError(e); // Can’t happen!
}}
Won’t compile
3515-214
ApplyingInformationHiding:HideInformationDetails• Subtleleaksofimplementationdetailsthrough– Documentation• E.g.,donotspecifyhashfunctions
– Implementation-specificreturntypes/exceptions• E.g.,PhonenumberAPIthatthrowsSQLexceptions
– Outputformats• E.g.,implements Serializable
• Lackofdocumentationà Implementationbecomesspecificationà nohiding
3615-214
Outline
• TheProcessofAPIDesign• Keydesignprinciple:Informationhiding• Concreteadviceforuser-centereddesign
3715-214
Applyprinciplesofuser-centereddesign
• e.g.,"PrinciplesofUniversalDesign"– Equitableuse
• Designisusefulandmarketabletopeoplewithdiverseabilities– Flexibilityinuse
• Designaccommodatesawiderangeofindividualpreferences– Simpleandintuitiveuse
• Useofthedesigniseasytounderstand– Perceptibleinformation
• Designcommunicatesnecessaryinformationeffectivelytouser– Toleranceforerror– Lowphysicaleffort– Sizeandspaceforapproachanduse
3815-214
Achievingflexibilityinusewhileremainingsimpleandintuitive:Minimizeconceptualweight
• APIshouldbeassmallaspossiblebutnosmaller–Whenindoubt,leaveitout
• Conceptualweight:HowmanyconceptsmustaprogrammerlearntouseyourAPI?– APIsshouldhavea"highpower-to-weightratio"
• Goodexamples:– java.util.*– java.util.Collections
3915-214
What’swronghere?
public class Thread implements Runnable {// Tests whether current thread has been interrupted.// Clears the interrupted status of current thread.public static boolean interrupted();
}
4015-214
Unintuitivebehavior:sideeffects
• UserofAPIshouldnotbesurprisedbybehavior,aka“theprincipleofleastastonishment”– It'sworthextraimplementationeffort– It'sevenworthreducedperformance
public class Thread implements Runnable {
// Tests whether current thread has been interrupted.// Clears the interrupted status of current thread.public static boolean interrupted();
}
4115-214
• Dowhatyousayyoudo:– "Don'tviolatethePrincipleofLeastAstonishment"
Goodnamesdrivegooddesign
4215-214
Discussthesenames
– get_x()vs getX()– Timervs timer– isEnabled()vs.enabled()– computeX()vs.generateX()?– deleteX()vs.removeX()?
4315-214
Goodnamesdrivegooddesign(2)
• Followlanguage- andplatform-dependentconventions,e.g.,– Typographical:
• get_x() vs. getX()• timer vs. Timer, HTTPServlet vs HttpServlet• edu.cmu.cs.cs214
– Gramatical (seenext)
4415-214
Goodnamesdrivegooddesign(3)
• Nounsforclasses– BigInteger,PriorityQueue
• Nounsoradjectivesforinterfaces– Collection,Comparable
• Nouns,linkingverbsorprepositionsfornon-mutativemethods– size,isEmpty,plus
• Actionverbsformutativemethods– put,add,clear
4515-214
Goodnamesdrivegooddesign(4)• Useclear,specificnamingconventions– getX() andsetX() forsimpleaccessors andmutators
– isX() forsimpleboolean accessors– computeX() formethodsthatperformcomputation– createX() ornewInstance() forfactorymethods– toX() formethodsthatconvertthetypeofanobject– asX() forwrapperoftheunderlyingobject
4615-214
Goodnamesdrivegooddesign(5)
• Beconsistent– computeX()vs.generateX()?– deleteX()vs.removeX()?
• Avoidcrypticabbreviations– Good:Font,Set,PrivateKey,Lock,ThreadFactory,TimeUnit,Future<T>
– Bad:DynAnyFactoryOperations,_BindingIteratorImplBase,ENCODING_CDR_ENCAPS,OMGVMCID
4715-214
DonotviolateLiskov'sbehavioralsubtypingrules
• Useinheritanceonlyfortruesubtypes• Examples:
1) class Stack extends Vector …
2) // A Properties instance maps Strings to Stringspublic class Properties extends HashTable {
public Object put(Object key, Object value);…
}
4815-214
Favorcompositionoverinheritance
// A Properties instance maps Strings to Stringspublic class Properties extends HashTable {
public Object put(Object key, Object value);…
}
public class Properties {private final HashTable data = new HashTable();public String put(String key, String value) {
data.put(key, value);}…
}
4915-214
Minimizemutability
• Classesshouldbeimmutableunlessthere’sagoodreasontodootherwise– Advantages:simple,thread-safe,reusable– Disadvantage:separateobjectforeachvalue
Bad:Date,CalendarGood:TimerTask
5015-214
• Component.getSize() returnsDimension• Dimension ismutable• EachgetSize callmustallocateDimension • Causesmillionsofneedlessobjectallocations• AlternativeaddedinJava1.2butoldclientcodestillslow:getX(),getY()
• Documentmutability– Carefullydescribestatespace–Makeclearwhenit'slegaltocallwhichmethod
Mutabilityandperformance
5115-214
Overloadmethodnamesjudiciously• Avoidambiguousoverloadsforsubtypes
– Recallthesubtletiesofmethoddispatch:public class Point() {
private int x;private int y;public boolean equals(Point p) {
return this.x == p.x && this.y == p.y;}
}• Ifyoumustbeambiguous,implementconsistentbehavior
public class TreeSet implements SortedSet {public TreeSet(Collection c); // Ignores order.public TreeSet(SortedSet s); // Respects order.
}
5215-214
Useappropriateparameter&returntypes
• Favorinterfacetypesoverclassesforinput– Providesflexibility,performance
• Usemostspecificpossibleinputparametertype– Moveserrorfromruntimetocompiletime
• Don'tuseString ifabettertypeexists– Stringsarecumbersome,error-prone,andslow
• Don'tusefloatingpointformonetaryvalues– Binaryfloatingpointcausesinexactresults!
• Usedouble (64bits)ratherthanfloat (32bits)– Precisionlossisreal,performancelossnegligible
5315-214
Useconsistentparameterordering
• AnegregiousexamplefromC:– char* strncpy(char* dest, char* src, size_t n);– void bcopy(void* src, void* dest, size_t n);
5415-214
Useconsistentparameterordering
• AnegregiousexamplefromC:– char* strncpy(char* dest, char* src, size_t n);– void bcopy(void* src, void* dest, size_t n);
• Somegoodexamples:java.util.Collections – firstparameteralwayscollectiontobemodifiedorqueried
java.util.concurrent – timealwaysspecifiedaslongdelay,TimeUnit unit
5515-214
Avoidlonglistsofparameters• Especiallywithrepeatedparametersofthesametype
HWND CreateWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName,DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam);
• Longlistsofidenticallytypedparams harmful– Programmerstransposeparametersbymistake– Programsstillcompileandrun,butmisbehave!
• Threeorfewerparametersisideal• Techniquesforshorteningparameterlists– Breakupmethod– Createhelperclasstoholdparameters– BuilderPattern
5615-214
What’swronghere?
// A Properties instance maps Strings to Stringspublic class Properties extends HashTable {
public Object put(Object key, Object value);
// Throws ClassCastException if this instance// contains any keys or values that are not Stringspublic void save(OutputStream out, String comments);
}
5715-214
Failfast• Reporterrorsassoonastheyaredetectable– Checkpreconditionsatthebeginningofeachmethod– Avoiddynamictypecasts,run-timetype-checking
// A Properties instance maps Strings to Stringspublic class Properties extends HashTable {
public Object put(Object key, Object value);
// Throws ClassCastException if this instance// contains any keys or values that are not Stringspublic void save(OutputStream out, String comments);
}
5815-214
Throwexceptionstoindicateexceptionalconditions• Don’tforceclienttouseexceptionsforcontrolflow
private byte[] a = new byte[CHUNK_SIZE];
void processBuffer (ByteBuffer buf) {try {
while (true) {buf.get(a);processBytes(a, CHUNK_SIZE);
}} catch (BufferUnderflowException e) {
int remaining = buf.remaining();buf.get(a, 0, remaining);processBytes(a, remaining);
}}
• Conversely,don’tfailsilentlyThreadGroup.enumerate(Thread[] list)
5915-214
Avoidcheckedexceptionsifpossible
• Overuseofcheckedexceptionscausesboilerplatetry {
Foo f = (Foo) g.clone();} catch (CloneNotSupportedException e) {
// Do nothing. This exception can't happen.
}
6015-214
Avoidreturnvaluesthatdemandexceptionalprocessing• Returnzero-lengtharrayoremptycollection,notnull
package java.awt.image;public interface BufferedImageOp {
// Returns the rendering hints for this operation,// or null if no hints have been set.public RenderingHints getRenderingHints();
}
• DonotreturnaString ifabettertypeexists
6115-214
Don'tletyouroutputbecomeyourdefactoAPI• Documentthefactthatoutputformatsmayevolveinthefuture
• Provideprogrammaticaccesstoalldataavailableinstringformpublic class Throwable {public void
printStackTrace(PrintStream s);}
6215-214
Don'tletyouroutputbecomeyourdefactoAPI• Documentthefactthatoutputformatsmayevolveinthefuture• Provideprogrammaticaccesstoalldataavailableinstringform
public class Throwable {public void printStackTrace(PrintStream s);public StackTraceElement[] getStackTrace();
}
public final class StackTraceElement {public String getFileName();public int getLineNumber();public String getClassName();public String getMethodName();public boolean isNativeMethod();
}
6315-214
Documentationmatters
Reuseissomethingthatisfareasiertosaythantodo.Doingitrequiresbothgooddesignandverygooddocumentation.Evenwhenweseegooddesign,whichisstillinfrequently,wewon'tseethecomponentsreusedwithoutgooddocumentation.
– D.L.Parnas,SoftwareAging.Proceedingsofthe16thInternationalConferenceonSoftwareEngineering,1994
6415-214
ContractsandDocumentation
• APIsshouldbeself-documenting– Goodnamesdrivegooddesign
• Documentreligiouslyanyway– Allpublicclasses– Allpublicmethods– Allpublicfields– Allmethodparameters– Explicitlywritebehavioralspecifications
• Documentationisintegraltothedesignanddevelopmentprocess
6515-214
Summary
• Acceptthefactthatyou,andothers,willmakemistakes– UseyourAPIasyoudesignit– Getfeedbackfromothers– Thinkintermsofusecases(domainengineering)– Hideinformationtogiveyourselfmaximumflexibilitylater
– Designforinattentive,hurriedusers– Documentreligiously
6615-214
BONUS:APIREFACTORING
6715-214
1.Sublist operationsinVectorpublic class Vector {
public int indexOf(Object elem, int index);public int lastIndexOf(Object elem, int index);...
}
• Notverypowerful- supportsonlysearch• Hardtousewithoutdocumentation
6815-214
Sublistoperationsrefactoredpublic interface List {
List subList(int fromIndex, int toIndex);...
}
• Extremelypowerful- supportsall operations• Useofinterfacereducesconceptualweight– Highpower-to-weightratio
• Easytousewithoutdocumentation
6915-214
2.Thread-localvariables
// Broken - inappropriate use of String as capability.// Keys constitute a shared global namespace.public class ThreadLocal {
private ThreadLocal() { } // Non-instantiable
// Sets current thread’s value for named variable.public static void set(String key, Object value);
// Returns current thread’s value for named variable.public static Object get(String key);
}
7015-214
Thread-localvariablesrefactored(1)public class ThreadLocal {
private ThreadLocal() { } // Noninstantiable
public static class Key { Key() { } }
// Generates a unique, unforgeable keypublic static Key getKey() { return new Key(); }
public static void set(Key key, Object value);public static Object get(Key key);
}
• Works,butrequiresboilerplatecodetousestatic ThreadLocal.Key serialNumberKey = ThreadLocal.getKey();ThreadLocal.set(serialNumberKey, nextSerialNumber());System.out.println(ThreadLocal.get(serialNumberKey));
7115-214
Thread-localvariablesrefactored(2)public class ThreadLocal<T> {
public ThreadLocal() { }public void set(T value);public T get();
}
• RemovesclutterfromAPIandclientcodestatic ThreadLocal<Integer> serialNumber =
new ThreadLocal<Integer>();serialNumber.set(nextSerialNumber());System.out.println(serialNumber.get());