Elements of Design Instance initialization Enforcing the instance

  • Slides: 88
Download presentation
Elements of Design • • • Instance initialization Enforcing the instance creation Instance /

Elements of Design • • • Instance initialization Enforcing the instance creation Instance / Class methods Instance variables / Class instance variables Class initialization Law of Demeter Factoring Constants Abstract Classes Template Methods Delegation Bad Coding Style Stéphane Ducasse «Chapter. Nr» . 1

Instance Initialization Stéphane Ducasse «Chapter. Nr» . 2

Instance Initialization Stéphane Ducasse «Chapter. Nr» . 2

Instance Initialization • How to ensure an instance is well initialized? – Automatic initialize

Instance Initialization • How to ensure an instance is well initialized? – Automatic initialize – Lazy initialize – Proposing the right interface – Providing default value Stéphane Ducasse «Chapter. Nr» . 3

A First Implementation of Packet Object subclass: #Packet instance. Variable. Names: ‘contents addressee originator

A First Implementation of Packet Object subclass: #Packet instance. Variable. Names: ‘contents addressee originator ‘ … category: ‘Lan-Simulation’ One instance method Packet>>print. On: a. Stream super print. On: a. Stream next. Put. All: ‘ addressed to: ‘; next. Put. All: self addressee. a. Stream next. Put. All: ‘ with contents: ’; next. Put. All: self contents Some Accessors Packet>>addressee ^addressee Packet>>addressee: a. Symbol addressee : = a. Symbol Stéphane Ducasse «Chapter. Nr» . 4

Packet CLASS Definition • Packet class is Automatically defined Packet class instance. Variable. Names:

Packet CLASS Definition • Packet class is Automatically defined Packet class instance. Variable. Names: '' • Example of instance creation Packet new addressee: # mac ; contents: ‘hello mac’ Stéphane Ducasse «Chapter. Nr» . 5

Fragile Instance Creation If we do not specify a contents, it breaks! |p| p

Fragile Instance Creation If we do not specify a contents, it breaks! |p| p : = Packet new addressee: #mac. p print. On: a. Stream -> error • Problems of this approach: – responsibility of the instance creation relies on the clients – can create packet without contents, without address – instance variable not initialized -> error (for example, print. On: ) -> system fragile Stéphane Ducasse «Chapter. Nr» . 6

Fragile Instance Creation Solutions Automatic initialization of instance variables • Proposing a solid interface

Fragile Instance Creation Solutions Automatic initialization of instance variables • Proposing a solid interface for the creatio • Lazy initialization • Stéphane Ducasse «Chapter. Nr» . 7

Assuring Instance Variable Initialization Kind of Smalltalk library mistake • Problem: By default #new

Assuring Instance Variable Initialization Kind of Smalltalk library mistake • Problem: By default #new class method returns instance with uninitialized instance variables. Moreover, #initialize method is not automatically called by creation methods #new/new: • How to initialize a newly created instance ? • Stéphane Ducasse «Chapter. Nr» . 8

The New/Initialize Couple Define an instance method that initializes the instance variables and override

The New/Initialize Couple Define an instance method that initializes the instance variables and override #new to invoke it. 1 Packet class>>new Class Method ^ super new initialize 3 4 Packet>>initialize Instance Method super initialize. contents : = ‘default message’ Packet new (1 -2) -> a. Packet initialize (3 -4) -> returning a. Packet but initialized! Reminder: You cannot access instance variables from a class method like #new Stéphane Ducasse «Chapter. Nr» . 9

The New/Initialize Couple Object>>initialize “do nothing. Called by new my subclasses ovrride me if

The New/Initialize Couple Object>>initialize “do nothing. Called by new my subclasses ovrride me if necessary” ^ self Stéphane Ducasse «Chapter. Nr» . 10

Strengthen Instance Creation Interface • Problem: A client can still create a. Packet without

Strengthen Instance Creation Interface • Problem: A client can still create a. Packet without address. • Solution: Force the client to use the class interface creation. Providing an interface for creation and avoiding the use of #new Packet send: ‘Hello mac’ to: #Mac First try: Packet class>>send: a. String to: an. Address ^ self new contents: a. String ; addressee: an. Address • • Stéphane Ducasse «Chapter. Nr» . 11

