Presentation is loading. Please wait.

Presentation is loading. Please wait.

2.2 Framework Techniques.

Similar presentations


Presentation on theme: "2.2 Framework Techniques."— Presentation transcript:

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


Download ppt "2.2 Framework Techniques."

Similar presentations


Ads by Google