Wzorce projektowe 2
Marcin Orchel
AGH University of Science and Technology in Poland
1 Diagramy UML
2 Wzorce operacyjne
Wprowadzenie do diagramów sekwencji UML
diagram sekwencji, ang. sequence diagram – typ diagramu interakcji, który koncentruje się na wymianie wiadomości pomiędzy wieloma liniami życia, diagram sekwencji opisuje interakcję za pomocą
wyszczególnienia sekwencji wiadomości, które są wymieniane, wraz ze specyfikacją.
linia życia, ang. lifeline – obiekt oraz linia poniżej
specyfikacja wykonania, ang. ExecutionSpecification – cienki prostokąt na linii życia
wiadomość, ang. message – strzałka wskazuje kierunek komunikacji, typ wiadomości call – wywołanie metody, return - powrót
:Type1 :Type2
synchCall return
asynchCall
Rysunek 1:Źródło: własne
Diagramy UML 6 / 120
Wzorce operacyjne
Pozwala uniknąć wiązania nadawcy z jego odbiorcą, ponieważ umożliwia obsłużenieżądaniawięcej niż jednemu obiektowi. Łączy w łańcuch obiekty odbiorcze i przekazuje między nimi żądanie do momenu obsłużenia go.
Łańcuch zobowiązań
Handler
handleRequest()
ConcreteHandler1
handleRequest()
ConcreteHandler2
handleRequest()
Client
successor
Wzorce operacyjne 9 / 120
abstract class PurchasePower { //Handler protected static final double BASE = 500;
protected PurchasePower successor;
abstract protected double getAllowable();
abstract protected String getRole();
public void setSuccessor(PurchasePower successor) { this.successor = successor;
}
public void processRequest(PurchaseRequest request) { if (request.getAmount() < this.getAllowable()) {
System.out.println(this.getRole() + " will approve
$" + request.getAmount());
} else if (successor != null) { successor.processRequest(request);
} } }
Łańcuch zobowiązań
class ManagerPPower extends PurchasePower { //
ConcreteHandler1
protected int getAllowable(){
return BASE*10;
}
protected String getRole(){
return "Manager";
} }
class DirectorPPower extends PurchasePower { protected int getAllowable(){
return BASE*20;
}
protected String getRole(){
return "Director";
class VicePresidentPPower extends PurchasePower { protected int getAllowable(){
return BASE*40;
}
protected String getRole(){
return "Vice President";
} }
class PresidentPPower extends PurchasePower { protected int getAllowable(){
return BASE*60;
}
protected String getRole(){
return "President";
} }
Łańcuch zobowiązań
class PurchaseRequest { private double amount;
private String purpose;
public PurchaseRequest(double amount, String purpose) { this.amount = amount;
this.purpose = purpose;}
public double getAmount() { return amount;}
public void setAmount(double amt) { amount = amt;
}
public String getPurpose() { return purpose;
}
public void setPurpose(String reason) { purpose = reason;
class CheckAuthority { // Client
public static void main(String[] args) {
ManagerPPower manager = new ManagerPPower();
DirectorPPower director = new DirectorPPower();
VicePresidentPPower vp = new VicePresidentPPower();
PresidentPPower president = new PresidentPPower();
manager.setSuccessor(director);
director.setSuccessor(vp);
vp.setSuccessor(president);
Łańcuch zobowiązań
try {
while (true) {
System.out.println("Enter the amount to check who should approve your expenditure.");
System.out.print(">");
double d = Double.parseDouble(new BufferedReader(new
InputStreamReader(System.in)).readLine());
manager.processRequest(new PurchaseRequest(d,
"General"));
}
} catch(Exception e) { System.exit(1);
} }
Łańcuch zobowiązań może być stosowany ze wzorcemKompozyt.
Wtedy obiekt nadrzędny komponentu może pełnić funkcję jego następnika.
Polecenie (ang. command )
Kapsułkujeżądanie w formie obiektu. Umożliwia to parametryzację klienta przy użyciu różnych żądań oraz umieszczanie żądań w kolejkach i dziennikach, a także zapewnia obsługę cofania operacji.
Client
Receiver
action()
Invoker Command
execute()
ConcreteCommand
state execute() receiver
receiver.action();
Rysunek 3:Źródło: własne
Wzorce operacyjne 18 / 120
Polecenie
import java.util.List;
import java.util.ArrayList;
/** The Command interface */
public interface Command { void execute();
}
/** The Invoker class */
public class Switch {
private List<Command> history = new ArrayList<Command>();
public void storeAndExecute(Command cmd) { this.history.add(cmd); // optional
cmd.execute();
}
/** The Receiver class */
public class Light { public void turnOn() {
System.out.println("The light is on");
}
public void turnOff() {
System.out.println("The light is off");
} }
Polecenie
/** The Command for turning on the light */
public class FlipUpCommand implements Command { //
ConcreteCommand
private Light theLight; //receiver public FlipUpCommand(Light light) {
this.theLight = light;
}
@Override
public void execute() { theLight.turnOn();
} }
/** The Command for turning off the light */
public class FlipDownCommand implements Command { private Light theLight;
public FlipDownCommand(Light light) { this.theLight = light;
}
@Override
public void execute() { theLight.turnOff();
} }
Polecenie
/* The test class or client */
public class PressSwitch {
public static void main(String[] args){
// Check number of arguments if (args.length != 1) {
System.err.println("Argument \"ON\" or \"OFF\" is required.");
System.exit(-1);
}
Light lamp = new Light(); // Receiver
Command switchUp = new FlipUpCommand(lamp);
Command switchDown = new FlipDownCommand(lamp);
Switch mySwitch = new Switch();
switch(args[0]) { case "ON":
mySwitch.storeAndExecute(switchUp);
break;
case "OFF":
mySwitch.storeAndExecute(switchDown);
break;
default:
System.err.println("Argument \"ON\" or \"OFF\" is required.");
System.exit(-1);
} } }
Polecenie
do implementacji Polecenia można użyć wzorca Kompozyt
Polecenie, które jest kopiowane przed umieszczeniem w historii, działa jak Prototyp
określa reprezentację gramatyki języka oraz interpreter, który
wykorzystuje tę reprezentację do interpretowania zdań z danego języka
Interpreter
TerminalExpression
interpret(Context)
Client
Context
AbstractExpression
interpret(Context)
NonterminalExpression
interpret(Context)
Wzorce operacyjne 27 / 120
import java.util.Map
interface Expression { // AbstractExpression
public int interpret(Map<String,Expression> variables);
}
class Number implements Expression { // TerminalExpression private int number;
public Number(int number) { this.number = number;
}
public int interpret(Map<String,Expression> variables) { return number;
} }
Interpreter
class Plus implements Expression { // NonterminalExpression Expression leftOperand;
Expression rightOperand;
public Plus(Expression left, Expression right) { leftOperand = left;
rightOperand = right;
}
public int interpret(Map<String,Expression> variables) { return leftOperand.interpret(variables) +
rightOperand.interpret(variables);
} }
class Minus implements Expression { Expression leftOperand;
Expression rightOperand;
public Minus(Expression left, Expression right) { leftOperand = left;
rightOperand = right;
}
public int interpret(Map<String,Expression> variables) { return leftOperand.interpret(variables) -
rightOperand.interpret(variables);
} }
Interpreter
class Variable implements Expression { private String name;
public Variable(String name) { this.name = name; } public int interpret(Map<String,Expression> variables) {
if(null==variables.get(name)) return 0; //Either return new Number(0).
return variables.get(name).interpret(variables);
} }
import java.util.Map;
import java.util.Stack;
class Evaluator implements Expression { private Expression syntaxTree;
public Evaluator(String expression) { Stack<Expression> expressionStack = new Stack<Expression>();
for (String token : expression.split(" ")) { if (token.equals("+")) {
Expression subExpression = new
Plus(expressionStack.pop(), expressionStack.pop());
expressionStack.push( subExpression );
} else if (token.equals("-")) {
// it’s necessary remove first the right operand from the stack
Expression right = expressionStack.pop();
Interpreter
// ..and after the left one
Expression left = expressionStack.pop();
Expression subExpression = new Minus(left, right);
expressionStack.push( subExpression );
} else
expressionStack.push( new Variable(token) );
}
syntaxTree = expressionStack.pop();
}
public int interpret(Map<String,Expression> context) { return syntaxTree.interpret(context);
} }
import java.util.Map;
import java.util.HashMap;
public class InterpreterExample { // Client public static void main(String[] args) {
String expression = "w x z - +";
Evaluator sentence = new Evaluator(expression);
Map<String,Expression> variables = new HashMap<String,Expression>();
variables.put("w", new Number(5));
variables.put("x", new Number(10));
variables.put("z", new Number(42));
int result = sentence.interpret(variables);
System.out.println(result);
} }
Iterator
zapewnia sekwencyjny dostęp do elementówobiektu złożonego bez ujawniania jego wewnętrznej reprezentacji.
Aggregate
createIterator()
ConcreteAggregate
createIterator()
Iterator
first() next() isDone() currentItem()
ConcreteIterator
return new ConcreteIterator(this)
Rysunek 5:Źródło: własne
Wzorce operacyjne 36 / 120
Iterator
public interface Iterator<E> { // java.util.Iterator boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E>
action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
} }
private class Itr implements Iterator<E> { // class in java.util.AbstractList ConcreteIterator
/**
* Index of element to be returned by subsequent call to next.
*/
int cursor = 0;
/**
* Index of element returned by most recent call to next or
* previous. Reset to -1 if this element is deleted by a call
* to remove.
*/
int lastRet = -1;
Iterator
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
int expectedModCount = modCount;
public boolean hasNext() { return cursor != size();
}
public E next() {
checkForComodification();
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) { checkForComodification();
throw new NoSuchElementException();
} }
Iterator
public void remove() { if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor) cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
} }
final void checkForComodification() { if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Wzorce operacyjne 41 / 120
w interfejsie List // Aggregate
ListIterator<E> listIterator();
w AbstractList // ConcreteAggregate
public ListIterator<E> listIterator() { return listIterator(0);
}
public ListIterator<E> listIterator(final int index) { rangeCheckForAdd(index);
return new ListItr(index);
}
Iterator
można użyć tego wzorca do przechodzenia po zawartości kompozytów
określa obiekt kapsułkujący informacje o interakcji między obiektami z danego zbioru. Wzorzec ten pomaga zapewnić luźne powiązanie, ponieważ zapobiega bezpośredniemu odwoływaniu się obiektów do siebie i umożliwia niezależne modyfikowanie interakcji między nimi.
Mediator
Mediator Colleague
ConcreteMediator
ConreteColleague1
ConreteColleague2
mediator
Wzorce operacyjne 45 / 120
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
interface Command { // Colleague void execute();
}
Mediator
interface Mediator { void book();
void view();
void search();
void registerView(BtnView v);
void registerSearch(BtnSearch s);
void registerBook(BtnBook b);
void registerDisplay(LblDisplay d);
}
//Concrete mediator
class ParticipantMediator implements Mediator { BtnView btnView;
BtnSearch btnSearch;
BtnBook btnBook;
LblDisplay show;
//....
public void registerView(BtnView v) { btnView = v;}
public void registerSearch(BtnSearch s) { btnSearch = s;}
public void registerBook(BtnBook b) { btnBook = b;
}
public void registerDisplay(LblDisplay d) { show = d;
}
Mediator
public void book() {
btnBook.setEnabled(false);
btnView.setEnabled(true);
btnSearch.setEnabled(true);
show.setText("booking...");
}
public void view() {
btnView.setEnabled(false);
btnSearch.setEnabled(true);
btnBook.setEnabled(true);
show.setText("viewing...");
}
public void search() {
btnSearch.setEnabled(false);
btnView.setEnabled(true);
btnBook.setEnabled(true);
show.setText("searching...");
Wzorce operacyjne 49 / 120
//A concrete colleague
class BtnView extends JButton implements Command { Mediator med;
BtnView(ActionListener al, Mediator m) { super("View");
addActionListener(al);
med = m;
med.registerView(this);
}
public void execute() { med.view();
} }
Mediator
//A concrete colleague
class BtnSearch extends JButton implements Command { Mediator med;
BtnSearch(ActionListener al, Mediator m) { super("Search");
addActionListener(al);
med = m;
med.registerSearch(this);
}
public void execute() { med.search();
} }
//A concrete colleague
class BtnBook extends JButton implements Command { Mediator med;
BtnBook(ActionListener al, Mediator m) { super("Book");
addActionListener(al);
med = m;
med.registerBook(this);
}
public void execute() { med.book();
} }
Mediator
class LblDisplay extends JLabel { Mediator med;
LblDisplay(Mediator m) { super("Just start...");
med = m;
med.registerDisplay(this);
setFont(new Font("Arial", Font.BOLD, 24));
} }
class MediatorDemo extends JFrame implements ActionListener {
Mediator med = new ParticipantMediator();
MediatorDemo() {
JPanel p = new JPanel();
p.add(new BtnView(this, med));
p.add(new BtnBook(this, med));
p.add(new BtnSearch(this, med));
getContentPane().add(new LblDisplay(med), "North");
getContentPane().add(p, "South");
setSize(400, 200);
setVisible(true);}
public void actionPerformed(ActionEvent ae) { Command comd = (Command) ae.getSource();
comd.execute();}
public static void main(String[] args) { new MediatorDemo();}
}
Wzorce operacyjne 54 / 120
Mediator
mediator vsfasada: fasada to abstrakcyjne ujęcie interfejsu, fasada nie definiuje nowych funkcji, a klasy podsystemu nie wiedzą o jej istnieniu, mediator to wyabstrahowanie dowolnej komunikacji między obiektami
Bez naruszania kapsułkowania rejestruje i zapisujew zewnętrznej jednostce wewnętrzny stan obiektu, co umożliwia późniejsze przywrócenie obiektu według zapamiętanego stanu.
Pamiątka
Rysunek 7:Źródło: wikipedia
import java.util.List;
import java.util.ArrayList;
class Originator { private String state;
// The class could also contain additional data that is not part of the
// state saved in the memento..
public void set(String state) {
System.out.println("Originator: Setting state to " + state);
this.state = state;
}
public Memento saveToMemento() {
System.out.println("Originator: Saving to Memento.");
return new Memento(this.state);
}
Pamiątka
public void restoreFromMemento(Memento memento) { this.state = memento.getSavedState();
System.out.println("Originator: State after restoring from Memento: " + state);
}
public static class Memento { private final String state;
public Memento(String stateToSave) { state = stateToSave;
}
public String getSavedState() { return state;
} } }
class Caretaker {
public static void main(String[] args) { List<Originator.Memento> savedStates = new ArrayList<Originator.Memento>();
Originator originator = new Originator();
originator.set("State1");
originator.set("State2");
savedStates.add(originator.saveToMemento());
originator.set("State3");
// We can request multiple mementos, and choose which one to roll back to.
savedStates.add(originator.saveToMemento());
originator.set("State4");
originator.restoreFromMemento(savedStates.get(1));
} }
Pamiątka
Pamiątka umożliwia zachowanie stanu potrzebnegoPoleceniu do cofnięcia efektów jego wykonania.
Określa zależność jeden do wielu między obiektami. Kiedy zmieni się stan jednego z obiektów, wszystkie obiekty zależne od niego są o tym automatycznie powiadamiane i aktualizowane.
inne nazwy publikuj-subskrybuj (ang. publish-subscribe)
Obserwator
Subject Attach(Observer) Detach(Observer) Notify()
ConcreteSubject subjectState GetState() SetState()
Observer Update()
ConcreteObserver observerState Update() observers
subject for every o in observers { o->Update()
}
return subjectState
observerState = subject->GetState()
Wzorce operacyjne 63 / 120
public interface Observer { // java.util.Observer void update(Observable o, Object arg);
}
public class Observable { // Subject private boolean changed = false;
private Vector<Observer> obs; // observers public Observable() {
obs = new Vector<>();
}
public synchronized void addObserver(Observer o) { if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) { obs.addElement(o);
} }
Obserwator
public synchronized void deleteObserver(Observer o) { obs.removeElement(o);
}
public void notifyObservers() { // notify notifyObservers(null);
}
public void notifyObservers(Object arg) { Object[] arrLocal;
synchronized (this) { if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
public synchronized void deleteObservers() { obs.removeAllElements();
}
protected synchronized void setChanged() { changed = true;
}
protected synchronized void clearChanged() { changed = false;
}
public synchronized boolean hasChanged() { return changed;
}
public synchronized int countObservers() { return obs.size();
}
Obserwator
import java.util.Observable;
import java.util.Scanner;
class EventSource extends Observable implements Runnable { // ConcreteSubject
public void run() { while (true) {
String response = new Scanner(System.in).next();
setChanged();
notifyObservers(response);
} } }
import java.util.Observable;
import static java.lang.System.out;
class MyApp {
public static void main(String[] args) { out.println("Enter Text >");
EventSource eventSource = new EventSource();
eventSource.addObserver( (Observable obj, Object arg) -> {
out.println("\nReceived response: " + arg);
});
new Thread(eventSource).start();
} }
Obserwator
można utworzyć dodatkową klasę między podmiotami, a obserwatorami, o nazwie Manager do której można zastosować wzorzec Singleton. Klasa ta działa jakmediatormiędzy podmiotami i obserwatorami. Klasa Manager może mieć mapę przyporządkowania obserwatorów do podmiotów i implementować złożoną semantykę aktualizacji
Umożliwia obiektowi modyfikację zachowania w wyniku zmiany wewnętrznego stanu. Wygląda to tak, jakby obiekt zmienił klasę.
Stan
Context +request()
state.handle()
State +handle()
ConcreteStateA +handle()
ConcreteStateB +handle() Rysunek 9:Źródło: wikipedia
interface Statelike { // State
void writeName(StateContext context, String name); //
handle }
class StateLowerCase implements Statelike { //
ConcreteState
@Override
public void writeName(final StateContext context, final String name) {
System.out.println(name.toLowerCase());
context.setState(new StateMultipleUpperCase());
} }
Stan
class StateMultipleUpperCase implements Statelike { /** Counter local to this state */
private int count = 0;
@Override
public void writeName(final StateContext context, final String name) {
System.out.println(name.toUpperCase());
/* Change state after StateMultipleUpperCase’s writeName() gets invoked twice */
if(++count > 1) {
context.setState(new StateLowerCase());
} } }
class StateContext { // Context private Statelike myState;
StateContext() {
setState(new StateLowerCase());
} /**
* Setter method for the state.
* Normally only called by classes implementing the State interface.
* @param newState the new state of this context
*/
void setState(final Statelike newState) { myState = newState;
}
public void writeName(final String name) { myState.writeName(this, name);
} }
Wzorce operacyjne 74 / 120
Stan
public class DemoOfClientState {
public static void main(String[] args) { final StateContext sc = new StateContext();
sc.writeName("Monday");
sc.writeName("Tuesday");
sc.writeName("Wednesday");
sc.writeName("Thursday");
sc.writeName("Friday");
sc.writeName("Saturday");
sc.writeName("Sunday");
} }
obiekty State mogą być singletonami
wzorzec Pyłekokreśla kiedy i jak można współużytkować obiekty State
Strategia (ang. strategy )
Określa rodzinę algorytmów, kapsułkuje każdy z nich i umożliwia ich zamienne stosowanie. Wzorzec ten pozwala zmieniać algorytmy niezależnie od korzystających z nich klientów.
Context
ContextInterface()
Strategy
AlgorithmInterface()
ConcreteStrategyA
AlgorithmInterface()
ConcreteStrategyB
AlgorithmInterface()
ConcreteStrategyC
AlgorithmInterface() strategy
Rysunek 10:Źródło: własne
Wzorce operacyjne 78 / 120
Strategia
interface BillingStrategy { // Strategy
public double getActPrice(double rawPrice);
}
// Normal billing strategy (unchanged price) class NormalStrategy implements BillingStrategy {
@Override
public double getActPrice(double rawPrice) { return rawPrice;
} }
// Strategy for Happy hour (50% discount)
class HappyHourStrategy implements BillingStrategy {
@Override
public double getActPrice(double rawPrice) { return rawPrice*0.5;
class Customer { // Context private List<Double> drinks;
private BillingStrategy strategy;
public Customer(BillingStrategy strategy) { this.drinks = new ArrayList<Double>();
this.strategy = strategy;
}
public void add(double price, int quantity) {
drinks.add(strategy.getActPrice(price * quantity));
}
// Payment of bill
public void printBill() { double sum = 0;
for (Double i : drinks) { sum += i;
}
System.out.println("Total due: " + sum);
drinks.clear();
}
Wzorce operacyjne 80 / 120
Strategia
// Set Strategy
public void setStrategy(BillingStrategy strategy) { this.strategy = strategy;
} }
public class StrategyPatternWiki {
public static void main(String[] args) {
Customer a = new Customer(new NormalStrategy());
// Normal billing a.add(1.0, 1);
// Start Happy Hour
a.setStrategy(new HappyHourStrategy());
a.add(1.0, 2);
// New Customer
Customer b = new Customer(new HappyHourStrategy());
// The Customer pays a.printBill();
// End Happy Hour
b.setStrategy(new NormalStrategy());
b.add(1.3, 2);
b.add(2.5, 1);
b.printBill();
} }
Strategia
obiekty Strategy mogą być tworzone jakopyłki
strategia vsdekorator: dekorator umożliwia zmianę skórki obiektu, strategia modyfikuje mechanizmy
most należy do wzorców strukturalnych, a strategia do wzorców operacyjnych, strategia operuje na algorytmach
most posiada klasę RefinedAbstraction, oraz klasę abstrakcyjną Abstraction, natomiast strategia posiada klasę Context, która nie jest abstrakcyjna
Metoda szablonowa (ang. template method )
Określa szkielet algorytmu i pozostawia doprecyzowanie niektórych jego kroków podklasom. Umożliwia modyfikację niektórych etapów algorytmu w podklasach bez zmiany jego struktury.
AbstractClass
templateMethod() primitiveOperation1() primitiveOperation2()
ConcreteClass
primitiveOperation1() primitiveOperation2()
...
primitiveOperation1() ...
primitiveOperation2() ...
Rysunek 11:Źródło: własne
Wzorce operacyjne 86 / 120
Metoda szablonowa
/**
* An abstract class that is common to several games in
* which players play against the others, but only one is
* playing at a given time.
*/
abstract class Game { // AbstractClass
/* Hook methods. Concrete implementation may differ in each subclass*/
protected int playersCount;
abstract void initializeGame();
abstract void makePlay(int player); //
primitiveOperation1
abstract boolean endOfGame();
abstract void printWinner(); // primitiveOperation2
/* A template method : */
public final void playOneGame(int playersCount) { this.playersCount = playersCount;
initializeGame();
int j = 0;
while (!endOfGame()) { makePlay(j);
j = (j + 1) % playersCount;
}
printWinner();
} }
Metoda szablonowa
//Now we can extend this class in order //to implement actual games:
class Monopoly extends Game {
/* Implementation of necessary concrete methods */
void initializeGame() { // Initialize players // Initialize money }
void makePlay(int player) { // Process one turn of player }
boolean endOfGame() {
// Return true if game is over // according to Monopoly rules }
void printWinner() { // Display who won }
Wzorce operacyjne 89 / 120
class Chess extends Game {
/* Implementation of necessary concrete methods */
void initializeGame() { // Initialize players
// Put the pieces on the board }
void makePlay(int player) {
// Process a turn for the player }
boolean endOfGame() {
// Return true if in Checkmate or // Stalemate has been reached }
void printWinner() {
// Display the winning player }
/* Specific declarations for the chess game. */
}
Wzorce operacyjne 90 / 120
Metoda szablonowa
w metodach szablonowych fragmenty algorytmu modyfikowane są przez dziedziczenie, a wstrategiiza pomocą delegowania zmieniany jestcały algorytm
metody wytwórcze mogą być wywoływane przez metody szablonowe
Reprezentuje operację wykonywaną na elementach struktury obiektów. Wzorzec ten umożliwia zdefiniowanie nowej operacjibez zmieniania klas elementów, na których działa.
Odwiedzający
Element accept(Visitor) ObjectStructure
Visitor
visitConcreteElementA(ConcreteElementA) visitConcreteElementB(ConcreteElementB)
ConcreteVisitor1 visitConcreteElementA(ConcreteElementA) visitConcreteElementB(ConcreteElementB)
ConcreteElementA accept(Visitor v) operationA()
ConcreteVisitor2 visitConcreteElementA(ConcreteElementA) visitConcreteElementB(ConcreteElementB)
ConcreteElementB accept(Visitor v) operationB()
v->visitConcreteElementA(this) v->visitConcreteElementB(this)
Wzorce operacyjne 93 / 120
interface ICarElementVisitor { // Visitor void visit(Wheel wheel);
void visit(Engine engine);
void visit(Body body);
void visit(Car car);
}
interface ICarElement { // Element
void accept(ICarElementVisitor visitor);
}
class Wheel implements ICarElement { // ConcreteElementA private String name;
public Wheel(String name) { this.name = name;
}
public String getName() { return this.name;
}
Odwiedzający
public void accept(ICarElementVisitor visitor) { visitor.visit(this);
} }
class Engine implements ICarElement {
public void accept(ICarElementVisitor visitor) { visitor.visit(this);
} }
class Body implements ICarElement {
public void accept(ICarElementVisitor visitor) { visitor.visit(this);
} }
class Car implements ICarElement { ICarElement[] elements;
public Car() {
this.elements = new ICarElement[] { new Wheel("front left"),
new Wheel("front right"), new Wheel("back left") , new Wheel("back right"), new Body(), new Engine() };
}
public void accept(ICarElementVisitor visitor) { for(ICarElement elem : elements) {
elem.accept(visitor);
}
visitor.visit(this);
} }
Odwiedzający
class CarElementPrintVisitor implements ICarElementVisitor {
public void visit(Wheel wheel) {
System.out.println("Visiting " + wheel.getName() + "
wheel");
}
public void visit(Engine engine) {
System.out.println("Visiting engine");
}
public void visit(Body body) {
System.out.println("Visiting body");
}
public void visit(Car car) {
System.out.println("Visiting car");
}
class CarElementDoVisitor implements ICarElementVisitor { public void visit(Wheel wheel) {
System.out.println("Kicking my " + wheel.getName() + "
wheel");
}
public void visit(Engine engine) {
System.out.println("Starting my engine");
}
public void visit(Body body) {
System.out.println("Moving my body");
}
public void visit(Car car) {
System.out.println("Starting my car");
} }
Odwiedzający
public class VisitorDemo {
public static void main(String[] args) { ICarElement car = new Car();
car.accept(new CarElementPrintVisitor());
car.accept(new CarElementDoVisitor());
} }
Chcemy miec poszczególne ConcreteElement w osobnych projektach.
Problem jest taki, że projekty te muszą być zależne od projektu zawierającego interfejs Element, zaś projekt, który zawiera interfejs Element, musi być zależny od interfejsu Visitor, który jest zależny od poszczególnych ConcreteElement, otrzymujemy zależność cykliczną projektów.
Rozwiązanie: dodatkowy interfejs SolverVisitor jest w projekcie SVM Core, SolverSVMTesterVisitor w projekcie SVM Tester, a poszczególni odwiedzający jak np. SolverDetailLoggerVisitor mogą być też w SVM Tester, element Solver w SVM Core, a klasy dziedziczące we własnych projektach. W projekcie SVM Core nie mamy wiedzy na temat klas dziedziczących po klasie Solver.
public interface SolverVisitor {
void visit(Solver solver);
}
Odwiedzający
public interface SolverSVMTesterVisitor extends SolverVisitor
{
void visit(AdvancedSolverExternalSVM advancedSolverExternalSVM);
void visit(AdvancedSolverL2NormSVC advancedSolverL2NormSVC);
void visit(AdvancedSolverSVC advancedSolverSVC);
void visit(AdvancedSolverDeltaSVR advancedSolverDeltaSVR);
void visit(AdvancedSolverDistributionFinder advancedSolverDistributionFinder);
void visit(AdvancedSolverDoubleSVC advancedSolverDoubleSVC);
void visit(SolverSVCCurveFitting solverSVCCurveFitting);
public class Bar {
public void visit(Foo foo) {
System.out.println("Visiting Foo");
}
public void visit(SubFoo foo) {
System.out.println("Visiting SubFoo");
} }
public class Foo {}
public class SubFoo extends Foo {}
public class Main {
public static void main(String[] args) { Bar bar = new Bar();
Foo foo = new SubFoo();
bar.visit(foo);
} }
Odwiedzający
public class SolverDetailLoggerVisitor implements SolverSVMTesterVisitor
{
private String detailLog;
public SolverDetailLoggerVisitor() {}
public String getDetailLog() {
return detailLog;}
@Override
public void visit(Solver solver) {
ReflectionUtils.invokeCurrentMethod(this, solver);}
@Override
public void visit(AdvancedSolverExternalSVM advancedSolverExternalSVM)
public static Object invokeCurrentMethod(Object object, Object... parameters)
{
try {
final StackTraceElement[] ste =
Thread.currentThread().getStackTrace();
String methodName = ste[2].getMethodName();
Class<?>[] parametersClasses = new Class[parameters.length];
for (int i = 0; i < parameters.length; i++) {
parametersClasses[i] = parameters[i].getClass();
}
Method m = object.getClass().getMethod(methodName, parametersClasses);
Object o = m.invoke(object, parameters);
return o;
} catch (IllegalAccessException |
IllegalArgumentException | InvocationTargetException | NoSuchMethodException e)
{
logger.error(Constants.EMPTY_STRING, e);
}
assert false;
return null;
}
Wzorce operacyjne 104 / 120
Odwiedzający
public interface Solver {
void solve();
Curve getCurve();
void acceptVisitor(SolverVisitor solverVisitor);
}
public class SolverBFDF extends SolverWithKernel {
@Override
public void acceptVisitor(SolverVisitor solverVisitor) {
solverVisitor.visit(this);
} ...
SolverDetailLoggerVisitor solverDetailLoggerVisitor = new SolverDetailLoggerVisitor();
solverParameters.getSvmSolver().acceptVisitor(solverDetailLoggerVisitor);
stringBuffer.append(solverDetailLoggerVisitor.getDetailLog());
można również pozbyć się klasy SolverSVMTesterVisitor, wtedy nie musimy dodawać do niej metody po dodaniu nowego typu Solvera
Odwiedzający
wzorzec ten może być wykorzystany do przeprowadzeniainterpretacji wzorzec ten może być wykorzystany do zastosowania operacji dla struktury obiektów we wzorcu Kompozyt
odwiedzający vs kompozyt: we wzorcu Odwiedzający jest jedna lokalizacja dla operacji, w kompozycie są one rozproszone
reguły biznesowe mogą być łączone ponownie za pomocą logiki boolean
Specyfikacja
public interface ISpecification {
boolean isSatisfiedBy(Object candidate);
ISpecification and(ISpecification other);
ISpecification andNot(ISpecification other);
ISpecification or(ISpecification other);
ISpecification orNot(ISpecification other);
ISpecification not();
}
Specyfikacja
public abstract class CompositeSpecification implements ISpecification
{
public abstract boolean isSatisfiedBy(Object candidate);
public ISpecification and(ISpecification other) {
return new AndSpecification(this, other); } public ISpecification andNot(ISpecification other) {
return new AndNotSpecification(this, other); } public ISpecification or(ISpecification other) {
return new OrSpecification(this, other); }
public ISpecification orNot(ISpecification other) {
return new OrNotSpecification(this, other); } public ISpecification not()
{
return new NotSpecification(this); } }
Specyfikacja
public class AndSpecification extends CompositeSpecification
{
private ISpecification leftCondition;
private ISpecification rightCondition;
public AndSpecification(ISpecification left, ISpecification right)
{
leftCondition = left;
rightCondition = right;
}
public boolean isSatisfiedBy(Object candidate) {
return leftCondition.isSatisfiedBy(candidate) &&
rightCondition.isSatisfiedBy(candidate);
public class AndNotSpecification extends CompositeSpecification
{
private ISpecification leftCondition;
private ISpecification rightCondition;
public AndSpecification(ISpecification left, ISpecification right)
{
leftCondition = left;
rightCondition = right;
}
public boolean isSatisfiedBy(Object candidate) {
return leftCondition.isSatisfiedBy(candidate) &&
rightCondition.isSatisfiedBy(candidate) != true;
} }
Specyfikacja
public class OrSpecification extends CompositeSpecification {
private ISpecification leftCondition;
private ISpecification rightCondition;
public OrSpecification(ISpecification left, ISpecification right)
{
leftCondition = left;
rightCondition = right;
}
public boolean isSatisfiedBy(Object candidate) {
return leftCondition.isSatisfiedBy(candidate) ||
rightCondition.isSatisfiedBy(candidate);
}
public class OrNotSpecification extends CompositeSpecification
{
private ISpecification leftCondition;
private ISpecification rightCondition;
public OrSpecification(ISpecification left, ISpecification right)
{
leftCondition = left;
rightCondition = right;
}
public boolean isSatisfiedBy(Object candidate) {
return leftCondition.isSatisfiedBy(candidate) ||
rightCondition.isSatisfiedBy(candidate) != true;
} }
Specyfikacja
public class NotSpecification extends CompositeSpecification
{
private ISpecification wrapped;
public NotSpecification(ISpecification x) {
wrapped = x;
}
public boolean isSatisfiedBy(Object candidate) {
return !wrapped.isSatisfiedBy(candidate);
} }
OverDueSpecification overDue = new OverDueSpecification();
NoticeSentSpecification noticeSent = new NoticeSentSpecification();
InCollectionSpecification inCollection = new InCollectionSpecification();
// example of specification pattern logic chaining ISpecification<Invoice> sendToCollection =
overDue.and(noticeSent).and(inCollection.not());
invoiceCollection = service.getInvoices();
for (Invoice currentInvoice : invoiceCollection) {
if (sendToCollection.isSatisfiedBy(currentInvoice)) { currentInvoice.sendToCollection();
} }
Bez wzorca specyfikacja
invoiceCollection = service.getInvoices();
for (Invoice currentInvoice : invoiceCollection) { currentInvoice.sendToCollectionIfNecessary();
}
//.. in the Invoice partial class:
public boolean shouldSendToCollection() { return overDue
&& noticeSent == false && inCollection == false; } public void sendToCollectionIfNecessary()
{
//Guard condition - with each of those new properties if (!shouldSendToCollection()) return;
this.sendToCollection();
}
blackboard– łączenie różnych źródeł danych
pusty obiekt, ang. null object – unikanie referencji null za pomocą dostarczania domyślnego obiektu z działaniami nie wykonującymi żadnych operacji
servant– definicja wspólnej funkcjonalności dla grupy klas bez definiowania tej funkcjonalności w każdej z nich