Download presentation
Presentation is loading. Please wait.
1
2.2 Framework Techniques
2
Overview best practice of inheritance Interlude: Refactoring
avoiding copy/paste reuse planning for reuse: self-sends abstract classes and template methods specialisation interfaces Interlude: Refactoring best practice of instantiation factory method abstract factory
3
Overview best practice of cooperation elimination of class-tests
elimination of case-statements double dispatch technique badly placed code
4
Best Practice of Inheritance
avoiding copy/paste reuse planning for reuse: self-sends abstract classes and template methods specialisation interfaces
5
Reusing the LAN New requirement: “Providing logging (or tracing) facilities: A message is printed, identifying the node and the packet, each time a packet is sent from a node”
6
Reuse: Copy/Paste Approach
LAN System Node accept:thePacket self send:thePacket send:thePacket self nextNode accept:thePacket Logging LAN System Node accept:thePacket self send:thePacket send:thePacket Transcript show: …. self nextNode accept:thePacket copy/paste directly changing code
7
Copy/Paste Reuse The reused class and the original are not related anymore difficult to maintain: bug fixes and upgrades need to be propagated manually over all duplicates Proliferation of versions code duplication leads to even more code duplication, especially with reuse
8
Copy/Paste Reuse Avoid copy/paste reuse Avoid direct editing of reusable classes avoid editing of classes in libraries reuse existing classes by using inheritance and method overriding
9
OOP Solution create subclass LoggingNode of class Node
override ‘send’ method
10
BUT? + Separate discussion (design patterns) How to combine ?
Node Node + LoggingNode Printserver Workstation Separate discussion (design patterns) How to combine ? - multiple inheritance - mixin classes
11
Best Practice of Inheritance
avoiding copy/paste reuse planning for reuse: self-sends abstract classes and template methods specialisation interfaces
12
Recap: self and super refer to the receiver of the currently executing method differ in where method-lookup starts self x super y A x B x y C x y
13
self in the Node class Node Node name name accept(p:Packet)
send(p:Packet) send(p:Packet) SUPER when sending #accept: to an instance of Node LoggingNode send (p : Packet) when sending #accept: to an instance of LoggingNode
14
Planning for reuse: self-sends
can be reused for logging same “external” functionality (send is protected !) cannot be reused for logging
15
Designing Classes for Reuse
Narrow interface principle behavior of a class should be based on a minimal set of methods that can be overridden distinguish “core” behavior/methods from “peripheral” behavior/methods
16
Best Practice of Inheritance
avoiding copy/paste reuse planning for reuse: self-sends abstract classes and template methods specialisation interfaces
17
Reusing the LAN New requirement: “Extend the LAN system so that documents of different types (Postscript, ASCII) can be printed on a corresponding printer”
18
Remember ...
19
Straightforward Approach
Printserver subclass: #AsciiPrinter accept: thePacket thePacket addressee = self name and: [ thePacket contents isAscii ] ifTrue: [ self print: thePacket ] ifFalse: [ super accept: thePacket] print: thePacket Transcript show: ‘Packet with contents “, thePacket contents printString Printserver subclass: #PostscriptPrinter accept: thePacket thePacket addressee = self name and: [ thePacket contents isPostscript ] ifTrue: [ self print: thePacket ] ifFalse: [ super accept: thePacket] print: thePacket Transcript show: ‘Packet with contents “, thePacket contents interpretString
20
Problems may lead to classes that are hard to understand
subtle interaction between self and super very deep narrow inheritance hierarchies that are hard to understand Inhibits further reuse and maintenance no abstraction is made of the commonalities between ASCII and Postscript printers
21
OOP Solution template method Node subclass: #AbstractPrintserver
accept: thePacket self isDestinationFor: thePacket ifTrue: [ self print: thePacket ] ifFalse: [ super accept: thePacket] print: thePacket self subclassResponsibility isDestinationFor: thePacket ^thePacket addressee = self name abstract method concrete method
22
Abstract classes
23
Reuse Abstract classes
AbstractPrintserver subclass: #AsciiPrinter print: thePacket Transcript show: ... isDestinationFor: thePacket ^super isDestinationFor: thePacket and: [ thePacket contentx isAscii ] AbstractPrintserver subclass: #PostscriptPrinter print: thePacket Transcript show: ... isDestinationFor: thePacket ^super isDestinationFor: thePacket and: [ thePacket contentx isPostscript ]
24
Best Practice of Inheritance
avoiding copy/paste reuse planning for reuse: self-sends abstract classes and template methods specialisation interfaces
25
Which methods to override ?
default methods e.g. isDestinationFor: abstract methods e.g. print: extending methods with new behaviour what about overriding template methods ? under what conditions can we override accept ?
26
Specialisation Interface
AbstractPrintServer accept(p:Packet) {*accept, isDestinationFor, print} isDestinationFor(p:Packet) print(p:Packet) client interface: documents the messages that can be sent interface to message passing clients encapsulates the implementation from message passing clients specialisation interface: documents the messages that are sent from an object to itself interface to inheriting clients encapsulates the implementation from inheriting clients
27
Interpreting the Specialisation Interface (1)
Object subclass: #Node Node methodsFor: 'input-output' accept:thePacket self send:thePacket Node methodsFor: 'private' send:thePacket self nextNode accept:thePacket implementation must invoke send Node name accept(p:Packet) {send} send(p:Packet) {nextNode} nextNode Specialisation interfaces “reify” part of the implementation - they link design to implementation Different parts of the implementation can be reified - which parts are interesting to inheritors ? - only essential parts should be documented
28
Interpreting the Specialisation Interface (2)
specialisation clause = UML constraint AbstractPrintServer accept(p:Packet) {*accept, isDestinationFor, print} isDestinationFor(p:Packet) print(p:Packet) super send Specialisation interface = set of all specialisation clauses
29
Specialisation Interfaces Reify Only Part of the Implementation
Object subclass: #Node Node methodsFor: 'input-output' accept:thePacket false ifTrue: [self send:thePacket] implementation must invoke send Node name accept(p:Packet) {send} send(p:Packet) {nextNode} nextNode Different interpretations are possible source level: “self send:...” occurs somewhere in method body run time: “send” must be sent to self in any possible method execution we take the simpler source level interpretation
30
Using Specialisation Interfaces
Terminology for talking about abstract classes and method overriding Designing abstract classes Assess what methods can be overridden
31
Terminology AbstractPrintServer
accept(p:Packet) {*accept, isDestinationFor, print} isDestinationFor(p:Packet) print(p:Packet) A template method is a method that refers to an abstract method in its specialisation interface Core methods are methods with an empty specialisation interface ...
32
Designing Abstract Classes
AbstractPolygon sidesDo(aBlock) circumference {sidesDo} Square PolyLine sidesDo(aBlock) sidesDo(aBlock) circumference {}
33
Overriding Methods AbstractPolygon UML constraints sidesDo(aBlock)
circumference {sidesDo} {Concretisation = sidesDo} {Concretisation = sidesDo} {Coarsening = circumference(-sidesDo)} Square PolyLine sidesDo(aBlock) sidesDo(aBlock) circumference {}
34
Overriding & introducing methods
Node accept(p:Packet) {send} send {Refinement = accept(+isDestinationFor,+print)} {Extension = isDestinationFor, print} AbstractPrintServer accept(p:Packet) {send, isDestinationFor, print} isDestinationFor(p:Packet) print(p:Packet) send
35
Documenting Inheritance
Concretisation: overriding abstract methods with concrete methods Abstraction: overriding concrete methods with abstract methods Refinement: adding message names to the specialisation interface Coarsening: removing message names from the specialisation interface Extension: adding new methods to the client interface Cancellation: removing methods from the client interface 19
36
Specialisation Interfaces
documenting the interface for inheritors basic terminology for inheritance of abstract classes basis for reasoning about inheritance OO is powerful, it has to be used with care.
37
Interlude: Refactoring
Refactoring = reorganization plan for a system to improve reuse A refactoring is behavior preserving A refactoring has a precondition Some refactorings are based on class invariants Refactoring into object-oriented frameworks turning an OO application into a framework
38
Why Refactoring? Abstract classes and frameworks are generalizations
People think concretely, not abstractly Abstractions are found bottom up, by examining concrete examples
39
Why Refactoring? Generalization proceeds by:
finding things that are given different names but are really the same (and thus renaming them) parameterizing to eliminate differences breaking large things into small things so that similar components can be found
40
Refactoring: Abstraction
concrete class A concrete class B abstraction concrete class B concrete class A abstract class X
41
Refactoring: Abstraction
Creating an Abstract Superclass Create a common superclass Make method signatures compatible Add method signatures to the superclass Make method bodies compatible Make instance variables compatible Move instance variables to the superclass Migrate common code to the abstract superclass
42
Refactoring: Abstraction
AbstractPrinter PostscriptPrinter AsciiPrinter AsciiPrinter PostscriptPrinter
43
Best Practice of Instantiation
factory methods abstract factory
44
Reusing the LAN How to instantiate objects in a reusable,
maintainable and adaptable way?
45
Straightforward Approach
Object subclass: LANExample intialize ws1 = Workstation new. ws2 = Workstation new. ps1 = PostscriptPrinter new
46
Problems names of classes are hardcoded
objects are created throughout the whole system what if subclasses are introduced? where in the code are objects created? how to instantiate objects of subclasses without editing existing classes?
47
OOP Solution abstract the object-creation process
do not explicitly create objects inside a method (e.g. do not use ‘new’) provide hooks for a class to create the appropriate objects define methods that do nothing else but instantiating an object = factory methods
48
Factory methods Object subclass: LANExample intialize
ws1 = self newWorkstation. ws2 = self newWorkstation. ps1 = self newPostscriptPrinter newWorkstation ^Workstation new newPostscriptPrinter ^PostscriptPrinter new
49
Consequences use inheritance and method overriding to instantiate objects of other classes class names are only hard coded in predefined places system is freed of creation code LanExample subclass: OtherLANExample newWorkstation ^LoggingWorkstation new newPostscriptPrinter ^LoggingPostscriptPrinter new
50
Best Practice of Instantiation
factory methods abstract factory
51
Reusing the LAN How to ensure that a system consistently
uses only one family of related objects?
52
Not So Straightforward Approach
use factory methods! Object subclass: LANExample intialize ws1 = self newWorkstation. ws2 = self newWorkstation. ps1 = self newPostscriptPrinter newWorkstation ^Workstation new newPostscriptPrinter ^PostscriptPrinter new LanExample subclass: OtherLANExample newWorkstation ^LoggingWorkstation new newPostscriptPrinter ^LoggingPostscriptPrinter new
53
Problems what if we forget to override a method?
objects will be mixed there is no enforcement factory methods may or may not be overridden factory methods rely on the programmer to not make mistakes
54
Abstract Factory define a class which is responsible for creating the right objects class consists of nothing but factory methods delegate creation of objects to an instance of this newly defined class
55
Abstract Factory Object subclass: AbstractLANFactory
newWorkstation ^self subclassResponsibility newPostscriptPrinter AbstractLANFactory subclass: #DefaultLANFactory newWorkstation ^Workstation new newPostscriptPrinter ^PostscriptPrinter new AbstractLANFactory subclass: #LoggingLANFactory newWorkstation ^LoggingWorkstation new newPostscriptPrinter ^LoggingPostscriptPrinter new
56
Consequences abstract the object-creation process
provide hooks for a class to create the appropriate objects no risk of mixing two families of objects objects are created in only one place improves maintainability, readability, reusability, ...
57
But ... how to instantiate Factory objects? use factory method
use Singleton design pattern (see later)
58
Refactorings for factory methods
spot creation code inside ordinary methods move this code inside its own (factory) method replace code in original method by invocation of factory method
59
Refactorings for abstract factory
spot factory methods that always get overridden together by subclasses move these methods into a new class replace invocation of factory methods by invocation of appropriate method on factory object
60
Best Practice of Cooperation
elimination of class-tests elimination of case-statements double dispatch technique Law of Demeter badly placed code
61
Reusing the LAN New requirement: “Providing more than one destination for a packet allows the sender of a packet to print a document either on #printer1 or #printer2, depending on which printer is first encountered in the local area network.” Remember: #printer1 and #printer2 are Smalltalk symbols that are used for identifying nodes in the LAN.
62
Straightforward Approach
Node subclass: #AbstractPrintserver accept: thePacket self isDestinationFor: thePacket ifTrue: [ self print: thePacket ] ifFalse: [ super accept: thePacket] isDestinationFor: thePacket ^thePacket addressee = self name or: [ thePacket addressee = #* ] wildcard symbol
63
Problems Solution is not very general
what about #prin* (any printer name that starts with #prin Solution is brittle towards change based on a convention (e.g. wildcards) adding a new kind of address requires editing the AbstractPrintserver class (and possibly others)
64
OOP Solution Objectify addresses
65
OOP Solution Delegate responsibilities Does this work ?
66
smalltalk recap: polymorphism
objects respond to messages different objects can respond differently to the same message KWAAK Speak WOEF Speak
67
Polymorphic Addresses
isDestinationFor: WildcardAddress isDestinationFor: Address AbstractPrintserver accept: Packet print:Packet isDestinationFor:anAddress ^true NodeAddress id:Symbol isDestinationFor: Address = Address isDestinationFor:anAddress ^self = anAddress
68
Planning for Reuse: Delegation
can be reused for different addressing schemes (wildcards, domains, ...) same “external” behavior cannot be reused for different addressing schemes
69
Delegation of Responsibilities
avoids case statements (especially over class types) Example: Address Printserver should not depend on representation of addresses Printserver must delegate address equality checking
70
Delegation of Responsibilities
Names are very important Wrong ASCIIPrinter printAsciiFile PostscriptPrinter printPostsciptFile Right ASCIIPrinter printFile PostscriptPrinter printFile Designing good interfaces is important objects with same interfaces can be substituted
71
Reusing the LAN New requirement: “Extend the LAN system so that packets can be broadcasted.”
72
Straightforward Approach
73
Problems bad coding practice Inhibits further reuse and maintenance
class tests should be avoided needs modification of all node classes than can serve as addressee Inhibits further reuse and maintenance what about packets that count the number of hops?
74
OOP Solution template method abstract method
75
Class Collaboration Design for a set of classes that collaborate to accomplish a certain task emphasis on the interaction between class instances Possibly consisting of abstract classes template and abstract methods need not reside in the same class Class collaboration can be reused all abstract classes must be filled in with concrete ones first
76
Reusing Class Collaboration
collaboration instance: printing collaboration instance: broadcasting
77
Reusing the LAN New requirement: “Providing a Document class which represents documents to be printed on a printer.”
78
Straightforward Approach
Object subclass: #Document instanceVariables: ‘contents’ printOn: aPrinter aPrinter print: (self newPacketWithContents: self contents) newPacketWithContents: contents ^Packet new; contents: contents
79
But ... contents can be ASCII as well as Postscript as well as other types but ... ASCII documents cannot contain figures Postscript and PDF documents can
80
What is often seen Object subclass: Document
instanceVariables: ‘contents’ includeFigure: aFigure self isAsciiDocument ifTrue: [ Transcript show: ‘Error ...’ ] self isPostscriptDocument ifTrue: [ ... ] self isPDFDocument printOn: aPrinter ....
81
OOP Solution Object subclass: #AbstractDocument
instanceVariables: ‘contents’ includeFigure: aFigure self subclassResponsibility AbstractDocument subclass: #AsciiDocument includeFigure: aFigure Transcript show: ‘Error ...’ AbstractDocument subclass: #PostscriptDocument includeFigure: aFigure “code to include the figure”
82
Refactoring for Specialization
Motivation : the design of a framework can be improved by decomposing a large, complex class into several smaller classes the complex class usually embodies both a general abstraction and several different concrete cases that are candidates for specialization
83
Refactoring: Specialization
Specialize a class by adding subclasses corresponding to the conditions in a conditional expression: choose a conditional whose conditions suggest subclasses (this depends on the desired abstraction) for each condition, create a subclass with a class invariant that matches the condition copy the body of the condition to each subclass, and in each class simplify the conditional based on the invariant that is true for the subclass specialize some (or all) expressions that create instances of the superclass
84
Refactoring: Specialization
Disk Management for MSDOS Class Invariant Disk ... copyDisk formatDisk Disk Management for MSDOS+MAC Disk ... copyDisk formatDisk Disk Management for MSDOS+MAC Disk disktype ... copyDisk formatDisk formatDisk self diskType = #MSDOS ifTrue: [ .. code1 ..]. self diskType = #MAC ifTrue: [ .. code2 ..]. MSDOSDisk ... copyDisk formatDisk MACDisk ... copyDisk formatDisk
85
Best Practice of Cooperation
elimination of class-tests elimination of case-statements double dispatch technique badly placed code
86
Reusing the LAN New requirement: “Make sure documents of a certain type are printed on a corresponding printer.”
87
Straightforward Approach
Object subclass: #AbstractDocument instanceVariableNames: ‘contents’ printOn: aPrinter self subclassResponsibility AbstractDocument subclass: #PostscriptDocument printOn: aPrinter aPrinter class = PostscriptPrinter ifTrue: [ aPrinter print: self newPacketWithContents: self contents ] AbstractDocument subclass: #ASCIIDocument printOn: aPrinter aPrinter class = ASCIIPrinter ifTrue: [ aPrinter print: self newPacketWithContents: self contents ]
88
Problems class-testing should be avoided
what if a subclass of ASCIIPrinter is introduced? what if a printer is introduced that can print both ASCII and Postscript?
89
Observation the way a document should be printed does not only depend on the type of the document, but also on the type of the printer! delegation by itself cannot solve this problem
90
Problem late binding polymorphism only takes into account the type of the receiver single dispatch solution to our problem should also take into account the type of the argument double dispatch!
91
OOP Solution AbstractPrintserver subclass: #PostscriptPrinter
printPostsciptDocument: aPSDocument Transcipt show: aPSDocument contents interpret printAsciiDocument: anASCIIDocument Transcipt show: ‘Error’ AsbtractPrintserver subclass: #ASCIIPrinter printPostsciptDocument: aPSDocument Transcipt show: ‘Error’ printAsciiDocument: anASCIIDocument Transcipt show: anASCIIDocument contents print AbstractDocument subclass: #ASCIIDocument printOn: aPrinter aPrinter printASCIIDocument: self AbstractDocument subclass: #PostscriptDocument printOn: aPrinter aPrinter printPostscriptDocument: self
92
Consequences results in more reusable, adaptable and maintainable code
avoids class-testing avoids case-statements
93
Best Practice of Cooperation
elimination of class-tests elimination of case-statements double dispatching badly placed code
94
Reusing the LAN New requirement: “Return the number of characters after a document is printed.”
95
Straightforward Approach
AddressableNode subclass: #AbstractPrintserver countChars: aDocument ^aDocument contents numberOfChars AbstractPrintserver subclass: #PostscriptPrinter printPostsciptDocument: aPSDocument Transcipt show: aPSDocument contents interpret. ^self countChars: aPSDocument AbstractPrintserver subclass: #ASCIIPrinter printAsciiDocument: anASCIIDocument Transcipt show: anASCIIDocument contents print. ^self countChars: anASCIIDocument
96
Problems countChars method does not use the receiver
no instance variables are used no instance methods are called indication that the method is defined on the wrong class
97
OOP Solution AbstractDocument subclass: #ASCIIDocument
printOn: aPrinter aPrinter printASCIIDocument: self. ^self contents countChars AbstractDocument subclass: #PostscriptDocument printOn: aPrinter aPrinter printPostscriptDocument: self. ^self contents countChars
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.