the singleton design pattern · abstract factory class. we could even have “intelligent”...

33
The Singleton Design Pattern

Upload: others

Post on 22-Jun-2020

4 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

The Singleton Design Pattern

Page 2: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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));

}

Page 3: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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:

Page 4: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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!

Page 5: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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”

Page 6: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

“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() { ... }}

Page 7: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

The Factory Method Design Pattern

Page 8: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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!

Page 9: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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?

Page 10: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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!

Page 11: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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.

Page 12: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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.

Page 13: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

▪ 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 ...}

Page 14: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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());

Page 15: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

The Observer/Observable Design Pattern

Page 16: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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;

Page 17: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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?

Page 18: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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());

Page 19: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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;

}

}

Page 20: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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

Page 21: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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);

}}

Page 22: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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!

Page 23: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

The Visitor Design Pattern

Page 24: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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

Page 25: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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

Page 26: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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...

Page 27: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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();}

}

Page 28: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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.

Page 29: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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"); }

}

Page 30: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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”

Page 31: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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

Page 32: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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) { }

}

Page 33: The Singleton Design Pattern · abstract factory class. We could even have “intelligent” factories. Instead of three methods: we could have something more general: ... The Observer/Observable

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