Yann-Gaël Guéhéneuc
This work is licensed under a Creative Commons Attribution-NonCommercial- ShareAlike 3.0 Unported License
CSE3009: 소프트웨어 구조및설계
(Software Architecture and Design)
Patterns in Practice
CSE3009: (Software Architecture and Design) Yann-Gal - - PowerPoint PPT Presentation
CSE3009: (Software Architecture and Design) Yann-Gal Guhneuc Patterns in Practice This work is licensed under a Creative Commons Attribution-NonCommercial- ShareAlike 3.0 Unported License Patterns
Yann-Gaël Guéhéneuc
This work is licensed under a Creative Commons Attribution-NonCommercial- ShareAlike 3.0 Unported License
(Software Architecture and Design)
Patterns in Practice
2/162
Patterns document reusable solutions to
recurring problems
– Architecture
– Design
– Implementation
3/162
Do you know
– C++? – Java? – Lisp? – Prolog? – Smalltalk?
4/162
class Dog { string name; Dog(const Dog* dog) : name(dogname) {}} class Kennel { Dog* dog; string name; } if (&kennel != this) { thisdog = new Dog(kennel.dog); thisname = kennel.name; } return *this;
Bruce Eckel ; Thinking in C++ ; Volume 2, pages 551–553, Planet PDF, 2nd Edition, 2000.
5/162
final Object oldListOfEntities = this.listOfEntities(); this.fireVetoableChange( "RemoveEntity",
anEntity); this.removeEntity(anEntity); this.firePropertyChange( "RemoveEntity",
anEntity);
6/162
(define (square ls) (if (null? ls) '() (cons ((lambda(x) (* x x)) (car ls)) (square (cdr ls)))))
7/162
checkLt0(LA, LT, LD, NNLA, NNLT, NNLD) :- nextEvent( [], E), interpretEvent(E, IE), checkLt1(IE, LA, LT, LD, NLA, NLT, NLD), !, ( (IE = programEnd, NNLA = NLA, NNLT = NLT, NNLD = NLD) ; checkLt0(NLA, NLT, NLD, NNLA, NNLT, NNLD) ).
8/162
Integer>>+ aNumber ^aNumber addInteger: self Float>>+ aNumber ^aNumber addFloat: self Integer>>addInteger: anInteger <primitive: 1> Float>>addFloat: aFloat <primitive: 2> Integer>>addFloat: aFloat ^self asFloat addFloat: aFloat Float>>addInteger: anInteger ^self addFloat: anInteger asFloat
Kent Beck ; Smalltalk – Best practice patterns ; Pages 55–57, Prentice Hall, 1997, ISBN 0-13-476904-X.
9/162
The examples showed idioms in the given
pieces of source code
– These idioms are recurring motifs in a program source code – These motifs connote a recognized, acknowledge style of programming
10/162
Definition Quality Form Example Catalogue Practice Conclusion
11/162
Definition Quality Form Example Catalogue Practice Conclusion
12/162
Context
– 1977 et 1979: architecture
Construction and the idea of generative patterns
perfection in architecture
– 1990: object-oriented design
and John Vlissides†
13/162
A Pattern Language:
Towns, Buildings, Construction
– 253 patterns – Generative grammar – “At the core... is the idea that people should design for themselves their own houses, streets and communities. This idea... comes simply from the observation that most of the wonderful places of the world were not made by architects but by the people.”
14/162
“Each pattern describes a problem which occurs over and
use this solution a million times over, without ever doing it the same way twice.”
—Christopher Alexander, 1977
“Each pattern is a three part rule, which express a relation between a context, a problem, and a solution.”
—Christopher Alexander, 1977
15/162
盈進学園 東野高等学校 http://eishin.ac/
16/162
Design Patterns:
Elements of Reusable OO Software
– 23 patterns – Not a language? – “Dynamic, highly parameterized software is harder to understand and build than more static software.”
17/162
“The strict modeling of the real world leads to reflect today’s realities but not necessarily tomorrow’s. The abstractions that emerge during design are key to making a design flexible.” —Erich Gamma, 1994
18/162
JHotDraw
http://www.jhotdraw.org/ http://www.javaworld.com/article/2074997/swing-gui-programming/ become-a-programming-picasso-with-jhotdraw.html
19/162
A pattern is a general reusable solution to a
commonly occurring problem within a given context in software development, operation, and maintenance
20/162
A pattern is a general reusable solution to a
commonly occurring problem within a given context in software development,
– Patterns have been identified for
21/162
A pattern is a general reusable solution to a
commonly occurring problem within a given context in software development,
22/162
A pattern is a general reusable solution to a
commonly occurring problem within a given context in software development,
– Problem faced by three people at three different times in a similar context – Particular problems are not included, except if they occur more than three times…
23/162
A pattern is a general reusable solution to a
commonly occurring problem within a given context in software development, operation, and maintenance
24/162
A pattern is a general reusable solution to a
commonly occurring problem within a given context in software development, operation, and maintenance
– Essentially, a solution must describe steps to solve the problem
25/162
A pattern is a general reusable solution to a
commonly occurring problem within a given context in software development, operation, and maintenance
26/162
A pattern is a general reusable solution to a
commonly occurring problem within a given context in software development, operation, and maintenance
– The solution must not be particular – The solution can be adapted – The solution must be adapted
27/162
A pattern is a general reusable solution to a
commonly occurring problem within a given context in software development, operation, and maintenance
– The solution must not be particular – The solution can be adapted – The solution must be adapted
28/162
class Dog { string name; Dog(const Dog* dog) : name(dogname) {}} class Kennel { Dog* dog; string name; } if (&kennel != this) { thisdog = new Dog(kennel.dog); thisname = kennel.name; } return *this;
Bruce Eckel ; Thinking in C++ ; Volume 2, pages 551–553, Planet PDF, 2nd Edition, 2000.
Development, operation, and maintenance Commonly occurring problem within a given context General reusable solution
29/162
Definition Quality Form Example Catalogue Practice Conclusion
30/162
A means to enhance the reusability
– Of the code written using the pattern + Its flexibility – Of the problem and its solution
A means to encapsulate design experience A common vocabulary among designers A means to have that
“quality without a name”
31/162
“There is a point you reach in tightening a nut, where you know that to tighten just a little more might strip the thread, but to leave it slightly looser would risk having the hut coming off from vibration. If you've worked with your hands a lot, you know what this means, but the advice itself is meaningless.”
—Robert Pirsig, circa. 1972
“[T]oo often software developers spend their days grinding away for pay at programs they neither need nor love. But not in the Linux world - which may explain why the average quality of software originated in the Linux community is so high.”
—Eric Raymond, 1998
32/162
33/162
ニワトリのヒナの雌雄鑑別 (chick sexing) “The master would stand over the apprentice and watch. The student would pick up a chick, examine its rear, and toss it into one bin or the
feedback: yes or no.” —Eagleman, 2011
http://discovermagazine.com/2011/sep/18-your-brain-knows-lot-more-than-you-realize
34/162
35/162
36/162
Rationale
– “Software design is widely recognised as being a “wicked” or “ill-structured” problem, characterised by ambiguous specifications, no true/false solutions (only ones that are “better” or “worse” from a particular perspective), the lack
solution has been reached, and no ultimate test
—Zhang and Budgen, 2012
37/162
“Important assumptions
– That patterns can be codified in such a way that they can be shared between different designers – That reuse will lead to “better” designs. There is an obvious question here of what constitutes “better”, but a key measure is maintainability”
—Zhang and Budgen, 2012
(With minor adaptations)
38/162
“Advantages:
– Using patterns improves programmer productivity and program quality – Novices can increase their design skills significantly by studying and applying patterns – Patterns encourage best practices, even for experiences designers – Design patterns improve communication, both among developers and from developers to maintainers”
—Zhang and Budgen, 2012
(With minor adaptations)
39/162
Definition Quality Form Example Catalogue Practice Conclusion
40/162
Several books, articles
– “Theoretical” – With examples – Among others…
41/162
Several books, articles
– Amazon.com
Software Design, Testing & Engineering › Object- Oriented Design › "patterns"
42/162
Several books, articles
– Amazon.com
– Unreleased books – Specific to a technology or frameworks » e.g., MVVM Unleashed by Michael Brown – Process oriented, user-interface, programming languages » e.g., Process Patterns: Building Large-Scale Systems Using Object Technology by Scott W. Ambler and Barbara Hanscome – Proceedings of conferences – Unrelated to software engineering
43/162
44/162
“Each pattern is a three part rule, which express a relation between a context, a problem, and a solution.” —Christopher Alexander, 1977
45/162
General form as for the GoF
also inspired by Coplien’s form
– Name – Problem(s) – Solution – Consequences
46/162
General form as for the GoF
also inspired by Coplien’s form
– Name – Problem(s) – Example(s) – Solution – Example(s) – Consequences – (Follow-up)
47/162
General form as for the GoF
also inspired by Coplien’s form
– Not formal – Room for interpretation – But…
48/162
Definition Quality Form Example Catalogue Practice Conclusion
49/162
Simplified compiler
– Parse files to build an AST – Iterate over the AST
javax.swing.tree.DefaultMutableTreeNode for a graphical representation of the AST
50/162
Simplified compiler
– Parse files to build an AST – Iterate over the AST
javax.swing.tree.DefaultMutableTreeNode for a graphical representation of the AST
51/162
AST
Field generateCode() Statement generateCode() Method generateCode() Class generateCode() CompilationUnit generateCode() Main
52/162
package compiler; import java.util.Set; public class Method { private Set statements; public void addStatement(final Statement aStatement) { this.statements.add(aStatement); } public void removeStatement(final Statement aStatement) { this.statements.remove(aStatement); } } package compiler; public class Field { /* To be implemented. */ } package compiler; public class Statement { /* To be implemented. */ }
53/162
package compiler; import java.util.Set; public class Class { private String name; private Set methods; private Set fields; public String getName() { return this.name; } public void addMethod(final Method aMethod) { this.methods.add(aMethod); } public void removeMethod(final Method aMethod) { this.methods.remove(aMethod); } public void addField(final Method aField) { this.fields.add(aField); } public void removeField(final Field aField) { this.fields.remove(aField); } }
54/162
package compiler; import java.util.Iterator; import java.util.Set; public class CompilationUnit { private Set classes; public void addClass(final Class aClass) { this.classes.add(aClass); } public void removeClass(final Class aClass) { this.classes.remove(aClass); } public Class getClass(final String aName) { final Iterator iterator = this.classes.iterator(); while (iterator.hasNext()) { final Class aClass = (Class) iterator.next(); if (aClass.getName().equals(aName)) { return aClass; } } return null; } }
55/162
How to generate microcode for
– Microsoft Windows operating system – Intel Pentium processor
Add a generateCode() method in each class
56/162
public class Method { … public String generateCode() { String generatedCode = ""; /* Do something at the beginning. */ final Iterator iterator = this.statements.iterator(); while (iterator.hasNext()) { final Statement aStatement = (Statement) iterator.next(); generatedCode += aStatement.generateCode(); } /* Do something at the end. */ return generatedCode; } } public class Field { … public String generateCode() { String generatedCode = ""; /* Do something. */ return generatedCode; } } public class Statement { … public String generateCode() { String generatedCode = ""; /* Do something. */ return generatedCode; } }
57/162
public class Class { … public String generateCode() { String generatedCode = ""; /* Do something at the beginning. */ final Iterator iteratorOnFields = this.fields.iterator(); while (iteratorOnFields.hasNext()) { final Field aField = (Field) iteratorOnFields.next(); generatedCode += aField.generateCode(); } final Iterator iteratorOnMethods = this.methods.iterator(); while (iteratorOnMethods.hasNext()) { final Method aMethod = (Method) iteratorOnMethods.next(); generatedCode += aMethod.generateCode(); } /* Do something at the end. */ return generatedCode; } }
58/162
public class CompilationUnit { … public String generateCode() { String generatedCode = ""; /* Do something at the beginning. */ final Iterator iterator = this.classes.iterator(); while (iterator.hasNext()) { final Class aClass = (Class) iterator.next(); generatedCode += aClass.generateCode(); } /* Do something at the end. */ return generatedCode; } }
59/162
cu : CompilationUnit c : Class m : Method s : Statement m : Main generateCode( ) generateCode( ) generateCode( ) generateCode( )
60/162
Limitations of the naïve implementation
– What about generating code for
Combinatorial explosion of generateCodeForXXX() methods in each class
61/162
Requirements
– Decouple the data structure
– From algorithms on the data structure
The Visitor design pattern guides you to do that!
62/162
Name: Visitor Intent: “Represent an operation to be
performed on the elements of an object
the elements on which it operates.”
63/162
Motivation: “Consider a compiler that
represents programs as abstract syntax
abstract syntax trees for "static semantic" analyses like checking that all variables are
64/162
Motivation (cont’d): “[…] It will be confusing
to have type-checking code mixed with pretty-printing code or flow analysis code. […] It would be better if each new operation could be added separately, and the node classes were independent of the operations that apply to them.”
65/162
Motivation (cont’d): “We can have both by
packaging related operations from each class in a separate object, called a visitor, and passing it to elements of the abstract syntax tree as it's traversed.”
66/162
Motivation (cont’d): “When an element
accepts the visitor, it sends a request to the visitor that encodes the element's class. It also includes the element as an argument. The visitor will then execute the operation for that element—the operation that used to be in the class of the element.”
67/162
Applicability
– An object structure contains many classes of
– Many distinct and unrelated operations need to be performed on objects in an object structure… – The classes defining the object structure rarely change, but you often want to define new
68/162
Structure
69/162
Participants
– Visitor (NodeVisitor)
each class…
– ConcreteVisitor (TypeCheckingVisitor)
– Element (Node)
– ConcreteElement (AssignmentNode)
– ObjectStructure (Program)
interface to allow the visitor to visit its elements
(see Composite) or a collection
70/162
Collaborations
71/162
Consequences: … Implementation: … Sample code: … Known uses
– ASTs – Meta-models – …
Related patterns: Composite
72/162
package compiler.visitor; import compiler.Class; import compiler.CompilationUnit; import compiler.Field; import compiler.Method; import compiler.Statement; public interface Visitor { void open(final Class aClass); void open(final CompilationUnit aCompilationUnit); void open(final Method aMethod); void close(final Class aClass); void close(final CompilationUnit aCompilationUnit); void close(final Method aMethod); void visit(final Field aField); void visit(final Statement aStatement); }
73/162
public class Method { … public void accept(final Visitor aVisitor) { aVisitor.open(this); final Iterator iterator = this.statements.iterator(); while (iterator.hasNext()) { final Statement aStatement = (Statement) iterator.next(); aStatement.accept(aVisitor); } aVisitor.close(this); } } public class Field { … public void accept(final Visitor aVisitor) { aVisitor.visit(this); } } public class Statement { … public void accept(final Visitor aVisitor) { aVisitor.visit(this); } }
74/162
public class Class { … public void accept(final Visitor aVisitor) { aVisitor.open(this); final Iterator iteratorOnFields = this.fields.iterator(); while (iteratorOnFields.hasNext()) { final Field aField = (Field) iteratorOnFields.next(); aField.accept(aVisitor); } final Iterator iteratorOnMethods = this.methods.iterator(); while (iteratorOnMethods.hasNext()) { final Method aMethod = (Method) iteratorOnMethods.next(); aMethod.accept(aVisitor); } aVisitor.close(this); } }
75/162
public class CompilationUnit { … public void accept(final Visitor aVisitor) { aVisitor.open(this); final Iterator iterator = this.classes.iterator(); while (iterator.hasNext()) { final Class aClass = (Class) iterator.next(); aClass.accept(aVisitor); } aVisitor.close(this); } }
76/162
cu : CompilationUnit c : Class m : Method s : Statement m : Main generateCode( ) generateCode( ) generateCode( ) generateCode( )
77/162
cu : CompilationUnit c : Class m : Method s : Statement m : Main generateCode( ) generateCode( ) generateCode( ) generateCode( )
m : Main cu : CompilationUnit c : Class m : Method s : Statement v : IVisitor accept(IVisitor) accept(IVisitor) accept(IVis itor) accept(IVisitor)78/162
By using the visitor design pattern
– Decouple data structure and algorithms – Put the traversal in only one place, in the AST – Allow the addition of new algorithms without changing the data structure
Much better, clearer implementation!
79/162
The Visitor design pattern is useful
anywhere you have
– A data (stable) structure – Algorithms (infinite) on that data structure
Design patterns provided good solution to
recurrent problems!
80/162
Definition Quality Form Example Catalogue Practice Conclusion
81/162
Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides ; Design Patterns: Elements of Reusable Object-Oriented Software ; Addison-Wesley, 1995
82/162
Design patterns
– Development and maintenance – Design/implementation levels – Examples in C++ and Smalltalk
Divided in three categories
– Behavioural – Creational – Structural
83/162
84/162
Abstract Factory (87)
– Provide an interface for creating families of related or dependent objects without specifying their concrete classes
Adapter (139)
– Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces
85/162
Bridge (151)
– Decouple an abstraction from its implementation so that the two can vary independently
Builder (97)
– Separate the construction of a complex object from its representation so that the same construction process can create different representations
86/162
Chain of Responsibility (223)
– Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it
87/162
Command (233)
– Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations
Composite (163)
– Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions
88/162
Decorator (175)
– Attach additional responsibilities to an object
alternative to subclassing for extending functionality
Facade (185)
– Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use
89/162
Factory Method (107)
– Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses
Flyweight (195)
– Use sharing to support large numbers of fine- grained objects efficiently
90/162
Interpreter (243)
– Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language
Iterator (257)
– Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation
91/162
Mediator (273)
– Define an object that encapsulates how a set of
coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently
Memento (283)
– Without violating encapsulation, capture and externalize an object's internal state so that the
92/162
Observer (293)
– Define a one-to-many dependency between
all its dependents are notified and updated automatically
Prototype (117)
– Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype
93/162
Proxy (207)
– Provide a surrogate or placeholder for another
Singleton (127)
– Ensure a class only has one instance, and provide a global point of access to it
94/162
State (305)
– Allow an object to alter its behaviour when its internal state changes. The object will appear to change its class
Strategy (315)
– Define a family of algorithms, encapsulate each
lets the algorithm vary independently from clients that use it
95/162
Template Method (325)
– Define the skeleton of an algorithm in an
Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure
96/162
Visitor (331)
– Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates
97/162
Definition Quality Form Example Catalogue Practice Conclusion
98/162
“The strict modeling of the real world leads to reflect today’s realities but not necessarily tomorrow’s. The abstractions that emerge during design are key to making a design flexible.” —Erich Gamma, 1994
99/162
“The description of communicating objects and classes customized to solve general design problem in a particular context.” —Erich Gamma, 1994
100/162
“Each design pattern lets some aspect of system structure vary independently of other aspects, thereby making a system more robust to a particular kind of change.” —Erich Gamma, 1994
101/162
The following is only complementary to reading books and practicing
102/162
Scattered information
– Informal text
A suggested example rather than a
general rule
103/162
When encountering complex problems?
– Numerous design patterns (is there any complete list out there?) – Granularity
104/162
Iterative induction process
– From an example to an abstraction to an application to the abstraction to an application… – Validation process?
105/162
Use known categories
– Behavioural – Creational – Structural
Use the Internet Read and discuss others’ code
106/162
Use known categories
– Behavioural – Creational – Structural
Use the Internet Read and discuss others’ code
107/162
Use known categories
– Behavioural – Creational – Structural
Use the Internet Read and discuss others’ code
108/162
Assess the trade-offs
– Flexibility – Complexity
109/162
Assess the trade-offs
– Flexibility – Complexity
110/162
Assess the trade-offs
– Flexibility – Complexity
Sometimes it is necessary to remove the
solution of a DP used in the code
111/162
Sometimes it is necessary to remove the
solution of a DP used in the code
112/162
“There is a natural relation between patterns and refactorings. “Patterns are where you want to be; refactorings are ways to get there from somewhere else” —Josuha Kerievsky citing Martin Fowler, 2004
113/162
114/162
Refactoring to a Visitor
– Previous example of code generation from a common AST
Implementing a variant of the Visitor
– padl.kernel.impl.Constituent.accept(IVisitor) – padl.kernel.impl.Constituent.accept(IVisitor, String) – padl.kernel.impl.Constituent.accept(Class, IVisitor, String, boolean)
Refactoring away from the Visitor
– ptidej.statement.creator.classfiles.loc.BCELLOCFinder
115/162
Refactoring to a Visitor
– Previous example of code generation from a common AST
Implementing a variant of the Visitor
– padl.kernel.impl.Constituent.accept(IVisitor) – padl.kernel.impl.Constituent.accept(IVisitor, String) – padl.kernel.impl.Constituent.accept(Class, IVisitor, String, boolean)
Refactoring away from the Visitor
– ptidej.statement.creator.classfiles.loc.BCELLOCFinder
116/162
Refactoring to a Visitor
– Previous example of code generation from a common AST
Implementing a variant of the Visitor
– padl.kernel.impl.Constituent.accept(IVisitor) – padl.kernel.impl.Constituent.accept(IVisitor, String) – padl.kernel.impl.Constituent.accept(Class, IVisitor, String, boolean)
Refactoring away from the Visitor
– ptidej.statement.creator.classfiles.loc.BCELLOCFinder
117/162
Implementing a variant of the Visitor
– Problem when implementing the solution of the Visitor design pattern
118/162
Implementing a variant of the Visitor
– Problem when implementing the solution of the Visitor design pattern
119/162
Implementing a variant of the Visitor
– Problem when implementing the solution of the Visitor design pattern
120/162
Implementing a variant of the Visitor
– Problem when implementing the solution of the Visitor design pattern
– Solution
121/162
Implementing a variant of the Visitor
– Problem when implementing the solution of the Visitor design pattern
– Solution
122/162
Use the introspection
mechanism of Java
private boolean accept( final java.lang.Class currentReceiver, final IVisitor visitor, final String methodName, final boolean shouldRecurse) { acceptClassName = currentReceiver.getName(); java.lang.Class argument = null; try { argument = visitor.getClass().getClassLoader().loadClass(acceptClassName); } catch (final ClassNotFoundException e) { visitor.unknownConstituentHandler(methodName, this); return false; } try { final Method method = visitor.getClass().getMethod( methodName, new java.lang.Class[] { argument }); method.invoke(visitor, new Object[] { this }); return true; } catch (final Exception e) { if (e instanceof NoSuchMethodException) { visitor.unknownConstituentHandler(methodName + '(‘ + argument.getName() + ')', this); } else { throw new RuntimeException(e); } } return false; }
123/162
Use the introspection
mechanism of Java
private boolean accept( final java.lang.Class currentReceiver, final IVisitor visitor, final String methodName, final boolean shouldRecurse) { acceptClassName = currentReceiver.getName(); java.lang.Class argument = null; try { argument = visitor.getClass().getClassLoader().loadClass(acceptClassName); } catch (final ClassNotFoundException e) { visitor.unknownConstituentHandler(methodName, this); return false; } try { final Method method = visitor.getClass().getMethod( methodName, new java.lang.Class[] { argument }); method.invoke(visitor, new Object[] { this }); return true; } catch (final Exception e) { if (e instanceof NoSuchMethodException) { visitor.unknownConstituentHandler(methodName + '(‘ + argument.getName() + ')', this); } else { throw new RuntimeException(e); } } return false; }
final Method method = visitor.getClass().getMethod( methodName, new java.lang.Class[] { argument }); method.invoke(visitor, new Object[] { this });
124/162
Use the introspection
mechanism of Java
private boolean accept( final java.lang.Class currentReceiver, final IVisitor visitor, final String methodName, final boolean shouldRecurse) { acceptClassName = currentReceiver.getName(); java.lang.Class argument = null; try { argument = visitor.getClass().getClassLoader().loadClass(acceptClassName); } catch (final ClassNotFoundException e) { visitor.unknownConstituentHandler(methodName, this); return false; } try { final Method method = visitor.getClass().getMethod( methodName, new java.lang.Class[] { argument }); method.invoke(visitor, new Object[] { this }); return true; } catch (final Exception e) { if (e instanceof NoSuchMethodException) { visitor.unknownConstituentHandler(methodName + '(‘ + argument.getName() + ')', this); } else { throw new RuntimeException(e); } } return false; }
125/162
Use the introspection
mechanism of Java
private boolean accept( final java.lang.Class currentReceiver, final IVisitor visitor, final String methodName, final boolean shouldRecurse) { acceptClassName = currentReceiver.getName(); java.lang.Class argument = null; try { argument = visitor.getClass().getClassLoader().loadClass(acceptClassName); } catch (final ClassNotFoundException e) { visitor.unknownConstituentHandler(methodName, this); return false; } try { final Method method = visitor.getClass().getMethod( methodName, new java.lang.Class[] { argument }); method.invoke(visitor, new Object[] { this }); return true; } catch (final Exception e) { if (e instanceof NoSuchMethodException) { visitor.unknownConstituentHandler(methodName + '(‘ + argument.getName() + ')', this); } else { throw new RuntimeException(e); } } return false; }
acceptClassName = currentReceiver.getName(); java.lang.Class argument = null; try { argument = visitor.getClass().getClassLoader().loadClass(acceptClassName); } catch (final ClassNotFoundException e) { visitor.unknownConstituentHandler(methodName, this); return false; }
126/162
Use the introspection
mechanism of Java
private boolean accept( final java.lang.Class currentReceiver, final IVisitor visitor, final String methodName, final boolean shouldRecurse) { acceptClassName = currentReceiver.getName(); java.lang.Class argument = null; try { argument = visitor.getClass().getClassLoader().loadClass(acceptClassName); } catch (final ClassNotFoundException e) { visitor.unknownConstituentHandler(methodName, this); return false; } try { final Method method = visitor.getClass().getMethod( methodName, new java.lang.Class[] { argument }); method.invoke(visitor, new Object[] { this }); return true; } catch (final Exception e) { if (e instanceof NoSuchMethodException) { visitor.unknownConstituentHandler(methodName + '(‘ + argument.getName() + ')', this); } else { throw new RuntimeException(e); } } return false; }
127/162
Use the introspection
mechanism of Java
private boolean accept( final java.lang.Class currentReceiver, final IVisitor visitor, final String methodName, final boolean shouldRecurse) { acceptClassName = currentReceiver.getName(); java.lang.Class argument = null; try { argument = visitor.getClass().getClassLoader().loadClass(acceptClassName); } catch (final ClassNotFoundException e) { visitor.unknownConstituentHandler(methodName, this); return false; } try { final Method method = visitor.getClass().getMethod( methodName, new java.lang.Class[] { argument }); method.invoke(visitor, new Object[] { this }); return true; } catch (final Exception e) { if (e instanceof NoSuchMethodException) { visitor.unknownConstituentHandler(methodName + '(‘ + argument.getName() + ')', this); } else { throw new RuntimeException(e); } } return false; }
catch (final Exception e) { if (e instanceof NoSuchMethodException) { visitor.unknownConstituentHandler(methodName + '(‘ + argument.get...; } else { throw new RuntimeException(e); } }
128/162
Use the introspection mechanism of Java
– No more duplicated accept(…) methods – Handles cases for visit(…) and
– Plus, allows extensions to the data structure without changing all existing Visitors
129/162
Use the introspection mechanism of Java
– No more duplicated accept(…) methods – Handles cases for visit(…) and
– Plus, allows extensions to the data structure without changing all existing Visitors
130/162
Refactoring to a Visitor
– Previous example of code generation from a common AST
Implementing a variant of the Visitor
– padl.kernel.impl.Constituent.accept(IVisitor) – padl.kernel.impl.Constituent.accept(IVisitor, String) – padl.kernel.impl.Constituent.accept(Class, IVisitor, String, boolean)
Refactoring away from the Visitor
– ptidej.statement.creator.classfiles.loc.BCELLOCFinder
131/162
Refactoring away from the Visitor
final FileInputStream fis = new FileInputStream(path); final ClassParser parser = new ClassParser(fis, path); final JavaClass clazz = parser.parse(); clazz.accept(this.instFinder); fis.close(); public class BCELLOCFinder implements Visitor { private JavaClass currentClass; public void visitCode(final Code aCode) { } public void visitCodeException(final CodeException aCodeException) { } // 18 other empty “visit” methods public void visitJavaClass(final JavaClass aClass) { this.currentClass = aClass; final Method[] methods = aClass.getMethods(); for (int i = 0; i < methods.length; i++) { this.visitMethod(methods[i]); } } // 4 more empty “visit” methods public void visitMethod(final Method aMethod) { Integer count = null; final String ckey = this.adaptor.adapt(this.currentClass); final String mkey = this.adaptor.adapt(this.currentClass, aMethod); final Map methodMap = this.methodMap(ckey); count = this.getLOC(code); methodMap.put(mkey, count); } // 6 more empty “visit” methods }
132/162
Refactoring away from the Visitor
final FileInputStream fis = new FileInputStream(path); final ClassParser parser = new ClassParser(fis, path); final JavaClass clazz = parser.parse(); clazz.accept(this.instFinder); fis.close(); public class BCELLOCFinder implements Visitor { private JavaClass currentClass; public void visitCode(final Code aCode) { } public void visitCodeException(final CodeException aCodeException) { } // 18 other empty “visit” methods public void visitJavaClass(final JavaClass aClass) { this.currentClass = aClass; final Method[] methods = aClass.getMethods(); for (int i = 0; i < methods.length; i++) { this.visitMethod(methods[i]); } } // 4 more empty “visit” methods public void visitMethod(final Method aMethod) { Integer count = null; final String ckey = this.adaptor.adapt(this.currentClass); final String mkey = this.adaptor.adapt(this.currentClass, aMethod); final Map methodMap = this.methodMap(ckey); count = this.getLOC(code); methodMap.put(mkey, count); } // 6 more empty “visit” methods }
final FileInputStream fis = new FileInputStream(path); final ClassParser parser = new ClassParser(fis, path); final JavaClass clazz = parser.parse(); clazz.accept(this.instFinder); fis.close();
133/162
Refactoring away from the Visitor
final FileInputStream fis = new FileInputStream(path); final ClassParser parser = new ClassParser(fis, path); final JavaClass clazz = parser.parse(); clazz.accept(this.instFinder); fis.close(); public class BCELLOCFinder implements Visitor { private JavaClass currentClass; public void visitCode(final Code aCode) { } public void visitCodeException(final CodeException aCodeException) { } // 18 other empty “visit” methods public void visitJavaClass(final JavaClass aClass) { this.currentClass = aClass; final Method[] methods = aClass.getMethods(); for (int i = 0; i < methods.length; i++) { this.visitMethod(methods[i]); } } // 4 more empty “visit” methods public void visitMethod(final Method aMethod) { Integer count = null; final String ckey = this.adaptor.adapt(this.currentClass); final String mkey = this.adaptor.adapt(this.currentClass, aMethod); final Map methodMap = this.methodMap(ckey); count = this.getLOC(code); methodMap.put(mkey, count); } // 6 more empty “visit” methods }
134/162
Refactoring away from the Visitor
final FileInputStream fis = new FileInputStream(path); final ClassParser parser = new ClassParser(fis, path); final JavaClass clazz = parser.parse(); clazz.accept(this.instFinder); fis.close(); public class BCELLOCFinder implements Visitor { private JavaClass currentClass; public void visitCode(final Code aCode) { } public void visitCodeException(final CodeException aCodeException) { } // 18 other empty “visit” methods public void visitJavaClass(final JavaClass aClass) { this.currentClass = aClass; final Method[] methods = aClass.getMethods(); for (int i = 0; i < methods.length; i++) { this.visitMethod(methods[i]); } } // 4 more empty “visit” methods public void visitMethod(final Method aMethod) { Integer count = null; final String ckey = this.adaptor.adapt(this.currentClass); final String mkey = this.adaptor.adapt(this.currentClass, aMethod); final Map methodMap = this.methodMap(ckey); count = this.getLOC(code); methodMap.put(mkey, count); } // 6 more empty “visit” methods }
public void visitJavaClass(final JavaClass aClass) { this.currentClass = aClass; final Method[] methods = aClass.getMethods(); for (int i = 0; i < methods.length; i++) { this.visitMethod(methods[i]); } }
135/162
Refactoring away from the Visitor
– 28 empty methods – Hard-coded call the visitMethod(…)
– JavaClasses do not contain other similar
– Unnecessary code, complexity
136/162
Refactoring away from the Visitor
– 28 empty methods – Hard-coded call the visitMethod(…)
– JavaClasses do not contain other similar
– Unnecessary code, complexity
137/162
Unnecessary code, complexity
– Trade-offs of (most of) design patterns
138/162
Trade-offs of (most of) design patterns
– Flexibility
– Complexity
139/162
Trade-offs of (most of) design patterns
– Flexibility
– Complexity
140/162
cu : CompilationUnit c : Class m : Method s : Statement m : Main generateCode( ) generateCode( ) generateCode( ) generateCode( )
m : Main cu : CompilationUnit c : Class m : Method s : Statement v : IVisitor accept(IVisitor) accept(IVisitor) accept(IVis itor) accept(IVisitor)141/162
Trade-offs of (most of) design patterns
– Flexibility
– Complexity
142/162
Trade-offs of (most of) design patterns
– Flexibility
– Complexity
143/162
Flexibility
– Favour composition over inheritance
Add one level of indirection
and (possibly)
Use double-dispatch
144/162
Add one level of indirection
“Rather than giving you a lengthy explanation, let me just point you to the Strategy pattern. It is my prototypical example for the flexibility of composition over inheritance.” Erich Gamma, 2005
145/162
Add one level of indirection
http://itewbm.tistory.com/29
146/162
Add one level of indirection
http://programmers.stackexchange.com/questions/203210/ is-context-inheritance-as-shown-by-head-first-design-patterns-duck-example-ir
147/162
148/162
Add one level of indirection
http://programmers.stackexchange.com/questions/203210/ is-context-inheritance-as-shown-by-head-first-design-patterns-duck-example-ir
N
p e c i a l c
e f
“ n
e h a v i
r ” w i t h t h e N u l l O b j e c t d e s i g n p a t t e r n E n c a p s u l a t e w h a t v a r i e s
149/162
Add one level of indirection
“You have a container, and you plug in some smaller objects. These smaller objects configure the container and customize the behaviour of the container. This is possible since the container delegates some behaviour to the smaller thing. In the end you get customization by configuration. ” Erich Gamma, 2005
(With minor adaptations)
150/162
Add one level of indirection
“You have a container, and you plug in some smaller objects. These smaller objects configure the container and customize the behaviour of the container. This is possible since the container delegates some behaviour to the smaller thing. In the end you get customization by configuration. ” Erich Gamma, 2005
(With minor adaptations)
151/162
Use double-dispatch
– Object receives a message – Object sends a message back with itself as parameter
152/162
Use double-dispatch
– Object receives a message – Object sends a message back with itself as parameter
print(CtxWriter aCTXWriter, Rect aRectangle) { ... } ... print(StringWriter aStringWriter, Triangle aTriangle) { ... } ... PrinterWriter printer = new SystemWriter(); IShape shape = new Square(); print(printer, shape);
153/162
http://c2.com/cgi-bin/wiki?DoubleDispatchExample http://www.patentstorm.us/patents/6721807/description.html
public class SystemWriter implements PrinterWriter { @Override public void print(final Rect rect) { System.out.print("Printed the rectangle: "); System.out.println(this); } @Override public void print(final Square aSquare) { System.out.print("Printed the square: "); System.out.println(this); } } public class Rect implements IShape { @Override public void printOn(final PrinterWriter aWriter) { aWriter.print(this); } } public class Square implements IShape { @Override public void printOn(final PrinterWriter aWriter) { aWriter.print(this); } } public class Main { public static void main(final String[] args) { final PrinterWriter writer = new SystemWriter(); IShape shape; shape = new Rect(); shape.printOn(writer); shape = new Square(); shape.printOn(writer); } } public interface PrinterWriter { void print(final Rect aRect); void print(final Square aSquare); ... } public interface IShape { void printOn(final PrinterWriter aWriter); }
154/162
Unnecessary code, complexity
– Trade-offs of (most of) design patterns
155/162
Beware also of “bad” solutions to recurring
design problems
http://www.jot.fm/issues/issue_2006_07/column4/
156/162
Anti-patterns (also antipatterns)
– A common response to a recurring problem that is usually ineffective and may be counterproductive – Code smells are symptoms of “bad” programming
157/162
Two examples of anti-patterns
– Visitor design and JavaClasses
ptidej.statement.creator.classfiles.loc.BCELLOCFinder
– Blob (aka God Class)
158/162
Definition Quality Form Example Catalogue Practice Conclusion
159/162
Patterns ease communication Patterns improve regularity
and quality… even without a name…
Patterns avoid surprises
i.e., reinventing the wheel differently each time
Patterns generate architectures
160/162
Identify a recurring design problem Identify a design pattern that potentially
solve the problem
Assess the costs and benefits of
implementing the proposed solution
– Quality and quality without a name
161/162
Identify a recurring design problem Identify that the solution brings
– Unneeded flexibility – Unnecessary complexity
Assess the costs and benefits of removing
the proposed solution
162/162
Tools supporting design patterns
– “GoF” book
– CASE Tools
– Navigation