Example of Other Instance Initialization • step 1. Sorted. Collection sort. Block: [: a

Example of Other Instance Initialization • step 1. Sorted. Collection sort. Block: [: a : b| a name < b name] Sorted. Collection class>>sort. Block: a. Block "Answer a new instance of Sorted. Collection such that its elements are sorted according to the criterion specified in a. Block. " ^self new sort. Block: a. Block • • • step 2. self new = a. Sorted. Collection step 3. a. Sorted. Collection sort. Block: a. Block step 4. returning the instance a. Sorted. Collection Stéphane Ducasse «Chapter. Nr» . 12

Another Example • step 1. Ordered. Collection with: 1 Collection class>>with: an. Object "Answer

Another Example • step 1. Ordered. Collection with: 1 Collection class>>with: an. Object "Answer a new instance of a Collection containing an. Object. " | new. Collection : = self new. Collection add: an. Object. ^new. Collection Stéphane Ducasse «Chapter. Nr» . 13

Lazy Initialization • When some instance variables are: – not used all the time

Lazy Initialization • When some instance variables are: – not used all the time – consuming space, difficult to initialize because depending on other – need a lot of computation Use lazy initialization based on accessors • Accessor access should be used consistently! • Stéphane Ducasse «Chapter. Nr» . 14

Lazy Initialization Example • A lazy initialization scheme with default value Packet>>contents is. Nil

Lazy Initialization Example • A lazy initialization scheme with default value Packet>>contents is. Nil if. True: [contents : = ‘no contents’] ^ contents a. Packet contents or self contents A lazy initialization scheme with computed value Dummy>>ratio. Between. Thermonuclear. And. Solar ratio is. Nil if. True: [ratio : = self heavy. Computation] ^ ratio • Stéphane Ducasse «Chapter. Nr» . 15

Providing a Default Value Ordered. Collection variable. Subclass: #Sorted. Collection instance. Variable. Names: 'sort.

Providing a Default Value Ordered. Collection variable. Subclass: #Sorted. Collection instance. Variable. Names: 'sort. Block ' class. Variable. Names: 'Default. Sort. Block ' Sorted. Collection class>>initialize Default. Sort. Block : = [: x : y | x <= y] Sorted. Collection>>initialize "Set the initial value of the receiver's sorting algorithm to a default. ” sort. Block : = Default. Sort. Block Stéphane Ducasse «Chapter. Nr» . 16

Providing a Default Value Sorted. Collection class>>new: an. Integer "Answer a new instance of

Providing a Default Value Sorted. Collection class>>new: an. Integer "Answer a new instance of Sorted. Collection. The default sorting is a <= comparison on elements. ” ^(super new: an. Integer) initialize Sorted. Collection class>>sort. Block: a. Block "Answer a new instance of Sorted. Collection such that its elements are sorted according to the criterion specified in a. Block. ” ^self new sort. Block: a. Block Stéphane Ducasse «Chapter. Nr» . 17

Invoking per Default the Creation Interface Ordered. Collection class>>new "Answer a new empty instance

Invoking per Default the Creation Interface Ordered. Collection class>>new "Answer a new empty instance of Ordered. Collection. " ^self new: 5 Stéphane Ducasse «Chapter. Nr» . 18

Forbidding #new ? Problem: We can still use #new to create fragile instances •

Forbidding #new ? Problem: We can still use #new to create fragile instances • Solution: #new should raise an error! Packet class>>new self error: 'Packet should only be created using send: to: ' • Stéphane Ducasse «Chapter. Nr» . 19

Forbidding #new Implications But we still have to be able to create instance! Packet

Forbidding #new Implications But we still have to be able to create instance! Packet class>>send: a. String to: an. Address ^ self new contents: a. String ; addressee: an. Address -> raises an error Packet class>>send: a. String to: an. Address ^ super new contents: a. String ; addressee: an. Address -> BAD STYLE: link between class and superclass dangerous in case of evolution Stéphane Ducasse «Chapter. Nr» . 20

Forbidding #new • Solution: use #basic. New and #basic. New: Packet class>>send: a. String

Forbidding #new • Solution: use #basic. New and #basic. New: Packet class>>send: a. String to: an. Address ^ self basic. New contents: a. String ; addressee: an. Address • Never override basic* methods Stéphane Ducasse «Chapter. Nr» . 21

Different Self/Super • Do not invoke a super with a different method selector. It’s

Different Self/Super • Do not invoke a super with a different method selector. It’s bad style because it links a class and a superclass. This is dangerous in case the software evolves. Packet class>>new self error: 'Packet should only be created using send: to: ' Packet class>>send: a. String to: an. Address ^ super new contents: a. String ; addressee: an. Address • Use basic. New and basic. New: Packet class>>send: a. String to: an. Address ^ self basic. New contents: a. String ; addressee: an. Address Stéphane Ducasse «Chapter. Nr» . 22

How to Reuse Superclass initialization? A class>>new ^ super new blabla; and. Blabla B

How to Reuse Superclass initialization? A class>>new ^ super new blabla; and. Blabla B class>>force. Client. Interface ^ self basic. New ? ? ? => Define the initialization behavior on the instance side A>>blalaaa ^ self blabla; and. Blabla A class>>new ^ super new blalaaa B class>>force. Client. Interface ^ self basic. New blalaaa Stéphane Ducasse «Chapter. Nr» . 23

Problem with m super n With the pink box: A foo bar ^ 10

Problem with m super n With the pink box: A foo bar ^ 10 ^ self foo A new bar -> 10 B new bar -> 10 C new bar -> 10 Without the pink box: B bar foo ^ super foo C foo Stéphane Ducasse ^ 100 A new bar -> 10 B new bar -> 100 C new bar -> 50 super shortcuts dynamic calls ^ 50 «Chapter. Nr» . 24

Class Level Issues Stéphane Ducasse «Chapter. Nr» . 25

Class Level Issues Stéphane Ducasse «Chapter. Nr» . 25

Class Methods - Class Instance Variables • • Classes (Packet class) represents class (Packet).

Class Methods - Class Instance Variables • • Classes (Packet class) represents class (Packet). Class instance variables are instance variables of class -> should represent the state of class: number of created instances, number of messages sent, superclasses, subclasses. . Class methods represent CLASS behavior: instance creation, class initialization, counting the number of instances. . If you weaken the second point: class state and behavior can be used to define common properties shared by all the instances Stéphane Ducasse «Chapter. Nr» . 26

Default value between class and instance Ex: If we want to encapsulate the way

Default value between class and instance Ex: If we want to encapsulate the way “no next node” is coded and shared this knowledge between class and instances. • Instead of writing: a. Node next. Node is. Nil not => a. Node has. Next. Node • Write: Node>>has. Next. Node ^ self next. Node = self no. Next. Node>>no. Next. Node ^self class no. Next. Node class>>no. Next. Node ^ #no. Node • Stéphane Ducasse «Chapter. Nr» . 27

Class Initialization How do we know that all the class behavior has been loaded?

Class Initialization How do we know that all the class behavior has been loaded? • At end ! • Automatically called by the system at load time or explicitly by the programmer. • Used to initialize a class. Variable, a pool dictionary or class instance variables. • ‘Classname initialize’ at the end of the saved files in Squeak • In post. Load. Action: in VW • Stéphane Ducasse «Chapter. Nr» . 28

Example of class initialization Magnitude subclass: #Date instance. Variable. Names: 'day year' class. Variable.

Example of class initialization Magnitude subclass: #Date instance. Variable. Names: 'day year' class. Variable. Names: 'Days. In. Month First. Day. Of. Month. Names Seconds. In. Day Week. Day. Names' pool. Dictionaries: '' Date class>>initialize "Initialize class variables representing the names of the months and days and the number of seconds, days in each month, and first day of each month. " Month. Names : = #(January February March April May June July August September October November December ). Seconds. In. Day : = 24 * 60. Days. In. Month : = #(31 28 31 30 31 ). First. Day. Of. Month : = #(1 32 60 91 121 152 182 213 244 274 305 335 ). Week. Day. Names : = #(Monday Tuesday Wednesday Thursday Friday Saturday Sunday ) Stéphane Ducasse «Chapter. Nr» . 29

Case Study • Scanner Stéphane Ducasse «Chapter. Nr» . 30

Case Study • Scanner Stéphane Ducasse «Chapter. Nr» . 30

A Case Study: The Scanner class Scanner new scan. Tokens: 'identifier keyword: 8 r

A Case Study: The Scanner class Scanner new scan. Tokens: 'identifier keyword: 8 r 31 ''string'' embedded. period key: word: . ' -> #(#identifier #keyword: 25 'string' 'embedded. period' #key: word: #'. ') • Class Definition Object subclass: #Scanner instance. Variable. Names: 'source mark prev. End here. Char token. Type save. Comments current. Comment buffer type. Table ' class. Variable. Names: 'Type. Table ' pool. Dictionaries: '' category: 'System-Compiler-Public Access' Stéphane Ducasse «Chapter. Nr» . 31

Scanner enigma • Why having an instance variable and a class. Variable denoting the

Scanner enigma • Why having an instance variable and a class. Variable denoting the same object (the scanner table)? Type. Table is used to initialize once the table • type. Table is used by every instance and each instance can customize the table (copying). • Stéphane Ducasse «Chapter. Nr» . 32

A Case Study: Scanner (II) Scanner>>initialize "Scanner initialize" | new. Table : = Scanner.

A Case Study: Scanner (II) Scanner>>initialize "Scanner initialize" | new. Table : = Scanner. Table new: 255 with. All: #x. Default. "default" new. Table at. All. Separators. Put: #x. Delimiter. new. Table at. All. Digits. Put: #x. Digit. new. Table at. All. Letters. Put: #x. Letter. new. Table at: $_ as. Integer put: #x. Letter. '!%&*+, -/<=>? @~' do: [: bin | new. Table at: bin as. Integer put: #x. Binary]. "Other multi-character tokens" new. Table at: $" as. Integer put: #x. Double. Quote. . "Single-character tokens" new. Table at: $( as. Integer put: #left. Parenthesis. . new. Table at: $^ as. Integer put: #up. Arrow. "spacing circumflex, formerly up arrow" new. Table at: $| as. Integer put: #vertical. Bar. Type. Table : = new. Table Stéphane Ducasse «Chapter. Nr» . 33

A Case Study: Scanner (III) • Instances only access the type table via the

A Case Study: Scanner (III) • Instances only access the type table via the instance variable that points to the table that has been initialized once. Scanner class>> new ^super new init. Scanner>>init. Scanner buffer : = Write. Stream on: (String new: 40). save. Comments : = true. type. Table : = Type. Table • A subclass just has to specialize init. Scanner without copying the initialization of the table My. Scanner>>init. Scanner super init. Scanner type. Table : = type. Table copy. type. Table at: $( as. Integer put: #x. Default. type. Table at: $) as. Integer put: #x. Default. Stéphane Ducasse «Chapter. Nr» . 34

Coupling Why coupled classes is fragile design? • Law of Demeter • Thoughts about

Coupling Why coupled classes is fragile design? • Law of Demeter • Thoughts about accessor use • Stéphane Ducasse «Chapter. Nr» . 35

The Core of the Problem Immediate Indirect Provider provider intermediate Client Provider +provider do.

The Core of the Problem Immediate Indirect Provider provider intermediate Client Provider +provider do. Something() get. Provider() intermediate. provider. do. Something() Or intermediate. get. Provider. do. Something() Stéphane Ducasse «Chapter. Nr» . 36

Why are Coupled Classes bad? Packet>>addressee ^addressee Workstation>>accept: a. Packet addressee = self name

Why are Coupled Classes bad? Packet>>addressee ^addressee Workstation>>accept: a. Packet addressee = self name if. True: [ Transcript show: 'A packet is accepted by the Workstation ', self name as. String] if. False: [super accept: a. Packet] If Packet changes the way addressee is represented, Workstation, Node, Printer. Server have to be changed too Stéphane Ducasse «Chapter. Nr» . 37

The Law ot Demeter • You should only send messages to: – an argument

The Law ot Demeter • You should only send messages to: – an argument passed to you – an object you create – self, super – your class Avoid global variables • Avoid objects returned from message sends other than self • Stéphane Ducasse «Chapter. Nr» . 38

Correct Messages some. Method: a. Parameter self foo. super some. Method: a. Parameter. self

Correct Messages some. Method: a. Parameter self foo. super some. Method: a. Parameter. self class foo. self inst. Var. One foo. self class. Var. One foo. a. Parameter foo. thing : = Thing new. thing foo Stéphane Ducasse «Chapter. Nr» . 39

Law of Demeter by Example Node. Manager>>declare. New. Node: a. Node |node. Description| (a.

Law of Demeter by Example Node. Manager>>declare. New. Node: a. Node |node. Description| (a. Node is. Valid) “Ok passed as an argument to me” if. True: [ a. Node certified]. node. Description : = Node. Description for: a. Node. node. Description local. Time. “I created it” self add. Node. Description: node. Description. “I can talk to myself“ node. Description data “Wrong I should not know” at: self creator. Key “that data is a dictionary” put: self creator Stéphane Ducasse «Chapter. Nr» . 40

Transformation Carburetor +fuel. Valve. Open Engine +carburetor Car -engine +increase. Speed() … engine. carburetor.

Transformation Carburetor +fuel. Valve. Open Engine +carburetor Car -engine +increase. Speed() … engine. carburetor. fuel. Valve. Open = true Carburetor +fuel. Valve. Open Engine -carburetor +speed. Up() carburetor. fuel. Valve. Open = true Carburetor -fuel. Valve. Open +open. Fuel. Valve() fuel. Valve. Open = true Stéphane Ducasse Engine -carburetor +speed. Up() Car -engine +increase. Speed() … engine. speed. Up() Car -engine +increase. Speed() carburetor. open. Fuel. Valve() «Chapter. Nr» . 41

Law of Demeter’s Dark Side Class A inst. Var: my. Collection A>>do: a. Block

Law of Demeter’s Dark Side Class A inst. Var: my. Collection A>>do: a. Block my. Collection do: a. Block A>>collect: a. Block ^ my. Collection collect: a. Block A>>select: a. Block ^ my. Collection select: a. Block A>>detect: a. Block ^ my. Collection detect: a. Block A>>is. Empty ^ my. Collection is. Empty ………………… Stéphane Ducasse «Chapter. Nr» . 42

About the Use of Accessors Literature says: “Access instance variables using methods” But •

About the Use of Accessors Literature says: “Access instance variables using methods” But • Be consistent inside a class, do not mix direct access and accessor use • First think accessors as private methods that should not be invoked by clients • Only when necessary put accessors in accessing protocol • Scheduler>>initialize self tasks: Ordered. Collection new. Scheduler>>tasks ^tasks Stéphane Ducasse «Chapter. Nr» . 43

Accessors • Accessors are good for lazy initialization Schedule>>tasks is. Nil if. True: [task

Accessors • Accessors are good for lazy initialization Schedule>>tasks is. Nil if. True: [task : =. . . ]. ^tasks BUT accessors methods should be PRIVATE by default at least at the beginning • • Return consistenly the receiver or the element but not the collection (otherwise people can look inside and modify it) or return a copy of it. Stéphane Ducasse «Chapter. Nr» . 44

Accessors Open Encapsulation • • The fact that accessors are methods doesn’t provide you

Accessors Open Encapsulation • • The fact that accessors are methods doesn’t provide you with a good data encapsulation. You could be tempted to write in a client: Scheduled. View>>add. Task. Button. . . model tasks add: new. Task What’s happen if we change the representation of tasks? If tasks is now an array it will break Take care about the coupling between your objects and provide a good interface! Schedule>>add. Task: a. Task tasks add: a. Task Stéphane Ducasse «Chapter. Nr» . 45

About the Use of Accessors (III) “Never do the work somebody else can do!”

About the Use of Accessors (III) “Never do the work somebody else can do!” Alan Knight XXX>>m total : = 0. a. Plant bilings do: [: each | (each status == #paid and: [each date> self start. Date]) if. True: [total : = total + each amount]]. • Instead write XXX>m total : = a. Plant total. Billings. Paid. Since: start. Date Plant> total. Billings. Paid. Since: start. Date total : = 0 bilings do: [: each | (each status == #paid and: [each date>start. Date]) if. True: [total : = total + each amount]]. ^ total Stéphane Ducasse «Chapter. Nr» . 46

Provide a Complete Interface Workstation>>accept: a. Packet addressee = self name … • It

Provide a Complete Interface Workstation>>accept: a. Packet addressee = self name … • It is the responsibility of an • object to propose a complete interface that propects itself from client intrusion. Shift the responsibility to the Packet object Packet>>is. Addressed. To: a. Node ^ addressee = a. Node name Workstation>>accept: a. Packet (a. Packet is. Addressed. To: self) if. True: [ Transcript show: 'A packet is accepted by the Workstation ', self name as. String] if. False: [super accept: a. Packet] Stéphane Ducasse «Chapter. Nr» . 47

Say once and only once Stéphane Ducasse «Chapter. Nr» . 48

Say once and only once Stéphane Ducasse «Chapter. Nr» . 48

Factoring Out Constants • Ex: We want to encapsulate the way “no next node”

Factoring Out Constants • Ex: We want to encapsulate the way “no next node” is coded. Instead of writing: Node>>next. Node ^ next. Node. Client>>transmit. To: a. Node next. Node = ‘no next node’ • Write: . . . Node. Client>>transmit. To: a. Node has. Next. Node>>has. Next. Node ^ (self next. Node = self class no. Next. Node) not Node class>>no. Next. Node ^ ‘no next node’ Stéphane Ducasse «Chapter. Nr» . 49

Initializing without Duplicating Node>>initialize access. Type : = ‘local’. . . Node>>is. Local ^

Initializing without Duplicating Node>>initialize access. Type : = ‘local’. . . Node>>is. Local ^ access. Type = ‘local’ • It’s better to write Node>>initialize access. Type : = self local. Access. Type Node>>is. Local ^ access. Type = self local. Access. Type Node>>local. Access. Type ^ ‘local’ Stéphane Ducasse «Chapter. Nr» . 50

Say something only once Ideally you could be able to change the constant without

Say something only once Ideally you could be able to change the constant without having any problems. • You may have to have mapping tables from model constants to UI constants or database constants. • Stéphane Ducasse «Chapter. Nr» . 51

Constants Needed at Creation Time • Previous solution works well for: Node class>>local. Node.

Constants Needed at Creation Time • Previous solution works well for: Node class>>local. Node. Named: a. String |inst| inst : = self new. inst name: a. String. inst type: inst local. Access. Type • If you want to have the following creation interface Node class>>name: a. String access. Type: a. Type ^self new name: a. String ; access. Type: a. Type Node class>>name: a. String ^self name: a. String access. Type: self local. Access. Type Stéphane Ducasse «Chapter. Nr» . 52

Constants Needed at Creation Time • You need: Node class>>local. Access. Type ^ ‘local’

Constants Needed at Creation Time • You need: Node class>>local. Access. Type ^ ‘local’ • -> Factor the constant between class and instance level Node>>local. Access. Type ^self class local. Access. Type • -> You could also use a Class. Variable that is shared between a class and its instances. Stéphane Ducasse «Chapter. Nr» . 53

How to invoke a method • Depending on **both** the receiver and an argument…

How to invoke a method • Depending on **both** the receiver and an argument… – Type check are bad – Use Double Dispatch Stéphane Ducasse «Chapter. Nr» . 54

Type Checking for Dispatching • • How to invoke a method depending on the

Type Checking for Dispatching • • How to invoke a method depending on the receiver and an argument? A not so good solution: PSPrinter>>print: a. Document ^ a. Document is. PS if. True: [self print. From. PS: a. Document] if. False: [self print. From. PS: a. Document as. PS] PSPrinter>>print. Form. PS: a. PSDoc <primitive> Pdf. Printer>>print: a. Document ^ a. Document is. PS if. True: [self print. From. PDF: a. Document as. PDF] if. False: [self print. From. PDF: a. Document] Pdf. Printer>>print. Form. PS: a. Pdf. Doc <primitive> Stéphane Ducasse «Chapter. Nr» . 55

Drawbacks of Typecheck Adding new kinds of documents requires changes everywhere • Adding new

Drawbacks of Typecheck Adding new kinds of documents requires changes everywhere • Adding new documents requires changes everywhere • No dynamic (without recompilation) possibilities • Stéphane Ducasse «Chapter. Nr» . 56

Double Dispatch • Solution: use the information given by the single dispatch and redispatch

Double Dispatch • Solution: use the information given by the single dispatch and redispatch with the argument (send a message back to the argument passing the receiver as an argument) Stéphane Ducasse «Chapter. Nr» . 57

Double Dispatch (a) PSPrinter>>print: a. Doc print. On. PSPrinter: self (b) Pdf. Printer>>print: a.

Double Dispatch (a) PSPrinter>>print: a. Doc print. On. PSPrinter: self (b) Pdf. Printer>>print: a. Doc print. On. Pdf. Printer: self (c) PSDoc>>print. On. PSPrinter: a. PSPrinter <primitive> (d) Pdf. Doc>>print. On. Pdf. Printer: a. PSPrinter a. PSprinter print: self as. PS (e) PSDoc>>print. On. PSPrinter: a. Pdf. Printer print: self as. Pdf (f) Pdf. Doc>>print. On. Pdf. Printer: a. Pdf. Printer <primitive> • Some Tests: psprinter print: psdoc =>(a->c) pdfprinter print: pdfdoc => (b->f) psprinter print: pdfdoc => (a->d->b->f) pdfprinter print: psdoc => (b->e->b->f) Stéphane Ducasse «Chapter. Nr» . 58

Let’s Step Back • • Example: Coercion between Float and Integer Not a really

Let’s Step Back • • Example: Coercion between Float and Integer Not a really good solution: Integer>>+ a. Number (a. Number is. Kind. Of: Float) if. True: [ a. Number as. Float + self] if. False: [ self add. Primitive: a. Number] Float>>+ a. Number (a. Number is. Kind. Of: Integer) if. True: [a. Number as. Float + self] if. False: [self add. Primitive: a. Number] • Here receiver and argument are the same, we can coerce in both senses. Stéphane Ducasse «Chapter. Nr» . 59

Double Dispatch on Numbers (a) Integer>>+ a. Number ^ a. Number sum. From. Integer:

Double Dispatch on Numbers (a) Integer>>+ a. Number ^ a. Number sum. From. Integer: self (b) Float>>+ a. Number ^ a. Number sum. From. Float: self (c) Integer>>sum. From. Integer: an. Integer <primitive: 40> (d) Float>>sum. From. Integer: an. Integer ^ an. Integer as. Float + self (e) Integer>>sum. From. Float: a. Float ^a. Float + self as. Float (f) Float>>sum. From. Float: a. Float <primitive: 41> Some Tests: 1 + 1: (a->c) 1. 0 + 1. 0: (b->f) 1 + 1. 0: (a->d->b->f) 1. 0 + 1: (b->e->b->f) Stéphane Ducasse «Chapter. Nr» . 60

Double Dispatching • Three Kinds of Messages – Primary operations – Double dispatching methods

Double Dispatching • Three Kinds of Messages – Primary operations – Double dispatching methods – Forwarding operations Stéphane Ducasse «Chapter. Nr» . 61

Cost of Double Dispatching Adding a new class requires adding a new message to

Cost of Double Dispatching Adding a new class requires adding a new message to each of other classes • Worst case is N*N methods for N classes. • However, total lines of code is not much larger • Stéphane Ducasse «Chapter. Nr» . 62

Unit of Reuse • Methods are Unit of Reuse Stéphane Ducasse «Chapter. Nr» .

Unit of Reuse • Methods are Unit of Reuse Stéphane Ducasse «Chapter. Nr» . 63

Methods are the Basic Units of Reuse Node>>compute. Ratio. For. Display |average. Ratio default.

Methods are the Basic Units of Reuse Node>>compute. Ratio. For. Display |average. Ratio default. Node. Size| average. Ratio : = 55. default. Node. Size : = self main. Window. Coordinate / maximise. View. Ratio. self window add: (UINode new with: (self band. Width * average. Ratio / default. Window. Size) … • We are forced to copy the method! Special. Node>>compute. Ratio. For. Display |average. Ratio default. Node. Size| average. Ratio : = 55. default. Node. Size : = self main. Window. Coordinate + minimal. Ratio / maximise. View. Ratio. self window add: (UINode new with: (self band. Width * average. Ratio / default. Window. Size) … Stéphane Ducasse «Chapter. Nr» . 64

Self sends: Plan for Reuse Node>>compute. Ratio. For. Display |average. Ratio default. Node. Size|

Self sends: Plan for Reuse Node>>compute. Ratio. For. Display |average. Ratio default. Node. Size| average. Ratio : = 55. default. Node. Size : = self default. Node. Size. self window add: (UINode new with: (self band. Width * average. Ratio / default. Window. Size). . . Node>>default. Node. Size ^self main. Window. Coordinate / maximise. View. Ratio Special. Node>>default. Node. Size ^self main. Window. Coordinate + minimal. Ratio / maximise. View. Ratio Stéphane Ducasse «Chapter. Nr» . 65

Do not Hardcode Constants Node>>compute. Ratio. For. Display |average. Ratio default. Node. Size| average.

Do not Hardcode Constants Node>>compute. Ratio. For. Display |average. Ratio default. Node. Size| average. Ratio : = 55. default. Node. Size : = self main. Window. Coordinate / maximise. View. Ratio. self window add: (UINode new with: (self band. Width * average. Ratio / default. Window. Size). . • We are forced to copy the method! Special. Node>>compute. Ratio. For. Display |average. Ratio default. Node. Size| average. Ratio : = 55. default. Node. Size : = self main. Window. Coordinate / maximise. View. Ratio. self window add: (Extended. UINode new with: (self band. Width * average. Ratio / default. Window. Size). Stéphane Ducasse «Chapter. Nr» . 66

Class Factories Node>>compute. Ratio. For. Display |average. Ratio | average. Ratio : = 55.

Class Factories Node>>compute. Ratio. For. Display |average. Ratio | average. Ratio : = 55. self window add: self UIClass new with: (self band. Width * average. Ratio / self default. Window. Size). . . Node>>UIClass ^UINode Special. Node>>UIClass ^Extended. UINode Stéphane Ducasse «Chapter. Nr» . 67

Hook and Template Methods Hooks: place for reuse • Template: context for reuse •

Hook and Template Methods Hooks: place for reuse • Template: context for reuse • Stéphane Ducasse «Chapter. Nr» . 68

Hook and Template Methods • • Templates: Context reused by subclasses Hook methods: holes

Hook and Template Methods • • Templates: Context reused by subclasses Hook methods: holes that can be specialized Hook methods do not have to be abstract, they may define default behavior or no behavior at all. This has an influence on the instantiability of the superclass. Stéphane Ducasse «Chapter. Nr» . 69

Hook Example: Copying Object>>copy " Answer another instance just like the receiver. Subclasses normally

Hook Example: Copying Object>>copy " Answer another instance just like the receiver. Subclasses normally override the post. Copy message, but some objects that should not be copied override copy. " ^self shallow. Copy post. Copy Object>>shallow. Copy "Answer a copy of the receiver which shares the receiver's instance variables. " <primitive: 532>. . Stéphane Ducasse «Chapter. Nr» . 70

post. Copy Object>>post. Copy " Finish doing whatever is required, beyond a shallow. Copy,

post. Copy Object>>post. Copy " Finish doing whatever is required, beyond a shallow. Copy, to implement 'copy'. Answer the receiver. This message is only intended to be sent to the newly created instance. Subclasses may add functionality, but they should always do super post. Copy first. " ^self Stéphane Ducasse «Chapter. Nr» . 71

Hook Specialisation Bag>>post. Copy "Make sure to copy the contents fully. " | new

Hook Specialisation Bag>>post. Copy "Make sure to copy the contents fully. " | new | super post. Copy. new : = contents class new: contents capacity. contents keys. And. Values. Do: [: obj : count | new at: obj put: count]. contents : = new. Stéphane Ducasse «Chapter. Nr» . 72

Hook and Template Example: Printing Object>>print. String "Answer a String whose characters are a

Hook and Template Example: Printing Object>>print. String "Answer a String whose characters are a description of the receiver. " | a. Stream : = Write. Stream on: (String new: 16). self print. On: a. Stream. ^a. Stream contents Object>>print. On: a. Stream "Append to the argument a. Stream a sequence of characters that describes the receiver. " | title : = self class name. a. Stream next. Put. All: ((title at: 1) is. Vowel if. True: ['an '] if. False: ['a ']). a. Stream print: self class Stéphane Ducasse «Chapter. Nr» . 73

Overriding the Hook Array>>print. On: a. Stream "Append to the argument, a. Stream, the

Overriding the Hook Array>>print. On: a. Stream "Append to the argument, a. Stream, the elements of the Array enclosed by parentheses. " | too. Many : = a. Stream position + self max. Print. a. Stream next. Put. All: '#('. self do: [: element | a. Stream position > too. Many if. True: [a. Stream next. Put. All: '. . . (more). . . )'. ^self]. element print. On: a. Stream] separated. By: [a. Stream space]. a. Stream next. Put: $) False>>print. On: a. Stream "Print false. " a. Stream next. Put. All: 'false' Stéphane Ducasse «Chapter. Nr» . 74

Specialization of the Hook • The class Behavior that represents a class extends the

Specialization of the Hook • The class Behavior that represents a class extends the default hook but still invokes the default one. Behavior>>print. On: a. Stream "Append to the argument a. Stream a statement of which superclass the receiver descends from. " a. Stream next. Put. All: 'a descendent of '. superclass print. On: a. Stream Stéphane Ducasse «Chapter. Nr» . 75

Guidelines for Creating Template Methods • Simplementation. • Break into steps. • Make step

Guidelines for Creating Template Methods • Simplementation. • Break into steps. • Make step methods. – Implement all the code in one method. – Comment logical subparts – Extract subparts as methods Call the step methods Make constant methods, i. e. , methods doing nothing else than returning. • Repeat steps 1 -5 if necessary on the methods created • • Stéphane Ducasse «Chapter. Nr» . 76

Delegation of Responsibilities Stéphane Ducasse «Chapter. Nr» . 77

Delegation of Responsibilities Stéphane Ducasse «Chapter. Nr» . 77

Towards Delegation: Matching Addresses • • • New requirement: A document can be printed

Towards Delegation: Matching Addresses • • • New requirement: A document can be printed on different printers for example lw 100 s or lw 200 s depending on which printer is first encountered. -> Packet need more than one destination Ad-hoc Solution: Lan. Printer>>accept: a. Packet (the. Packet addressee = #*lw*) if. True: [ self print: the. Packet] if. False: [ (the. Packet is. Addressed. To: self) if. True: [self print: the. Packet] if. False: [super accept: the. Packet]] • Limits: – not general – brittle because based on a convention – adding a new kind of address behavior requires editing the class Printer Stéphane Ducasse «Chapter. Nr» . 78

Create Object and Delegate Stéphane Ducasse «Chapter. Nr» . 79

Create Object and Delegate Stéphane Ducasse «Chapter. Nr» . 79

Create Object and Delegate • • An alternative solution: is. Addressed. To: could be

Create Object and Delegate • • An alternative solution: is. Addressed. To: could be sent directly to the address With the current solution, the packet can still control the process if needed Stéphane Ducasse «Chapter. Nr» . 80

Reifying Address Reify: v. making something an object (philosophy) Node. Address is responsible for

Reifying Address Reify: v. making something an object (philosophy) Node. Address is responsible for identifying the packet receivers Object subclass: #Node. Address instance. Variable. Names: ‘id‘ Node. Address>>is. Addressed. To: a. Node. Address ^ self id = a. Node. Address id Packet>>is. Addressed. To: a. Node ^ self addressee is. Addressed. To: a. Node name Having the same name for packet and for address is not necessary but the name is meaningful! Refactoring Remark: name was not a good name anyway, and now it has become an address -> we should rename it. Stéphane Ducasse «Chapter. Nr» . 81

Matching Address subclass: #Matching. Address instance. Variable. Names: ‘’ Node. Address>>is. Addressed. To: a.

Matching Address subclass: #Matching. Address instance. Variable. Names: ‘’ Node. Address>>is. Addressed. To: a. Node. Address ^ self id match: a. Node. Address id • • Works for packets with matchable addresses Packet send: ‘lulu’ to: (Matching. Address with: #*lw*) Does not work for nodes with matchable addresses because the match is directed. But it corresponds to the requirements! Node with. Name: (Matching. Address with: #*lw*) Packet>>is. Addressed. To: a. Node ^ self addressee is. Addressed. To: a. Node name • Remarks inheritance class relationship is not really good because we can avoid duplication (coming soon) Creation interfaces could be drastically improved Stéphane Ducasse «Chapter. Nr» . 82

Addresses Object subclass: #Address instance. Variable. Names: ‘id‘ Address>>is. Addressed. To: an. Address ^self

Addresses Object subclass: #Address instance. Variable. Names: ‘id‘ Address>>is. Addressed. To: an. Address ^self subclass. Responsibility Address subclass: #Node. Address instance. Variable. Names: ‘‘ Address subclass: #Matching. Address instance. Variable. Names: ‘‘ Stéphane Ducasse «Chapter. Nr» . 83

Trade-Off • Delegation Pros • Delegation Cons – No blob class: one class one

Trade-Off • Delegation Pros • Delegation Cons – No blob class: one class one responsibility – Variation possibility – Pluggable behavior without inheritance extension – Runtime pluggability – Difficult to follow responsibilities and message flow – Adding new classes = adding complexities (more names) – New object Stéphane Ducasse «Chapter. Nr» . 84

Designing Classes for Reuse • • • Encapsulation principle: minimize data representation dependencies Complete

Designing Classes for Reuse • • • Encapsulation principle: minimize data representation dependencies Complete interface No overuse of accessors Responsibility of the instance creation Loose coupling between classes Methods are units of reuse (self send) Use polymorphism as much as possible to avoid type checking Behavior up and state down Use correct names for class Use correct names for methods Stéphane Ducasse «Chapter. Nr» . 85

Minor Stuff Stéphane Ducasse «Chapter. Nr» . 86

Minor Stuff Stéphane Ducasse «Chapter. Nr» . 86

Do not overuse conversions nodes as. Set • removes all the duplicated nodes (if

Do not overuse conversions nodes as. Set • removes all the duplicated nodes (if node knows how to compare). But a systematic use of as. Set to protect yourself from duplicate is not good nodes as. Set as. Ordered. Collection • returns an ordered collection after removing duplicates • Look for the real source of duplication if you do not want it! Stéphane Ducasse «Chapter. Nr» . 87

Hiding missing information Dictionary>>at: a. Key • This raises an error if the key

Hiding missing information Dictionary>>at: a. Key • This raises an error if the key is not found Dictionary>>at: a. Key if. Absent: a. Block • This allows one to specify action <a. Block> to be done when the key does not exist. Do not overuse it: nodes at: node. Id if. Absent: [] • This is bad because at least we should know that the node. Id was missing Stéphane Ducasse «Chapter. Nr» . 88