principles of software construction: objects, design, and …ckaestne/15214/s2017/slides/... ·...

Post on 26-May-2020

5 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

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());

top related