390 likes | 516 Views
Patterns. Intention revealing selector Naming based on side effects Double-dispatching method Accessing methods Query methods Boolean property setting Converter methods Constructors. Composed Method. How big should a method be? Write methods that perform one identifiable task.
E N D
Intention revealing selector • Naming based on side effects • Double-dispatching method • Accessing methods • Query methods • Boolean property setting • Converter methods • Constructors
Composed Method • How big should a method be? • Write methods that perform one identifiable task. • Few lines per method. • Consistent level of abstraction. • Minimizes number of methods you have to change in subclass. • Minimizes code copying in subclass.
Composed Method Usage • Top down • - self input; process; output • Bottom up • - common expressions • - long loop bodies • - comments • From client - two or more messages to another object is suspicious
Methods from Comments • Comments indicate "identifiable task" • If you need to comment a block of code, it probably should be a separate method. • Turn method comment into method name.
Reversing Method • Good code has a rhythm that makes it easy to understand. Code that breaks the rhythm is harder to read and understand. • Point • printOn: aStream • x printOn: aStream. • aStream nextPutAll: ‘ @’. • y printOn: aStream
Reversing method • Point • printOn: aStream • aStream • print: x; • nextPutAll: ‘ @’; • print: y
Reversing method • Make a method on the parameter. • Derive its name from the original message. • Take the original receiver as a parameter. • Implement the method by sending the original message to the original receiver.
Method object • What do you do about a method that is too big but that can’t be decomposed because of all the temporary variables that are used? • Turn method into a class. Temporary variables and arguments become instance variables. Then you can decompose the method.
Example of Method Object • In class Class • readNewSubclassOn: aCodeReader in: aNameSpace • | fullName numInstVars numMetaInstVars • metaclassFormat allMetaclassInstVars metaclassInstVars classFormat allClassInstVars classInstVars newMeta class bookkeepingInfoStart bookkeepingInfoSize | • fullName := aCodeReader readByteSymbol.
aCodeReader fileFormat >= 10 • ifTrue: • [metaclassFormat := aCodeReader readLong. • numMetaInstVars := aCodeReader readLong. • allMetaclassInstVars := aCodeReader readByteStringCollection. • metaclassInstVars := allMetaclassInstVars • copyFrom: allMetaclassInstVars size - numMetaInstVars + 1 • to: allMetaclassInstVars size. • classFormat := aCodeReader readLong. • numInstVars := aCodeReader readLong. • allClassInstVars := aCodeReader readByteStringCollection. • classInstVars := allClassInstVars copyFrom: allClassInstVars size - numInstVars + 1 to: allClassInstVars size] • ifFalse: • [metaclassFormat := aCodeReader readLong. • metaclassInstVars := aCodeReader readByteStringCollection. • numMetaInstVars := metaclassInstVars size. • classFormat := aCodeReader readLong. • classInstVars := aCodeReader readByteStringCollection. • numInstVars := classInstVars size].
Make class NewSubclassReader, a subclass of Object. • Make instance variables for arguments, receiver, and temporaries: aCodeReader aNameSpace superclass fullName numInstVars numMetaInstVars metaclassFormat allMetaclassInstVars metaclassInstVars classFormat allClassInstVars classInstVars newMeta class
readNewSubclassOn: aCodeReader in: aNameSpace • (NewSubclassReader on: aCodeReader in: aNameSpace) readNewSubclass
readNewSubclass • self initializeClassInformation. • self makeNewMetaclass. • self makeNewClass. • self isNewClassIncompatibleWithSuperclass ifTrue: • [CodeReader invalidClassFormatSignal • raiseWith: fullName • errorString: ' : ', fullName]. • self checkShapeChange. • self readSubclassInfoFromCodeReader. • ^class
Extract method • Turn an expression into a method of its own • References to arguments or temporaries turn into arguments of new method. • References to instance variables don’t change • ^ in the expression can be a problem
Execute Around Method • Pairs of actions that must be taken together • Open file / close file • Start tag / end tag • aCursor showWhile: [ … ] • aFile openDuring: [ aThing printOn: aFile]
Simple Superclass Name • What should we call the root of a hierarchy? • Complex name conveys full meaning. • Simple name is easy to say, type, extend. • But need to show that subclasses are related.
Simple Superclass Name • Give superclasses simple names: two or (preferably) one word • - Number • - Collection • - VisualComponent
Qualified Subclass Name • What should you call a subclass that plays a role similar to its superclass? • Unique name conveys most information • Derived name communicates relationship to superclass
Qualified Subclass Name • Use names with obvious meaning. Otherwise, prepend an adjective to most important superclass. • - OrderedCollection • - UndefinedObject • - CloneFigureCommand, CompositeCommand, ConnectionCommand
Variables: Roles vs. Types • Types are specified by classes • aRectangle • aCollection • aView • Roles - how an object is used • location • employees • topView
Role Suggesting Instance Variable • What should you name an instance variable? • Type is important for understanding implementation. But class comment can describe type. • Role communicates intent, and this is harder to understand than type.
Role Suggesting Instance Variable • Name instance variables for the role they play. Make the name plural if the variable is a collection. • Point: x, y • Interval: start, stop, step • Polyline: vertices
Type Suggesting Parameter Name • Name of variable can either communicate type or role. • Keywords communicate their parameter's role, so name of variable should give new information.
Type Suggesting Parameter Name • Name parameters according to their most general expected class, preceded by "a" or "an". If there is more than one parameter with the same expected class, precede the class with a descriptive word.
Temporaries • Name temporaries after role they play. • Use temporaries to: • collect intermediate results • reuse result of an expression • name result of an expression • Methods are simpler when they don't use temporaries!
Class Comments • Every class should have a comment that describes the purpose of the class and the types of its instance variables
Method comments • Name of method should describe what it does. • Name of arguments should describe what they are. • Name of method should describe return type. • Method comment should describe anything else that isn’t obvious.
Common State • Different instances of a class have different values for some state. How do you represent it? • Declare an instance variable in the class. • Name the variable using Role Suggesting Instance Variable Name. Initialize it with either Lazy Initialization, Explicit Initialization, or a Constructor Parameter Method.
Explicit Initialization • How do you initialize instance variables to their default value? • Make the code readable instead of changeable. • Implement a method “initialize” that sets all the values. Override the class method “new” to invoke it on new instances.
Lazy Initialization • How do you initialize instance variables to their default value? • Emphasize ease of change. • Force all accesses to variable to go through accessing method. Make Getting method check whether the variable has been initialized and initialize it if necessary.
Lazy Initialization • Timer>>count • count isNil • ifTrue: [count := self defaultCount] • ^count
Default Value Method • How do you represent the default value of a variable? • Create a method that returns the value. Prepend “default” to the name of the variable as the name of the method.
Constant Method • How do you code a constant? • Create a method that returns the constant.
How do you get and set an instance variable’s value? • Direct Variable Access: Access and set the variable directly. • Indirect Variable Access: Use a Getting Method and a Setting Method
Collection Accessor Method • How do you provide access to an instance variable that holds a collection? • Provide methods that delegate to the collection. To name the methods, add the name of the collection to the collection name. • addEmployee: • includesEmployee:
Enumeration Method • How do you provide access to collection elements? • Implement a method that executes a Block for each element of the collection. Name the method by concatenating the name of the collection and “Do:” • employeesDo:
How to make a good program • Make it run the test cases • Make it easy to understand, simple, follow coding standards, and eliminate duplication. • It is OK to get the tests to run first, and then worry about making your program readable. • It is OK to make sure your program is always readable.
How to make a good program • It is not OK to hand in programs that are hard to understand, that don’t follow standard patterns, and that have a lot of duplication.