the singleton design pattern · abstract factory class. we could even have “intelligent”...
TRANSCRIPT
The Singleton Design Pattern
An example
▪ This program gets information about the available memory from the JVM:
▪ Why not this?
public class TestRunTime {public static void main(String[] args) {
Runtime rt=Runtime.getRuntime();long m=rt.totalMemory();System.out.println(String.format("We have %d bytes available",m));
}}
public static void main(String[] args) {Runtime rt=new Runtime(); // <- does not compile!long m=rt.totalMemory();System.out.println(String.format("We have %d bytes available",m));
}
An example (2)
public class Runtime {private static final Runtime currentRuntime = new Runtime();
/** Don't let anyone else instantiate this class */private Runtime() {}
public static Runtime getRuntime() {return currentRuntime;
}
public long totalMemory() {...
}}
▪ This is how the class Runtime is defined in the JDK:
Singleton
▪ The authors of the Runtime class wanted to be sure that there is only
one instance of that class
▪ Such a class is called a Singleton
▪ The Singleton is like a global variable. There is only on Runtime object in
the entire program.
▪ Singletons are quite popular in Java. Some examples:
java.lang.Runtime
java.awt.Desktop
...
▪ Singletons make sense if you have certain resources (runtime,
desktop,...) that only exist once
▪ Don’t use Singletons if not absolutely necessary!
How to not use Singletons
▪ Do not use Singletons to emulate global variables in Java
▪ This program is very hard to maintain because the Singleton creates a
“magic connection” between MyClass1 and MyClass2. You cannot see
from outside that MyClass1 and MyClass2 depend on each other.
class MySingleton {public String username;private static final MySingleton single = new MySingleton();private MySingleton() {}public static MySingleton getSingleton() { return single; }
}
class MyClass1 {void method1() {
MySingleton.getSingleton().username="Alice";}
}class MyClass2 {
void method2() {System.out.println(MySingleton.getSingleton().username);
}}
Singleton misused to
create a global variable
“username”
“Lazy initialization”
▪ Lazy initialization can be used if you want to create a Singleton object
only when it is really needed
▪ Here is a possible version of the Runtime class with lazy initialization:
public class Runtime {// don’t create the object at the beginningprivate static final Runtime currentRuntime = null;
private Runtime() {}
public static Runtime getRuntime() {if(currentRuntime==null) { // already created?
currentRuntime=new Runtime(); // create now}return currentRuntime;
}
public long totalMemory() { ... }}
The Factory Method Design Pattern
A zoo with small and big animals
public class Zoo {private ArrayList<Animal> animals=new ArrayList<>();
public Zoo(int n) {Random randomNumberGenerator=new Random();for(int i=0;i<n;i++) {
boolean randomB=randomNumberGenerator.nextBoolean();if(randomB)
animals.add(new Cat());else
animals.add(new Pony());}
}
public int getYearlyCosts() {int sum=0;for(Animal animal : animals) {
sum += animal.getYearlyCosts();}return sum;
}}
That’s a nice Zoo class.
But we cannot change
the type of animals in
the zoo. It’s always cats
and ponies!
Using factory methods to create objects
▪ In this code, we have moved the creation of the objects into factory
methods (=methods that create objects)
private Animal createSmallAnimal() {return new Cat();
}
private Animal createBigAnimal() {return Pony();
}
public ZooWithFactoryMethod(int n) {Random randomNumberGenerator=new Random();for(int i=0;i<n;i++) {
boolean randomB=randomNumberGenerator.nextBoolean();if(randomB)
animals.add(createSmallAnimal());else
animals.add(createBigAnimal());}
}
Okay, that looks better. The
code is now independent of
the concrete classes Cat
and Pony. But how does
that help?
Using a factory class
▪ We can no move the object creation out of the Zoo class:
public class AnimalFactory {public Animal createSmallAnimal() {
return new Cat();}
public Animal createBigAnimal() {return new Pony();
}}
public class ZooWithFactoryClass {private ArrayList<Animal> animals=new ArrayList<>();
public ZooWithFactoryClass(int n) {AnimalFactory af=new AnimalFactory();Random randomNumberGenerator=new Random();for(int i=0; i<n; i++) {
boolean randomB=randomNumberGenerator.nextBoolean();if(randomB)
animals.add(af.createBigAnimal());else
animals.add(af.createSmallAnimal());}
}...
The Zoo uses a factory
class to create the animal
objects. The Zoo class
doesn’t even know that
there are Cat and Pony
subclasses!
Using an abstract factory class
public abstract class AbstractAnimalFactory {public abstract Animal createSmallAnimal();
public abstract Animal createBigAnimal();}
public class ZooWithAbstractFactory {private ArrayList<Animal> animals=new ArrayList<>();
public ZooWithAbstractFactory(AbstractFactory af, int n) {Random randomNumberGenerator=new Random();for(int i=0; i<n; i++) {
boolean randomB=randomNumberGenerator.nextBoolean();if(randomB)
animals.add(af.createBigAnimal());else
animals.add(af.createSmallAnimal());}
}...
▪ Let’s turn the factory into an abstract class. In this way, the code is
independent of the concrete sub-classes of the Animal class:
The AbstractFactory
only declares the
factory methods.
The implementation
is somewhere else.
Sub-classing the abstract factory
public class PetFactory extends AbstractAnimalFactory {@Overridepublic Animal createSmallAnimal() {
return new Cat();}@Overridepublic Animal createBigAnimal() {
return new Pony();}
}
public class WildlifeFactory extends AbstractAnimalFactory {@Overridepublic Animal createSmallAnimal() {
return new Fox();}@Overridepublic Animal createBigAnimal() {
return new Elephant();}
}
ZooWithAbstractFactory z1=new ZooWithAbstractFactory(new PetFactory(),10);ZooWithAbstractFactory z2=new ZooWithAbstractFactory(new WildlifeFactory(),10);
We can now create zoos with different animals without changing the code
of the Zoo class:
These are two concrete
implementations of the
abstract factory class.
▪ We could even have “intelligent” factories. Instead of three methods:
we could have something more general:
▪ And in the implementation in the PetFactory class:
public abstract class AbstractAnimalFactory {public abstract Animal createSmallAnimal(); public abstract Animal createBigAnimal();public abstract Animal createVeryBigAnimal();
}
Factory methods with parameters
public abstract class AbstractAnimalFactory {public abstract Animal createAnimal(String size, String color);
}
@Overridepublic Animal createAnimal(String size, String color) {
if(size.equals(“very small”) && color.equals(“grey”))return new Mouse();
else if(size.equals(“small”))return new Cat();
else if ...}
Factory: Summary
▪ Factories are useful when you want to create objects without knowing
(or without wanting to know) the exact class:
▪ By using a factory, our Zoo class has become independent from the
concrete sub-classes of the Animal class
• The Zoo class does not even need to know which sub-classes exist
▪ Other people can now use our Zoo class with new animals that they
defined themselves
• Users of our Zoo class can add new sub-classes to the Animal classwithout having to change the code of the Zoo class
animals.add(af.createSmallAnimal());
The Observer/Observable Design Pattern
A simple application
▪ A boring application: nothing happens when you press the button
public class SimpleApp {
public static void main(String[] args) {JFrame frame=new JFrame("Hello");frame.setSize(400,200);frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JButton button=new JButton("Press me!");frame.add(button);
frame.setVisible(true); }
}Make the
window visible
Create a
window
Create a button
and add it to the
window
import javax.swing.JButton;import javax.swing.JFrame;
Reacting to button press
import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JOptionPane;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;
class ButtonActionListener implements ActionListener {@Overridepublic void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null,"Thank you!");}
}
public class AppWithActionListener {public static void main(String[] args) {
JFrame frame=new JFrame("Hello");frame.setSize(400,200);frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JButton button=new JButton("Press me!");button.addActionListener(new ButtonActionListener());frame.add(button);
frame.setVisible(true);}
}
Show a message
box when the
button is pressed
What‘s happening
here?
How an ActionListener works
▪ ActionListener is an interface with one method. In our application, we
have implemented that interface:
▪ We add our ButtonActionListener to the button:
▪ What does that mean? We are telling the button:
“Dear button, please call the actionPerformed method of my action
listener object when something interesting happens to you.”
(“Something interesting” = “Somebody clicked on you”)
class ButtonActionListener implements ActionListener {@Overridepublic void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null,"Thank you!");}
}
button.addActionListener(new ButtonActionListener());
Observer and Observables
▪ In our previous example, the ActionListener interface was used to
observe actions of the button
ActionListener = observing actions
Button click = observable action
▪ We can generalize this idea to any kind of things we want to observe
▪ Let’s try it with this simple class. We would like to observe the value of
the account.public class Account {
private int value ;
public void deposit(int d) {value+=d;
}
}
An observable account
▪ Here is the observer interface. We want that the method
accountHasChanged is called when the account changes its value.
▪ Here is an observable version of the Account class:
public interface AccountObserver {public void accountHasChanged(int newValue);
}
public class ObservableAccount {private int value ;private AccountObserver observer;
public void deposit(int d) {value+=d;if(observer!=null) {
observer.accountHasChanged(value);}
}
public void setObserver(AccountObserver o) {observer=o;
}}
We have to check first
whether there is an observer.
Everytime a deposit is
made, we tell the
observer
How to use it
public class MyObserver implements AccountObserver {@Overridepublic void accountHasChanged(int newValue) {
System.out.println(“Account has changed. New value: "+newValue);}
}
public class MyMain {
public static void main(String[] args) {ObservableAccount account=new ObservableAccount();MyObserver observer=new MyObserver();account.setObserver(observer);
account.deposit(100);account.deposit(50);
}}
Observer/Observable: Summary
▪ The Observer/Observable design pattern allows you to separate objects
containing data from the code reacting to changes in the data
• The ObservableAccount class does not need to know what happenswhen the account value changes. We can change the code in the observer without changing the account implmentation.
▪ Disadvantage: The program becomes harder to understand
• A simple call account.deposit(100) can have a lot of effects in the observer
▪ Note: Often, it would be useful to have multiple observers for one
observable. So, instead of
public void setObserver(AccountObserver o)
we would like to have
public void addObserver(AccountObserver o)
See the exercise on inginious!
The Visitor Design Pattern
class Engine extends CarElement {private int hp;
public Engine(int hp) { this.hp=hp; }
public int getHP() { return hp; }@Overridepublic int getPrice() { return hp*100; }
}
As usual, let’s start with a simple example
public abstract class CarElement {public abstract int getPrice();
}
class Wheel extends CarElement {@Overridepublic int getPrice() { return 100; }
}
class Car {private Engine engine=new Engine(90);private Wheel[] wheels=new Wheel[] {
new Wheel(), new Wheel(), new Wheel(), new Wheel()};
}
A car is built from
several CarElement
objects
class Wheel extends CarElement {@Overridepublic int getPrice() { return 100; }@Overridepublic void print() { System.out.println("A wheel"); }
}
class Engine extends CarElement {private int hp;
public Engine(int hp) { this.hp=hp; }
public int getHP() { return hp; }@Overridepublic int getPrice() { return hp*100; }@Overridepublic void print() { System.out.println("Engine with "+hp+" hp"); }
}
Print a description of the car
public abstract class CarElement {public abstract int getPrice();public abstract void print();
}
We add a print()
method to the car
elements
Print a description of the car (2)
class Car {private Engine engine=new Engine(90);private Wheel[] wheels=new Wheel[] {
new Wheel(), new Wheel(),new Wheel(), new Wheel()
};
public void print() {System.out.println(“A car”);engine.print();wheels[0].print();wheels[1].print();wheels[2].print();wheels[3].print();
}}
To print a description
of the car, the car
prints the car
elements
▪ Okay, that works. No problem here. But it’s a little bit annoying that we
have to modify the Car and CarElement classes to be able to print
them...
Calculating the price of a car
▪ Again, that works. And again, it’s a little bit annoying that we have to
modify the Car class to add this new functionality
▪ Is it really necessary that the Car class should know how to print a car
and how to calculate the price of a car? Do we have to modify the
classes everytime we want a new functionality?
class Car {private Engine engine=new Engine(90);private Wheel[] wheels=new Wheel[] {
new Wheel(), new Wheel(),new Wheel(), new Wheel()
};
public void printCar() {engine.print();wheels[0].print(); wheels[1].print(); wheels[2].print(); wheels[3].print();
}
public int getCarPrice() {return engine.getPrice()+wheels[0].getPrice()+wheels[1].getPrice()
+wheels[2].getPrice()+wheels[3].getPrice();}
}
Making everything public?
▪ We could move the printCar() and getCarPrice() methods into a
different class. But that requires that the fields of the car are public:
class Car {public Engine engine=new Engine(90);public Wheel[] wheels=new Wheel[] {
new Wheel(), new Wheel(),new Wheel(), new Wheel()
};}
class CarTools {public void printCar(Car car) {
car.engine.print();car.wheels[0].print(); car.wheels[1].print();car.wheels[2].print(); car.wheels[3].print();
}
public int getCarPrice(Car car) {return car.engine.getPrice()+car.wheels[0].getPrice()+...
}}
Not nice!
Now, everybody can
see how a car object
is implemented
internally.
The Visitor Design Pattern
▪ In the Visitor design pattern, complex data structures (like the Car class)
allow “visitors” to visit their elements.
▪ Visitors can do work (like printing, calculating the price,...) that we don’t
want to put into the Car class. Here is a visitor printing the car description:
public interface Visitor {public void visit(Wheel wheel);public void visit(Engine engine);public void visit(Car car);
}
public class PrintVisitor implements Visitor {@Overridepublic void visit(Wheel wheel) { System.out.println("A wheel"); }
@Overridepublic void visit(Engine engine) {
System.out.println("Engine with "+engine.getHP()+" hp");}
@Overridepublic void visit(Car car) { System.out.println("A car"); }
}
class Engine extends CarElement implements Visitable {private int hp;public Engine(int hp) { this.hp=hp; }public int getHP() { return hp; }@Overridepublic int getPrice() { return hp*100; }@Overridepublic void accept(Visitor visitor) {
visitor.visit(this);}
}
The Visitor Design Pattern (2)
▪ Every object visited by the visitor decides what to do with the visitor:
public interface Visitable {public void accept(Visitor visitor);
}
class Wheel extends CarElement implements Visitable {@Overridepublic int getPrice() { return 100; }@Overridepublic void accept(Visitor visitor) {
visitor.visit(this);}
}
A wheel does not
know what a visitor
does. It just
“accepts” the
visitor.
This will print “A wheel”
This will print
“Engine with 90 hp”
The Visitor Design Pattern (3)
▪ We can now print the car description for a car object:
car.accept(new PrintVisitor());
▪ Note:
• The Visitor does not need to know how a car is structured internally
• The visited car does not need to know how to print a description
class Car implements Visitable {private Engine engine=new Engine(90);private Wheel[] wheels=new Wheel[] {
new Wheel(), new Wheel(),new Wheel(), new Wheel()
};
@Overridepublic void accept(Visitor visitor) {
visitor.visit(this);engine.accept(visitor);wheels[0].accept(visitor);wheels[1].accept(visitor);wheels[2].accept(visitor);wheels[3].accept(visitor);
}}
This will print “A car”
Here, we send the
visitor to the car
elements
Another visitor for price calculation
▪ We can implement the price calculation also as a visitor:
▪ Again, the visitor does not need to know how a car is structured
internally. It doesn’t even know how many wheels a car has!
public class PriceVisitor implements Visitor {int totalPrice=0;
@Overridepublic void visit(Wheel wheel) { totalPrice+=wheel.getPrice(); }@Overridepublic void visit(Engine engine) { totalPrice+=engine.getPrice(); }@Overridepublic void visit(Car car) { }
}
Visitors: Summary
▪ Visitors are a way to separate the structure of an object from the code
working on that object
• Visited object = the object containing the data of interest
• Visitor = the code working on the data
▪ In our examples, we can
• modify the structure of the car without having to modify thePrintVisitor or the PriceVisitor
• We could add more wheels (6 instead of 4)
• We could store the wheels in a list instead of an array
• ...
• create new visitors without having to modify the Car and CarElementclasses