(OO) Design Patterns
What they’re all about
Examinable Skills from 210
Composite: https://www.youtube.com/watch?v=THiP0qJlSNk
Observer (without Java's help): https://www.youtube.com/watch?v=s72vPpx2LNA
Observer (with Java's help): https://youtu.be/wEorv69GRfg <<< DEPRACATED!
What is a design pattern?
A tried and true solution, to a commonly encountered problem
Industry noticed this too!
Basically, smart people, who have done this a lot, are making a suggestion!
Where does the name come from?
Design Patterns Format
This technique was translated into OO S/W Dev
In 310 we’ll focus on OO patterns
OO Design patterns specifically help developers navigate object oriented solutions, and often make smart use of polymorphism to solve some problems.
There are actually lots of kind of software design patterns (Architectural patterns, Web design patterns, etc) but we’re sticking with OO ones in 310.
There are LOTS of OO design patterns!
this is just some of them!
structural design patterns are design patterns that ease the design by identifying a simple way to realize relationships between entities
creational design patterns are design patterns that deal with object creation mechanisms, trying to create objects in a manner suitable to the situation.
behavioural design patterns are design patterns that identify common communication patterns between objects and realize these patterns
They fit into 3 basic categories
Don’t worry about this
CODE SMELLS VIOLATING PRINCIPLES
REFACTORINGS
PRINCIPLED CODE and often PATTERNS
Hence:
Patterns (just like generally principled code) ARISE and EMERGE
We will be looking at each pattern in the context of which principles are violated, which refactorings solve the violations, and how this results naturally in design pattern designs
Emerging to Design Patterns
Basic recipe for understanding a design pattern solution
THE OBSERVER PATTERN EMERGENT STORY
Watcher
Watchee
THE OBSERVER PATTERN EMERGENT STORY
WatcherA
Watchee
WatcherB
Lots of notification code
Lots of watching code
Switch on watcher
duplication
Don’t Repeat Yourself!
Single responsibility violation
Dependency Inversion Violation
THE OBSERVER PATTERN EMERGENT STORY
WatcherA
Watchee
WatcherB
Lots of notification code
Lots of watching code
Switch on watcher
duplication
Don’t Repeat Yourself!
Single responsibility violation
extract class
Subject
pull up methods
introduce hierarchy
notify( )
Dependency Inversion Violation
THE OBSERVER PATTERN UNFOLDING STORY
Dependency Inversion Violation
WatcherA
Watchee
WatcherB
Lots of watching code
Switch on watcher
duplication
Single responsibility violation
Subject
Observer
extract class
extract class
pull up method
introduce hierarchy
notify( )
Don’t Repeat Yourself!
update( )
THE OBSERVER PATTERN EMERGENT STORY
WatcherA
Watchee
WatcherB
Subject
Observer
depend on abstraction
Dependency Inversion Violation
notify( )
update( )
*
For Observer...
Composite - review
“I want to be able to have a hierarchical organisation, but don’t want to write special code for containers and leaf nodes when walking the tree”
The “write special code” is the hint at where the OO polymorphic power play comes in.
Composite — So what is it we don’t want?
for (Leaf leaf : leaves){
leaf.print();
}
for (Group subgroup : subgroups){
subgroup.print();
}
Group.print():
two calls to what should be essentially the same thing
this is going to get annoying if you’re walking the list for lots of reasons!
this is a code smell!
Composite — OO Design pattern solution
Leaf extends component and implements the “operation”
Composite (or Group, or whatever) also extends the Component, implements the “operation”, but also has group-related stuff
We introduce a higher level of abstraction: a “Component”
in our example, the “operation” was print.
void Composite.print:
for (Component c : components){
c.print();
}
isn’t that so much nicer?
void Leaf.print :
print(contents)
For Composite...
Let’s spot principles violations
Download the starter code here
This is 210 review, but we’re now looking at this from a principles perspective
Activity Link:
Examinable Skills So Far
PATTERNS
OBSERVER
COMPOSITE
Now a few more patterns!
Let’s go over a few more examples of OO design patterns, once again examining them wrt
principles, abstraction and polymorphism
Lots of design patterns
We will focus on…
Factory Pattern
INTENT:
A framework needs to standardize the architectural model for a range of applications, but allow for individual applications to define their own domain objects and provide for their instantiation.
http://sourcemaking.com/design_patterns/factory_method
Simplest scenario (not the end of the story)
One single kind of product
This is the real car behaviour
Simplest scenario (not the end of the story)
One single kind of product
This is the real car behaviour
But all of this is about making the car!
So what’s the code smell?
What’s the principle violation?
What’s the fix?
Simplest scenario (not the end of the story)
So this is nice!
No issues.
But the code won’t stay this way.
Note the new routing:
What if we have different kinds of cars?
So what’s the code smell?
What’s the principle violation?
What’s the fix?
Move Car Specific stuff into the factory too!
Making methods move in too
So now we can make different kinds of cars …. But ...
So what’s the code smell?
What’s the principle violation?
What’s the fix?
Introduce a Factory Hierarchy
public static void main(String[] args) {
//Car car = new Car();
//CarFactory carFactory = new CarFactory();
//Car car = carFactory.requestCar("Big");
CarFactory carFactory = new BigCarFactory();
Car car = carFactory.requestCar();
}
Looking at it in context...
public class BigCarFactory extends CarFactory {
@Override
public Car requestCar() {
WheelSet wheels = new BigWheels();
Chassis chassis = new BigChassis();
//and lots more things
return new BigCar(wheels, chassis);
}
}
Same method
public static void main(String[] args) {
//Car car = new Car();
//CarFactory carFactory = new CarFactory();
//Car car = carFactory.requestCar("Big");
CarFactory carFactory = new BigCarFactory();
Car car = carFactory.requestCar();
}
Factory Pattern solution for fixing Creational Overabundance
The official pattern design
Sequence Diagram
Client
ConcreteProductFactory
ConcreteProduct
new
makeProduct()
new
product
product
Passing in all the guts of the work that we just did
Factory analysis
Type Switch → Single Responsibility Violation because of too much construction code AND ALSO constructing two different things!!
The factory at the top of the factory hierarchy
AND maybe the product at the top of the product hierarchy
Make the thing makeCar() (in the example it was requestCar() createProduct()
From the client, Instead of calling the car constructor
But … BUILDER???
I think this is decorator
director
Pizza Makerer
Place base
Place Cheese
Now give it to me
MealBuilder mb;
orderMeal:
mb.addHamburger()
mb.addCheeseburger()
mb.addDrink()
return mb.getMeal()
https://refactoring.guru/design-patterns/builder
Tie In!!!!
Data parser, takes a string, encapsulates in an object
Factory and Principles
Single Responsibility Principle?
Open/Closed Principle?
Liskov Substitution Principle?
Interface Segregation Principle?
Dependency Inversion Principle?
public class BigCarFactory extends CarFactory {
@Override
public Car requestCar() {
WheelSet wheels = new BigWheels();
Chassis chassis = new BigChassis();
//and lots more things
return new BigCar(wheels, chassis);
}
}
public static void main(String[] args) {
CarFactory carFactory = getWhateverKindOfFactoryShopperWants();
carFactory.requestCar();
}
Refactor to Factory!
Take a look at the following code: Player Factory
Right now creation happens right inside of each kind of Player. Imagine it expanding to much more functionality.
Refactor this code into the Factory Pattern.
Determine what the Factory class(es) will be, and what code will be moved out of the original classes.
You try first, and then help me do it!
ACTIVITY POINTS:
Examinable Skills for Factory
Identify the principles violations that necessitate the factory pattern (single responsibility principle violation because of excessive construction code)
Identify how to form a Factory class or Factory class hierarchy to capture the construction
Be able to re-route construction through the Factory class(es) in the client (the UI, main, etc)
Refactor a class or class hierarchy where construction has bloated into the Factory pattern.
PATTERNS
FACTORY
Adapter Pattern
Convert the interface of a class into another interface clients expect.
Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.
Wrap an existing class with a new interface.
http://sourcemaking.com/design_patterns/adapter
don’t want to rewire our plug…
also still don’t want to get electrocuted
it’s a temporary fix, but if we ever went to a different country, we’d have a broken plug!!!
we are in a hotel, so not really allowed to rewire this
also what if we do it wrong??
oh no…
This is a different philosophy!
Adapter Pattern
An "off the shelf" component offers compelling functionality that you would like to reuse, but its "view of the world" is not compatible with the philosophy and architecture of the system currently being developed.
http://sourcemaking.com/design_patterns/adapter
Now I just need one of these for every country I visit, and I don’t risk electrocution!
oh that’s helpful!
Wrong kind of adapter! This is a universal adapter, which will have a switch on type!
No: What you’re describing is the right design.
Simple example
Your code uses a new version of the rectangle, but you still need to be able to use the old version
A special adapter implementation for each adaptee
Adapter
Adapter Pattern
Field name
Adapter Pattern
Field name
displayRoads()
displaySurfaces()
One class doing the work of two --->
SRP violation
MyImplClass
------
displayMap()
displayRoads()
displaySurfaces()
GoogleMapsAPI
-----
Jpeg getMap
JPeg getRoads
Jpeg getSurfaces
MapAdapter
------
PNG getMap()
PNG getRoads
PNG getSurfaces
MyImplClass
------
displayMap()
displayRoads()
displaySurfaces()
GoogleMapsAPI
-----
Jpeg getMap
JPeg getRoads
Jpeg getSurfaces
MapAdapter
------
PNG getMap()
PNG getRoads
PNG getSurfaces
BingMapsAPI
-----
gif getMap
pdf getRoads
Jpeg getSurfaces
MyImplClass
------
displayMap()
displayRoads()
displaySurfaces()
GoogleMapsAPI
-----
Jpeg getMap
JPeg getRoads
Jpeg getSurfaces
GoogleMapAdapter
------
PNG getMap()
PNG getRoads
PNG getSurfaces
BingMapsAPI
-----
gif getMap
pdf getRoads
Jpeg getSurfaces
MapAdapterInterface
------
PNG getMap()
PNG getRoads
PNG getSurfaces
BingMapAdapter
------
PNG getMap()
PNG getRoads
PNG getSurfaces
Adapter Pattern
This is the equivalent of rewiring our plug. Potentially hazardous to our health, and what if we change countries?
bingMaps.getQuad...
Adapter Pattern
Code smell plus violation of SRP
We could make a single adapter
And we are depending on an implementation! Dependency Inversion violated
Switch on Type
So we refactor, and voila, the adapter pattern!
A special adapter implementation for each adaptee
GoogleMapsAdapter
bingMapsServ.getQuad...
So we refactor, and voila, the adapter pattern!
Client
Adapter Interface
Adapter Implementation
Adaptee
AdapterInterface
Interestingly, sometimes this is presented without the abstract class/interface
This is fine, as long as you know you’re not going to want to switch adapters. If you are (and we assume we will) then an adapter interface sets you up better.
Don’t do this on our exam!
So let’s make a plug and socket combo!!
Client
Adapter Interface
Adapter Implementation
Adaptee
Adapter Implementation
Adaptee
Sequence Diagram
Client
Adapter
Adaptee
Let’s analyse
Where is the principle violation? SRP (presence of and likely duplication of conversion code), DIP (switch).
Where is the abstraction? Abstract Adapter (the interface)
Where is the concretization? Concrete Adapter
What is the overridden method (or methods)? ALL of the methods called by originally the client on the adaptee (wall socket).
When is/are the overridden method(s) called?
And what about principles?
Which principle(s) does this pattern most address?
Single Responsibility Principle, DIP
If you do the whole pattern, you get Dependency Inversion.
Examinable Skills for Adapter
Identify when a class has too much conversion code
Identify how to form an Adapter interface and associated implementation (we will always do it in this pairing, even though in practice you may choose to ditch the interface)
Be able to route calls to a desired service through the adapter you have created
Refactor a class or class hierarchy where conversion has bloated into the Adapter pattern.
Explain how the Adapter Pattern ensures principled design as mapped to the SOLID principles
PATTERNS
ADAPTER
A good example from sourcemaking
In this example, all the adaptation is being done in the client -- what principles do we see being violated?
And the solution is…..
Notice: They couldn’t change either Line or Rectangle -- those had to stay exactly the same.
They could, however, change their code to point at the adapter (hierarchy) instead of at the objects themselves
Extra Practice:
Code to Refactor here: https://www.ugrad.cs.ubc.ca/~cs310/in_class_systems/CPSC310-SquarePegAdapterWithSols.zip
Note: The client, the RHAdapterInterface and RHAdapterImplementation (or whatever you want to call them) are the ONLY pieces of the design that should need to change! The whole point of this pattern is that you don't change the client, and you don't change the adaptee (the round hole) other than making certain changes:
- replace the declaration and instantiation with a declaration of the AdapterInterface, and the AdapterImplementation respectively
- rip all the conversion code out of the client, and put it into the AdapterImplementation
- make sure the AdapterInterface (and hence implementation) have one method for each method used by the client.
SP->RH Final Design
Client
Adapter Interface
Adapter Implementation
Adaptee
Main
SquareToSomethingInterface
SquareToRoundAdapter
RoundHole
SquarePeg
constructor-->constructor
putIn-->putIn
putIn-->putIn
Singleton
Difference static v objects
Class Dog extends Animal
---------
Static maxAge
- Dog ( )
bark( )
Class Dog extends Animal
---------
- static Dog instance
---------
+ static Dog getInstance( )
- Dog ( )
+ breathe( )
+ bark( )
Instance d:@80
@30
Class Animal
---------
breathe( )
@10
Main:
Dog d = new Dog()
Dog d = Dog.getInstance( )
d.bark( );
Singleton
In software engineering, the singleton pattern is a design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system
Encapsulated "just-in-time initialization" or "initialization on first use".
http://sourcemaking.com/design_patterns/singleton
Singleton … at the code level
class Electorate: % this is the Client
private field President onlyInstance
private method whoIsThePresident():
President thePresident = President.instance()
this means it has an instance of itself
static
Access to a shared resource
Authors contributing to a shared document
public static void main(String[] args) {
Author sal = new Author();
sal.write("It was the best of times");
sal.readAloud();
Author biff = new Author();
biff.write("It was the worst of times");
biff.readAloud();
}
Get the code: CPSC310-Singleton.zip (don’t peek at the solution part)
Main:
Document d = new Doc()
Author sal = new Author(d)� Author bif = new Author(d)
Class Author():
Document d = new Document()
Document d = Document.getInstance();
We don’t want each author to have their own document
Because then we have two documents created!
That’s not collaborative!
But for the sake of argument, let’s say we don’t want to just pass in the document … imagine these authors are created too “distantly” to one another to share an invocation environment.
public static void main(String[] args) {
Author sal = new Author();
sal.write("It was the best of times");
sal.readAloud();
Author biff = new Author();
biff.write("It was the worst of times");
biff.readAloud();
}
So we make a Singleton!
Statically save an instance of the class
Statically provide access to that instance
(creating one if needed)
Protect the constructor, so only the singleton class can call it
LAZY INSTANTIATION
And now the document is collaborative
public static void main(String[] args) {
Author sal = new Author();
sal.write("It was the best of times");
sal.readAloud();
Author biff = new Author();
biff.write("It was the worst of times");
biff.readAloud();
}
2 lines!
Singleton - analysis
WHERE IS THE ABSTRACTION?
WHERE IS THE POLYMORPHISM?
Singleton - analysis
WHAT VIOLATION IS IT SOLVING?
WHAT VIOLATION IS IT CAUSING???
Singletons are going out of style
Because of their technical and design issues�
Because dependency injection is proving to be a better approach since it decouples the access control and the object implementation.
So what’s dependency injection?
Move the construction OUT OF the Author (why is the author making the document anyway?) and into the context.
Pass the document into the Author as an argument as construction time.
Good video on dependency injection WRT testing (old, but still relevant)
Examinable Skills for Singleton
Identify when to use the Singleton pattern
Identify an example of when it is being used by looking for its main features (protected constructor by making it private, static instance field)
Be able to apply the Singleton pattern to an existing class, or create one from scratch
Explain how to access the Singleton instance by calling getInstance instead of calling the constructor
Explain how singleton relates to design principles (good and bad)
Explain the difference between Singleton and Dependency Injection
PATTERNS
SINGLETON
State
State is a behavioral design pattern that lets an object alter its behavior when its internal state changes. It appears as if the object changed its class.
CS310-State.zip (barista and person projects)
The Moody Barista
The Moody Barista
public class Barista {
private int annoyingDelay;
private String mood;
private int customerNumber=0;
public void makeCoffee() throws InterruptedException {
setMood();
getBeans();
expressCoffee();
giveCustomerCoffee();
goodbye();
}
private void goodbye() throws InterruptedException {
if (mood =="Chatty") System.out.println("Have a lovely day!!!!");
if (mood =="Curt") System.out.println("bye.");
Thread.sleep(annoyingDelay);
}
private void giveCustomerCoffee() throws InterruptedException {
if (mood =="Chatty") System.out.println("Hope this is to your liking!");
if (mood =="Curt") System.out.println("Here.");
Thread.sleep(annoyingDelay);
}
This if-statement is everywhere … our instinct is to refactor the switch to different types of Barrista … but let’s look deeper...!
The Moody Barista
setMood is called every single time the Barrista makes coffee!
We can’t get a whole new barista object every time the barista changes moods. We need to be able to maintain a pointer to the same object, so that it can maintain its own internal information, but we want the barista to behave differently!
So … refactor to ChattyBarrista, and CurtBarrista
won’t work!
Barista
-------
+makeCoffee
MoodBehaviour
-------
-giveCoffee
-goodbye
private void goodbye() throws InterruptedException {
if (mood =="Chatty") System.out.println("Have a lovely day!!!!");
if (mood =="Curt") System.out.println("bye.");
Thread.sleep(annoyingDelay);
}
private void giveCustomerCoffee() throws InterruptedException {
if (mood =="Chatty") System.out.println("Hope this is to your liking!");
if (mood =="Curt") System.out.println("Here.");
Thread.sleep(annoyingDelay);
}
Barista
-------
+makeCoffee
MoodBehaviour
-------
-giveCoffee
-goodbye
CurtBehaviour
-------
-giveCoffee
-goodbye
ChattyBehaviour
-------
-giveCoffee
-goodbye
public void makeCoffee(){
setMood();
mood.giveCoffee();
mood.goodbye();
}
public void setMood(){
If thingsaregoingbadly(){
mood = new CurtBehaviour() }
else
mood = new ChattyBehaviour()
}
mood
The Moody Barrista
Yes, our refactor to types is mostly right: we still need a mood hierarchy (we still have different kinds of behaviours, and they still have things in common that we need to abstract)
<<should be dashed lines>>
The Moody Barista
But we want the Barista to HAVE those mood behaviours, not BE those moods. So the right answer is …..
Delegation!
And voila the State Pattern
<<interface>>
The total Moody Barista
Notice the delegation to moodstrat for every behaviour!
getBeans
getBeans
mood.getBeans()
This is a field!!!!!!!
Alternate way of doing it if there are no changes to makeCoffee() -- every method delegates to the Mood instance
State Analysis
How does State satisfy or exemplify each of the SOLID principles?
What abstraction is introduced?
What concretisation is introduced?
What method(s) is/are overridden?
Where are those methods called?
From within the context of use, through delegation
State class (whatever you called it).
Subtypes of the state class (like happy/sad/grumpy or “waiting” or some implementation class that extends State)
Any methods defined in the State class that require the affected behaviour
“handle” or some bunch of operations
Which principles was it trying to solve:
Smell is type switch ---> single responsibility principle because class is trying to embody multiple states at once.
State Machines
with the State Pattern
State and Strategy
The Strategy pattern is identical to state except that the client is choosing a single strategy and sticking with it.
It’s like if the Barista never changed moods -- just got initialised with one mood at the beginning of their career and stayed the same forever.
State Examinable Skills
Be able to explain why delegation is a better option than subclassing the Client class
Be able to explain the abstraction, concretisation, and polymorphism involved, as well as how and when the overridden method is invoked.
Be able to relate the State pattern to code smells and design principles.
Be able to explain and implement the simple version of the pattern and the state machine version of the pattern
Be able to explain how it relates to Strategy
PATTERNS
STATE
STRATEGY
Visitor Pattern
Examinable Skills
Let’s look at a basic implementation of a Row Selector
Client
Get the code
Starter code from the refactoring lecture: https://github.students.cs.ubc.ca/CPSC310/toy-project-refactoring
Each query becomes a tree (called an Abstract Syntax Tree)
Selection
Not
Selection
Has
“Red”
Each query becomes a tree (called an Abstract Syntax Tree)
Selection
And
Selection
Has
“Red”
Selection
Has
“Green”
And during parsing, every node calls “parse” on each of its subnodes
Selection
And
Selection
Has
“Red”
Selection
Has
“Green”
parse
parse
parse
parse
parse
parse
parse
Each parse method does two things:
And during evaluating, every node calls “evaluate” on each of its subnodes
Selection
And
Selection
Has
“Red”
Selection
Has
“Green”
evaluate
evaluate
evaluate
evaluate
evaluate
evaluate
evaluate
Each evaluate method returns a value for the node, sometimes calling evaluate on the subnodes if needed
So where does “Parsing” live? Everywhere!
Client
Parsing
SCATTERED CONCERN!
And where does “evaluation” live? Everywhere!
Client
Evaluation
SCATTERED CONCERN!
And what about each individual class? Are those designs sound?
Client
TANGLING: SRP violation!
Divergent changes!
CODE SMELL:
One class doing the work of two
So what would you want
Node
Selection
And
Not
Client
Parser
Evaluator
Has
So what would you want
Node
Selection
And
Not
Client
Parser
Evaluator
parse Selection
parse And
parse Not
parse Has
evaluate Selection
evaluate And
evaluate Not
evaluate Has
Has
But there’s an implementation problem!
Parser
parse Selection
parse And
parse Not
parse Has
Selection
And
Selection
Has
Selection
Has
If we’re moving all the parsing to the Parser class, we need calls to be within the Parser class!
parse
parse
parse
parse
parse
How, if we are in parse Selection, will we know which other parse method to call?
But the pointer to the child node lives in the AST and not in the Parser
So we move all the parse methods into the Parser
Let’s start with the parser…..
We make the references external
Yes, there is feature envy. This is a tradeoff.
And then we need to fix the call down to selection!
So why isn’t that line of code compiling?
Because selection in “node.selection” is declared as a Node.
We don’t have a parse method with “Node” as the argument type.
PARAMETER TYPES ARE DETERMINED AT COMPILATION!
We can try casting in this case….. But ….
So casting works if we know exactly what we will have
We could do a <shudder> switch on type?
But that’s horrible. And it would need to be in every single parse method that can have multiple kinds of children (prone to error!)
So on what can we blame our problem?
But Java binds PARAMETERS at COMPILE TIME
Java binds RECEIVERS at RUNTIME
This is called SINGLE DISPATCH
So we can know the dynamic type (actual type) of the receiver (meaning the object on which the method is called)
But we can only know the DECLARED type (apparent type) of the parameters
So Single Dispatch is our problem
Single Dynamic Dispatch means that only one (single) thing is taken into account when deciding which method to call.
That one single thing is the type of the receiver.
this.got(a)
Public void got(A a){ always calls this one }
What would multiple dispatch look like?
This won’t work, because Java does not support multiple dispatch!
This code won’t compile!!!
So instead we need to use Double Dispatch
DEFINITIONS
SINGLE DISPATCH: The dispatch (method calling) mechanism that Java uses to decide which method to call -- it uses ONE SINGLE dynamic piece of information: The dynamic type of the *RECEIVER* (aka: the object on which the method is called.
DOUBLE DISPATCH: Is a TRICK you can use in a single dispatch language, to gain access to the dynamic type of a parameter passed into a method, without using a switch on INSTANCE OF type.
Parser with DOUBLE DISPATCH
Parser
ParserVisitor’s parse(Selection) method calls selection.subnode.accept(this)
The compiler won’t know what selection.subnode really is.
But the runtime environment will!
Let’s say it’s the “And” node.
accept(this)
Selection
And
Selection
Has
Selection
Has
The “this” variable’s type was bound at compile time. But because it is sitting RIGHT IN the “And” class, it was bound to “And”.
So then when we call p.parse(this), the parse method that gets called will be the one that takes the “And” parameter!
1
2
We introduce a new method accept(Parser p){
p.parse(this)
}
Into every class
parse(this)
And do the same for Evaluate
Evaluator
accept(this)
Selection
And
Selection
Has
Selection
Has
We introduce a new method accept(Evaluator e){
return e.evaluate(this)
}
Into every class
evaluate(this)
But …. duplication!
Evaluator
Now, in every class we’ve got pretty much the same code!
accept(this)
Selection
And
Selection
Has
Selection
Has
evaluate(this)
accept(Parser p){
p.parse(this)
}
accept(Evaluator e){
e.evaluate(this)
}
Parser
accept(this)
parse(this)
So … Abstract!
Evaluator
accept(this)
Selection
And
Selection
Has
Selection
Has
visit(this)
accept(Parser p){
p.parse(this)
}
accept(Evaluator e){
e.evaluate(this)
}
Parser
accept(this)
visit(this)
Visitor
accept(Visitor v){
v.visit(this)
}
Both of these methods can just be one generic “accept” method
Visitor needs a “visit” method for every Node
And all the rest of the visit methods too
And we can easily make new visitors
We could make a tree-walker that prints out each node in the tree as we visit it.
this is a Selection
visitSelection(
So what are the dynamics with the new naming...
visit(Statement node):
node.subnode.accept(this)
visit(And print):
//do And stuff...
accept(Visitor v):
v.visit(this)
And
ParseVisitor
Let’s imagine stmt is a “PRINT”
statically bound
This two call approach for the sake of accessing the dynamic type is called
double dispatch
And we’ve arrived at the canonical visitor!
Put all the visit methods, for all the different types of nodes, in a single ConcreteVisitor
Eclipse’s ASTVisitor has 95 public methods: one for each type of Java language AST node:
...
Examinable Skills for Visitor
Activity
Starting with the with_visitors package of the MiniRowSelector project, extract an EvaluatorVisitor.
(for fun, you can add more language features if you want)
Activity Link: https://ubc.ca1.qualtrics.com/jfe/form/SV_eRmZQCGRHkkoNFk
And your Parser visitor will also have to change its signature:
public class ParserVisitor extends Visitor<Void> {
With methods looking like:
public Void visit(...){
Note: Because the evaluator/interp method returns a value, the declaration for the EvaluatorVisitor class will have to be:
public class EvaluatorVisitor extends Visitor<Boolean> {
And the methods will each have to return a Boolean like this:
public Boolean visit(...
public class Interpreter extends Visitor<Boolean>{
public final String row;
public Interpreter(String row){
this.row=row;
}
@Override
Boolean visit(And node) {
return (node.selection1.accept(this)) &&
(node.selection2.accept(this));
}
(in node) public abstract <X> X accept(Visitor<X> v);
In the nodes themselves:
public <X> X accept(Visitor<X> v){
return v.visit(this);
}