• Nie Znaleziono Wyników

Wzorce projektowe

N/A
N/A
Protected

Academic year: 2021

Share "Wzorce projektowe"

Copied!
103
0
0

Pełen tekst

(1)

Wzorce projektowe

Marcin Orchel

AGH University of Science and Technology

in Poland

(2)

Agenda

(3)

1 Diagramy UML

2 Wzorce konstrukcyjne

3 Wzorce strukturalne

(4)

Diagramy UML

(5)

Wprowadzenie do diagramów klas UML

diagram klas, ang. class diagram – podstawowymi elementami diagramu są klasy, diagram strukturalny

zależność, ang. dependency – to relacja podkreślająca, że pojedynczy element modelu wymaga innych elementów modelu do specyfikacji lub implementacji, zależny od

generalizacja, ang. generalization – uogólnienie, dziedziczenie asocjacja, ang. association – powiązanie między obiektami danych klas, strzałka obok nazwy oznacza kierunek asocjacji, powiązanie początku z końcem

agregacja, ang. aggregation – typ asocjacji, całość-część, jedna

instancja jest używana do grupowania razem zbioru instancji

kompozycja, ang. composition – typ asocjacji, silniejsza forma

agregacji, obiekt całość jest odpowiedzialny za istnienie i zapis

obiektu części, część musi być zawarta maksymalnie w jednym

obiekcie całość w danym czasie. Jeśli obiekt całość jest usuwany, to

wszystkie instancje części też są usuwane.

(6)

Wprowadzenie do diagramów klas UML cd.

Zależność vs asocjacja: asocjacja prawie zawsze oznacza, że jeden obiekt posiada drugi obiekt jako atrybut (ma referencję do), natomiast zależność oznacza zazwyczaj, że obiekt akceptuje inny obiekt jako parametr do metody, instancjonuje go, albo używa tego obiektu, ten pierwszy obiekt jest zależny od drugiego. Jeśli mamy do czynienia z asocjacją to ze względu na późniejsze użycie tego obiektu będziemy mieli do czynienia z zależnością.

agregacja vs kompozycja, w kompozycji obiekt część nie może istnieć

bez obiektu całość - death relationship

(7)

Schemat przykładowego diagramu klas UML

Class1 +Public attribute -Private attribute

#Protected attribute Package

+Public operation(parameter1)

Abstract class 2 +Public attribute

-Private attribute

#Protected attribute Package

+Public abstract operation(parameter1) dependency

Class2 +Public attribute -Private attribute

#Protected attribute Package

+Public operation(parameter1)

Class3 +Public attribute -Private attribute

#Protected attribute Package

+Public operation(parameter1) Aggregation

Class4 +Public attribute -Private attribute

#Protected attribute Package

+Public operation(parameter1) Association

Class5 +Public attribute -Private attribute

#Protected attribute Package

+Public operation(parameter1) Composition

Note

Diagramy UML 7 / 103

(8)

Wzorce konstrukcyjne

(9)

Singleton (ang. singleton)

gwarantuje, że klasa będzie miała tylko jeden egzemplarz, i zapewnia globalny dostęp do niego

w matematyce niezmienny zbiór z jednym elementem

public static <T> Set<T> Collections.singleton(T o) public static <T> List<T> Collections.singletonList(T o)

public static <K,V> Map<K,V>

Collections.singletonMap(K key, V value) java.lang.Runtime#getRuntime()

java.lang.System#getSecurityManager()

(10)

Schemat wzorca singleton

Singleton

static uniqueInstance singletonData

static instance() singletonOperation() getSingletonData()

return uniqueInstance

Wzorce konstrukcyjne 10 / 103

(11)

Metoda wytwórcza (ang. factory method )

Określa interfejs do tworzenia obiektów, przy czym umożliwia podklasom wyznaczenie klasy danego obiektu. Metoda wytwórcza umożliwia klasom przekazanie procesu tworzenia egzemplarzy podklasom.

przykłady z jdk NumberFormat.getInstance()

(12)

Schemat wzorca metody wytwórczej

Product

ConcreteProduct

Creator +factoryMethod() +anOperation()

ConcreteCreator +factoryMethod()

product=factoryMethod()

return new ConcreteProduct()

Wzorce konstrukcyjne 12 / 103

(13)

Metoda wytwórcza

public abstract class MazeGame { public MazeGame() {

Room room1 = makeRoom();

Room room2 = makeRoom();

room1.connect(room2);

}

abstract protected Room makeRoom();

}

public class MagicMazeGame extends MazeGame {

@Override

protected Room makeRoom() { return new MagicRoom();

}

}

(14)

Metoda wytwórcza

public class OrdinaryMazeGame extends MazeGame {

@Override

protected Room makeRoom() { return new OrdinaryRoom();

} }

MazeGame ordinaryGame = new OrdinaryMazeGame();

MazeGame magicGame = new MagicMazeGame();

(15)

Fabryka abstrakcyjna (ang. abstract factory )

udostępnia interfejs do tworzenia rodzin powiązanych ze sobą lub

zależnych od siebie obiektów bez określania ich konkretnych klas

(16)

Schemat fabryki abstrakcyjnej

Client

ProductA2

«interface»

AbstractProductA

ProductA1

ProductB2 ProductB1

«interface»

AbstractProductB CreateProductA()

CreateProductB()

«interface»

AbstractFactory

CreateProductA() CreateProductB() ConcreteFactory2

CreateProductA() CreateProductB() ConcreteFactory1

«import»

«import»

«import»

«instantiate»

«instantiate»

«instantiate»

(17)

Fabryka abstrakcyjna

abstract class GUIFactory {

public static GUIFactory getFactory() { int sys = readFromConfigFile("OS_TYPE");

if (sys == 0) {

return new WinFactory();

} else {

return new OSXFactory();

} }

public abstract Button createButton();

}

class WinFactory extends GUIFactory { public Button createButton() {

return new WinButton();

}

}

(18)

Fabryka abstrakcyjna

class OSXFactory extends GUIFactory { public Button createButton() {

return new OSXButton();

} }

abstract class Button {

public abstract void paint();

}

class WinButton extends Button { public void paint() {

System.out.println("Przycisk WinButton");

} }

class OSXButton extends Button {

public void paint() {

(19)

Fabryka abstrakcyjna

public class Application {

public static void main(String[] args) {

GUIFactory factory = GUIFactory.getFactory();

Button button = factory.createButton();

button.paint();

}

// Wyświetlony zostanie tekst:

// "Przycisk WinButton"

// lub:

// "Przycisk OSXButton"

}

(20)

Metoda wytwórcza vs fabryka abstrakcyjna

w metodzie wytwórczej klasa tworząca posiada tylko jedną metodę do tworzenia jednego rodzaju obiektów, natomiast w fabryce

abstrakcyjnej mamy wiele metod do tworzenia różnych rodzajów obiektów

nazwa metoda wytwórcza wskazuje na metodę, a fabryka na obiekt fabrykę abstrakcyjną można zaimplementować za pomocą metody wytwórczej

fabryki konkretne są często singletonami

(21)

Budowniczy (ang. builder )

oddziela tworzenie złożonego obiektu od jego reprezentacji, dzięki

temu ten sam proces konstrukcji może prowadzić do powstawania

różnych reprezentacji.

(22)

Schemat budowniczego

Director builder : Builder construct()

Builder buildPart()

ConcreteBuilder buildPart() getResult() : Product

Product

<< create >>

this.builder.buildPart();

Rysunek 5: Źródło: wikipedia

(23)

Budowniczy

/** "Produkt" */

class Pizza {

String dough = "";

private String sauce = "";

private String topping = "";

public void setDough(String dough) { this.dough = dough; }

public void setSauce(String sauce) { this.sauce = sauce; }

public void setTopping(String topping) { this.topping = topping; }

}

(24)

Budowniczy

/** "Abstrakcyjny budowniczy" */

abstract class PizzaBuilder { protected Pizza pizza;

public Pizza getPizza() { return pizza; }

public void createNewPizzaProduct() { pizza = new Pizza(); }

public abstract void buildDough();

public abstract void buildSauce();

public abstract void buildTopping();

}

(25)

Budowniczy

/** "Konkretny budowniczy" */

class HawaiianPizzaBuilder extends PizzaBuilder {

public void buildDough() { pizza.setDough("cross"); } public void buildSauce() { pizza.setSauce("mild"); } public void buildTopping() {

pizza.setTopping("ham+pineapple"); } }

/** "Konkretny budowniczy" */

class SpicyPizzaBuilder extends PizzaBuilder { public void buildDough() { pizza.setDough("pan

baked"); }

public void buildSauce() { pizza.setSauce("hot"); } public void buildTopping() {

pizza.setTopping("pepperoni+salami"); }

}

(26)

Budowniczy

/** "Nadzorca" */

class Waiter {

private PizzaBuilder pizzaBuilder;

public void setPizzaBuilder(PizzaBuilder pb) { pizzaBuilder = pb; }

public Pizza getPizza() { return pizzaBuilder.getPizza(); } public void constructPizza() {

pizzaBuilder.createNewPizzaProduct();

pizzaBuilder.buildDough();

pizzaBuilder.buildSauce();

pizzaBuilder.buildTopping();

(27)

Budowniczy

/** Klient zamawiający pizzę. */

class BuilderExample {

public static void main(String[] args) { Waiter waiter = new Waiter();

PizzaBuilder hawaiian_pizzabuilder = new HawaiianPizzaBuilder();

PizzaBuilder spicy_pizzabuilder = new SpicyPizzaBuilder();

waiter.setPizzaBuilder( hawaiian_pizzabuilder );

waiter.constructPizza();

Pizza pizza = waiter.getPizza();

}

}

(28)

Budowniczy

można użyć wzorca Singleton do implementacji tego wzorca

(29)

Budowniczy vs fabryka abstrakcyjna

budowniczy rozdziela tworzenie obiektu na części, fabryka abstrakcyjna skupia się na tworzeniu rodziny obiektów

we wzorcu budowniczy nadzorca najpierw konstruuje wszystkie części,

i później zwraca produkt, fabryka abstrakcyjna nie tworzy części, tylko

bezpośrednio produkt.

(30)

Prototyp (ang. prototype)

określa na podstawie prototypowego egzemplarza rodzaje tworzonych

obiektów i generuje nowe obiekty przez kopiowanie tego prototypu

(31)

Schemat prototypu

operation() Client

clone()

ConcretePrototype1

clone()

ConcretePrototype2 clone()

«interface»

Prototype

return copy of self return copy of self

Object p = prototype.clone();

«import»

I

Rysunek 6: Źródło: wikipedia

(32)

Prototyp

/** Prototype Class **/

public class Cookie implements Cloneable { public Object clone() {

try {

Cookie copy = (Cookie)super.clone();

//In an actual implementation of this pattern you might now change references to

//the expensive to produce parts from the copies that are held inside the prototype.

return copy;

}

catch(CloneNotSupportedException e) { e.printStackTrace();

return null;

(33)

Prototyp

/** Concrete Prototypes to clone **/

public class CoconutCookie extends Cookie { } /** Client Class**/

public class CookieMachine {

private Cookie cookie;//could have been a private Cloneable cookie;

public CookieMachine(Cookie cookie) { this.cookie = cookie;}

public Cookie makeCookie() { return (Cookie)cookie.clone();}

public static void main(String args[]) { Cookie tempCookie = null;

Cookie prot = new CoconutCookie();

CookieMachine cm = new CookieMachine(prot);

for (int i=0; i<100; i++)

tempCookie = cm.makeCookie();

(34)

Prototyp

można użyć wzorca Singleton do implementacji tego wzorca

można używać wspólnie ten wzorzec z Fabryką abstrakcyjną, fabryka

abstrakcyjna przechowuje zestaw prototypów

(35)

Prototyp vs metoda wytwórcza

prototyp nie tworzy od nowa obiektów, tylko kopiuje siebie, nie ma produktu

prototyp vs fabryka abstrakcyjna: fabryka abstrakcyjna może być

zaimplementowana za pomocą prototypu

(36)

Wzorce strukturalne

(37)

Adapter (ang. adapter )

Przekształca interfejs klasy na inny, oczekiwany przez klienta. Adapter umożliwia współdziałanie klasom, które z uwagi na niezgodne

interfejsy standardowo nie mogą współpracować ze sobą.

przykłady z jdk: java.util.Arrays#asList(),

java.io.InputStreamReader(InputStream) (zwraca Reader),

java.io.OutputStreamWriter(OutputStream) (zwraca Writer)

adapter obiektowy: adapter zawiera instancję klasy, którą adaptuje

adapter klasowy: wielokrotne dziedziczenie

(38)

Schemat adaptera klasowego

specificRequest()

Client Target

+request()

Adapter

+request()

Adaptee

+specificRequest()

Wzorce strukturalne 38 / 103

(39)

Schemat adaptera obiektowego

adaptee.specificRequest()

Client Target

+request()

Adapter

+request()

Adaptee

+specificRequest()

Wzorce strukturalne 39 / 103

(40)

Adapter

class LegacyLine {

public void draw(int x1, int y1, int x2, int y2) { System.out.println("line from (" + x1 + ’,’ + y1 + ") to (" + x2 + ’,’ + y2 + ’)’);

}}

class LegacyRectangle {

public void draw(int x, int y, int w, int h) {

System.out.println("rectangle at (" + x + ’,’ + y + ") with width " + w + " and height " + h);

} }

interface Shape

(41)

Adapter

class Line implements Shape {

private LegacyLine adaptee = new LegacyLine();

public void draw(int x1, int y1, int x2, int y2) { adaptee.draw(x1, y1, x2, y2);

} }

class Rectangle implements Shape {

private LegacyRectangle adaptee = new LegacyRectangle();

public void draw(int x1, int y1, int x2, int y2) { adaptee.draw( Math.min(x1, x2), Math.min(y1, y2),

Math.abs(x2 - x1), Math.abs(y2 - y1) );

}

}

(42)

Adapter

public class AdapterDemo {

public static void main(String[] args) {

Shape[] shapes = { new Line(), new Rectangle() };

// A begin and end point from a graphical editor int x1 = 10, y1 = 20;

int x2 = 30, y2 = 60;

for (Shape shape : shapes) shape.draw(x1, y1, x2, y2);

}

}

(43)

Most (ang. bridge)

oddziela abstrakcję od jej implementacji, dzięki czemu można

modyfikować te dwa elementy niezależnie od siebie

(44)

Schemat mostu

imp->operationImp();

Client Abstraction +operation()

RefinedAbstraction

Implementor +operationImp()

ConcreteImplementorA +operationImp()

ConcreteImplementorB +operationImp()

Wzorce strukturalne 44 / 103

(45)

Most

/** "Implementor" */

interface DrawingAPI {

public void drawCircle(double x, double y, double radius);}

/** "ConcreteImplementor" 1/2 */

class DrawingAPI1 implements DrawingAPI {

public void drawCircle(double x, double y, double radius) {

System.out.printf("API1.circle at %f:%f radius %f\n", x, y, radius);

}}

/** "ConcreteImplementor" 2/2 */

class DrawingAPI2 implements DrawingAPI {

public void drawCircle(double x, double y, double radius) {

System.out.printf("API2.circle at %f:%f radius %f\n", x, y, radius);

Wzorce strukturalne 45 / 103

(46)

Most

/** "Abstraction" */

abstract class Shape {

protected DrawingAPI drawingAPI;

protected Shape(DrawingAPI drawingAPI){

this.drawingAPI = drawingAPI;

}

public abstract void draw(); // low-level

public abstract void resizeByPercentage(double pct); //

high-level

}

(47)

Most

/** "Refined Abstraction" */

class CircleShape extends Shape { private double x, y, radius;

public CircleShape(double x, double y, double radius, DrawingAPI drawingAPI) {

super(drawingAPI);

this.x = x; this.y = y; this.radius = radius;

}

// low-level i.e. Implementation specific public void draw() {

drawingAPI.drawCircle(x, y, radius);

}

// high-level i.e. Abstraction specific public void resizeByPercentage(double pct) {

radius *= (1.0 + pct/100.0);

}

(48)

Most

/** "Client" */

class BridgePattern {

public static void main(String[] args) { Shape[] shapes = new Shape[] {

new CircleShape(1, 2, 3, new DrawingAPI1()), new CircleShape(5, 7, 11, new DrawingAPI2()) };

for (Shape shape : shapes) { shape.resizeByPercentage(2.5);

shape.draw();

}

}

}

(49)

Most

do utworzenia konkretnego mostu można użyć wzorca Fabryka abstrakcyjna

most vs adapter: adapter przeznaczony jest do umożliwiania

współdziałania między niepowiązanymi klasami, i stosuje się go już po zaprojektowaniu, a most stosuje się przed rozpoczęciem projektowania w celu umożliwienia niezależnej modyfikacji abstrakcji i

implementacji; mają podobne struktury; mają inne funkcje: most

rozdziela interfejs od implementacji, a adapter służy do zmieniania

interfejsu istniejącego obiektu.

(50)

Kompozyt (ang. composite)

Składa obiekty w struktury drzewiaste odzwierciedlające hierarchię typu część-całość. Wzorzec ten umożliwia klientom traktowanie poszczególnych obiektów i ich złożeń w taki sam sposób.

przykład z jdk: java.awt.Container#add(Component)

(51)

Schemat kompozytu

Component + operation()

Leaf + operation()

Composite + operation() + add() + remove() + getChild()

1 0..*

parent

child

(52)

Kompozyt

/** "Component" */

interface Graphic { //Prints the graphic.

public void print();

}

/** "Composite" */

import java.util.List;

import java.util.ArrayList;

class CompositeGraphic implements Graphic { //Collection of child graphics.

private List<Graphic> childGraphics = new ArrayList<Graphic>();

//Prints the graphic.

public void print() {

for (Graphic graphic : childGraphics) {

(53)

Kompozyt

//Adds the graphic to the composition.

public void add(Graphic graphic) { childGraphics.add(graphic);

}

//Removes the graphic from the composition.

public void remove(Graphic graphic) { childGraphics.remove(graphic);

} }

/** "Leaf" */

class Ellipse implements Graphic { //Prints the graphic.

public void print() {

System.out.println("Ellipse");

}

(54)

Kompozyt

/** Client */

public class Program {

public static void main(String[] args) { //Initialize four ellipses

Ellipse ellipse1 = new Ellipse();

Ellipse ellipse2 = new Ellipse();

Ellipse ellipse3 = new Ellipse();

Ellipse ellipse4 = new Ellipse();

//Initialize three composite graphics

CompositeGraphic graphic = new CompositeGraphic();

CompositeGraphic graphic1 = new CompositeGraphic();

CompositeGraphic graphic2 = new CompositeGraphic();

//Composes the graphics

graphic1.add(ellipse1);

(55)

Kompozyt

graphic2.add(ellipse4);

graphic.add(graphic1);

graphic.add(graphic2);

//Prints the complete graphic (four times the string

"Ellipse").

graphic.print();

}

}

(56)

Kompozyt

można użyć wzorca Budowniczy do tworzenia kompozytów

można użyć wzorca Prototyp do budowy wielu kompozytów

(57)

Dekorator (ang. decorator )

Dynamicznie dołącza dodatkowe obowiązki do obiektu. Wzorzec ten udostępnia alternatywny elastyczny sposób tworzenia podklas o wzbogaconych funkcjach.

Przykłady z jdk wszystkie podklasy java.io.InputStream,

OutputStream, Reader and Writer mają konstruktor, który

przyjmuje instancję tego samego typu. java.util.Collections,

metody checkedXXX(), synchronizedXXX() i unmodifiableXXX().

(58)

Schemat dekoratora

super.operation();

addedBehavior();

Component +operation()

ConcreteComponent +operation()

Decorator +operation()

ConcreteDecoratorA +addedState +operation()

ConcreteDecoratorB +operation() +addedBehavior()

component.operation();

Wzorce strukturalne 58 / 103

(59)

Dekorator

// The Window interface class public interface Window {

public void draw(); // Draws the Window

public String getDescription(); // Returns a description of the Window

}

// Extension of a simple Window without any scrollbars class SimpleWindow implements Window {

public void draw() { // Draw window }

public String getDescription() { return "simple window";

}

(60)

Dekorator

// abstract decorator class - note that it implements Window

abstract class WindowDecorator implements Window { protected Window windowToBeDecorated; // the Window

being decorated

public WindowDecorator (Window windowToBeDecorated) { this.windowToBeDecorated = windowToBeDecorated;

}

public void draw() {

windowToBeDecorated.draw(); //Delegation }

public String getDescription() {

return windowToBeDecorated.getDescription();

(61)

Dekorator

// The first concrete decorator which adds vertical scrollbar functionality

class VerticalScrollBarDecorator extends WindowDecorator { public VerticalScrollBarDecorator (Window

windowToBeDecorated) {

super(windowToBeDecorated);}

@Override

public void draw() { super.draw();

drawVerticalScrollBar();}

private void drawVerticalScrollBar() { // Draw the vertical scrollbar

}

@Override

public String getDescription() {

return super.getDescription() + ", including vertical scrollbars";

Wzorce strukturalne 61 / 103

(62)

Dekorator

// The second concrete decorator which adds horizontal scrollbar functionality

class HorizontalScrollBarDecorator extends WindowDecorator {

public HorizontalScrollBarDecorator (Window windowToBeDecorated) {

super(windowToBeDecorated);}

@Override

public void draw() { super.draw();

drawHorizontalScrollBar();}

private void drawHorizontalScrollBar() { // Draw the horizontal scrollbar}

@Override

public String getDescription() {

return super.getDescription() + ", including

(63)

Dekorator

public class DecoratedWindowTest {

public static void main(String[] args) {

// Create a decorated Window with horizontal and vertical scrollbars

Window decoratedWindow = new HorizontalScrollBarDecorator (

new VerticalScrollBarDecorator (new SimpleWindow()));

// Print the Window’s description

System.out.println(decoratedWindow.getDescription());

}

}

(64)

Dekorator

public abstract class DataVectorDecorator implements DataVector

{

protected DataVector dataVectorToBeDecorated;

public DataVectorDecorator(DataVector dataVector) {

assert dataVector != null;

this.dataVectorToBeDecorated = dataVector;

}

(65)

Dekorator

@SuppressWarnings("unchecked")

@Override

public <T> T getDecorator(Class<T> clazz) {

if (clazz.equals(this.getClass())) {

return (T) this;

} else {

return dataVectorToBeDecorated.getDecorator(clazz);

}

}

(66)

Dekorator

@Override

public boolean isDecorator() {

return true;

}

@Override

public DataVector getDataVectorWithoutDecorator() {

return

dataVectorToBeDecorated.getDataVectorWithoutDecorator();

}

(67)

Dekorator

DataVectorCiDecorator decorator =

dataVector.getDecorator(DataVectorCiDecorator.class);

if (decorator != null) {

decorator.setCi(1.0);

} else {

DataVectorCiDecorator dataVectorCiDecorator = new DataVectorCiDecorator(dataVector, 1.0);

newDataMatrix.getDataVectors().set(i, dataVectorCiDecorator);

}

(68)

Dekorator vs dziedziczenie

W przypadku podklas może powstawać wiele podklas z różnym

zestawem atrybutów. Można byłoby zgrupować wszystkie atrybuty w

jednej klasie i używać ich wybiórczo, ale związane byłoby to z kosztem

pamięciowym nieużywanych atrybutów. Zamiast tworzenia dużej ilości

podklas, można dynamicznie dołączać potrzebną funkcjonalność.

(69)

Dekorator

adapter vs dekorator: dekorator modyfikuje tylko zadania obiektu, a nie jego interfejs, adapter zapewnia obiektowi nowy interfejs

dekorator vs kompozyt: dekorator można traktować jako uproszczony obiekt złożony obejmujący tylko jeden komponent; dekorator dodaje nowe zadania i nie jest przeznaczony do łączenia obiektów

można użyć wzorca Prototyp do budowy wielu dekoratorów

(70)

Fasada (ang. facade)

Udostępnia jednolity interfejs dla zbioru interfejsów z podsystemu.

Fasada określa interfejs wyższego poziomu ułatwiający korzystanie z

podsystemów.

(71)

Schemat fasady

Rysunek 12: Źródło: wikipedia

(72)

Fasada

/* Complex parts */

class CPU {

public void freeze() { ... }

public void jump(long position) { ... } public void execute() { ... }

}

class Memory {

public void load(long position, byte[] data) { ... } }

class HardDrive {

public byte[] read(long lba, int size) { ... }

(73)

Fasada

/* Facade */

class ComputerFacade { private CPU processor;

private Memory ram;

private HardDrive hd;

public ComputerFacade() { this.processor = new CPU();

this.ram = new Memory();

this.hd = new HardDrive();

}

public void start() { processor.freeze();

ram.load(BOOT_ADDRESS, hd.read(BOOT_SECTOR, SECTOR_SIZE));

processor.jump(BOOT_ADDRESS);

processor.execute();

(74)

Fasada

/* Client */

class You {

public static void main(String[] args) {

ComputerFacade computer = new ComputerFacade();

computer.start();

}

}

(75)

Fasada

ze wzorcem Fasada można używać wzorca Fabryka abstrakcyjna do udostępnienia interfejsu do tworzenia obiektów podsystemu

niezależnie od podsystemów

fasadę można zastąpić fabryką abstrakcyjną w celu ukrycia specyficznych klas dla platformy

zwykle potrzebny jest jeden obiekt fasady, dlatego obiekty fasady są

często singletonami

(76)

Pyłek (ang. flyweight)

wykorzystuje współdzielenie do wydajnej obsługi dużej liczby małych

obiektów

(77)

Schemat pyłka

FlyweightFactory +GetFlyweight(key)

Flyweight +Operation(extrinsicState)

ConcreteFlyweight +IntrinsicState

+Operation(extrinsicState)

UnsharedConcreteFlyweight +allState

+Operation(extrinsicState) Client

if (flyweight(key) exists) { return existing flyweight;

} else {

create new flyweight;

add it to pool of flyweights;

return the new flyweight;

}

Wzorce strukturalne 77 / 103

(78)

Pyłek

import java.util.List;

import java.util.Map;

import java.util.Vector;

import java.util.concurrent.ConcurrentHashMap;

// Instances of CoffeeFlavour will be the Flyweights class CoffeeFlavour {

private final String name;

CoffeeFlavour(String newFlavor) { this.name = newFlavor;

}

@Override

public String toString() {

return name;

(79)

Pyłek

// Menu acts as a factory and cache for CoffeeFlavour flyweight objects

class Menu {

private Map<String, CoffeeFlavour> flavours = new ConcurrentHashMap<String, CoffeeFlavour>();

CoffeeFlavour lookup(String flavorName) { if (!flavours.containsKey(flavorName))

flavours.put(flavorName, new CoffeeFlavour(flavorName));

return flavours.get(flavorName);

}

int totalCoffeeFlavoursMade() { return flavours.size();

}

}

(80)

Pyłek

class Order {

private final int tableNumber;

private final CoffeeFlavour flavour;

Order(int tableNumber, CoffeeFlavour flavor) { this.tableNumber = tableNumber;

this.flavour = flavor;

}

void serve() {

System.out.println("Serving " + flavour + " to table "

+ tableNumber);

}

}

(81)

Pyłek

public class CoffeeShop {

private final List<Order> orders = new Vector<Order>();

private final Menu menu = new Menu();

void takeOrder(String flavourName, int table) { CoffeeFlavour flavour = menu.lookup(flavourName);

Order order = new Order(table, flavour);

orders.add(order);

}

void service() {

for (Order order : orders) order.serve();

}

String report() {

return "\ntotal CoffeeFlavour objects made: "

+ menu.totalCoffeeFlavoursMade();

}

(82)

Pyłek

public static void main(String[] args) { CoffeeShop shop = new CoffeeShop();

shop.takeOrder("Cappuccino", 2);

shop.takeOrder("Frappe", 1);

shop.takeOrder("Espresso", 1);

shop.takeOrder("Frappe", 897);

shop.takeOrder("Cappuccino", 97);

shop.takeOrder("Frappe", 3);

shop.takeOrder("Espresso", 3);

shop.takeOrder("Cappuccino", 3);

shop.takeOrder("Espresso", 96);

shop.takeOrder("Frappe", 552);

shop.takeOrder("Cappuccino", 121);

shop.takeOrder("Espresso", 121);

shop.service();

(83)

Pyłek

zamiast tworzyć do każdego zamówienia obiekty CoffeeFlavour,

tworzymy tylko określoną liczbę obiektów CofeeFlavour

(84)

Pyłek

pyłek można stosować ze wzorcem Kompozyt do zaimplementowania

logicznie hierarchicznej struktury za pomocą acyklicznego grafu

skierowanego ze współużytkowanymi węzłami-liśćmi

(85)

Pełnomocnik (ang. proxy )

udostępnia zastępnik lub reprezentanta innego obiektu w celu

kontrolowania dostępu do niego

(86)

Schemat pełnomocnika

Proxy

request()

RealSubject

request()

Subject

request()

realSubject.request();

realSubject

Wzorce strukturalne 86 / 103

(87)

Pełnomocnik

interface Image {

public void displayImage();

}

//on System A

class RealImage implements Image { private String filename = null;

/**

* Constructor

* @param filename

*/

public RealImage(final String filename) { this.filename = filename;

loadImageFromDisk();

}

(88)

Pełnomocnik

/**

* Loads the image from the disk

*/

private void loadImageFromDisk() {

System.out.println("Loading " + filename);

} /**

* Displays the image

*/

public void displayImage() {

System.out.println("Displaying " + filename);

}

}

(89)

Pełnomocnik

//on System B

class ProxyImage implements Image { private RealImage image = null;

private String filename = null;

/**

* Constructor

* @param filename

*/

public ProxyImage(final String filename) { this.filename = filename;

}

(90)

Pełnomocnik

/**

* Displays the image

*/

public void displayImage() { if (image == null) {

image = new RealImage(filename);

}

image.displayImage();

}

}

(91)

Pełnomocnik

class ProxyExample { /**

* Test method

*/

public static void main(String[] args) { final Image IMAGE1 = new

ProxyImage("HiRes_10MB_Photo1");

final Image IMAGE2 = new

ProxyImage("HiRes_10MB_Photo2");

IMAGE1.displayImage(); // loading necessary IMAGE1.displayImage(); // loading unnecessary IMAGE2.displayImage(); // loading necessary IMAGE2.displayImage(); // loading unnecessary IMAGE1.displayImage(); // loading unnecessary }

}

(92)

Pełnomocnik

pełnomocnik vs adapter. adapter tworzy nowy interfejs, natomiast w pełnomocniku interfejs jest taki sam. Interfejs pełnomocnika może być podzbiorem interfejsu obiektu, może odrzucić żądanie wykonania operacji.

pełnomocnik vs dekorator. Dekorator dodaje zadania do obiektu,

pełnomocnik kontroluje dostęp do niego. Implementacja może być

podobna.

(93)

Bliźniak (ang. twin)

cel: symulacja wielokrotnego dziedziczenia w językach, które go nie

posiadają

(94)

Schemat wielokrotnego dziedziczenia

(95)

Schemat bliźniaka

(96)

Bliźniak

public class Gameboard extends Canvas { public int width, height;

public GameItem firstItem;

...

}

(97)

Bliźniak

public abstract class GameItem { Gameboard board;

int posX, posY;

GameItem next;

public abstract void draw(); // M1

public abstract void click (MouseEvent e);

public abstract boolean intersects (GameItem other);

public abstract void collideWith (GameItem other);

public void check() { GameItem x;

for (x = board.firstItem; x != null; x = x.next) if (intersects(x)) collideWith(x);

}

(98)

Bliźniak

public static BallItem newBall

(int posX, int posY, int radius) {//method of GameBoard BallItem ballItem = new BallItem(posX, posY, radius);

BallThread ballThread = new BallThread();

ballItem.twin = ballThread;

ballThread.twin = ballItem;

return ballItem;

}

}

(99)

Bliźniak

public class BallItem extends GameItem { BallThread twin;

int radius; int dx, dy;

boolean suspended;

public void draw() { // M1

board.getGraphics().drawOval(posX-radius, posY-radius, 2*radius, 2*radius); }

public void move() { posX += dx; posY += dy; } public void click() {

if (suspended) twin.resume(); else twin.suspend();

suspended = ! suspended;

}

(100)

Bliźniak

public boolean intersects (GameItem other) { if (other instanceof Wall)

return posX - radius <= other.posX

&& other.posX <= posX + radius

|| posY - radius <= other.posY

&& other.posY <= posY + radius;

else return false;

}

public void collideWith (GameItem other) { Wall wall = (Wall) other;

if (wall.isVertical) dx = - dx; else dy = - dy; }}

(101)

Bliźniak

public class BallThread extends Thread { BallItem twin;

public void run() { while (true) {

twin.draw(); /*erase*/ twin.move(); twin.draw();

}

}

}

(102)

Inne wzorce konstrukcyjne

leniwa inicjalizacja, ang. lazy initialization – opóźnienie tworzenia obiektu, obliczania wartości aż do momentu zapotrzebowania na nie multiton – jest to singleton, ale rozszerzony o mapę nazwanych obiektów jako pary klucz-wartość, zapewniona jest jedna instancja na dany klucz

pula obiektów ang. object pool – pula obiektów, które są trzymane w gotowości do użycia

inicjalizacja przy pozyskaniu zasobu, ang. resource acquisition is initialization – technika łączy przejęcie i zwolnienie zasobu z inicjowaniem i usuwaniem zmiennych, czas używania zasobu jest czasem życia obiektu, zasób jest alokowany podczas tworzenia obiektu przez konstruktor, a zwalniany podczas usuwania obiektu w

destruktorze

(103)

Inne wzorce strukturalne

front controller – istnieje jeden, centralny obiekt (np. serwlet), który zarządza wszystkimi żądaniami przychodzącymi od klienta,

przykładowo centralna nawigacja w aplikacjach webowych, zarządzanie sesją, caching, filtracja wejścia, wzorzec MVC marker – informacja w czasie działania programu o obiektach w postaci metadanych. Realizacja: klasa implementuje interfejs marker i metody które wykorzystują obiekty tej klasy testują istnienie tego interfejsu, przykład interfejs Serializable, metoda writeObject() sprawdza czy jest implementowany ten interfejs.

wzorzec modułu – implementacja modułów w języku, który je nie

wspiera, np. wykorzystując wzorzec singleton, pakiety w Javie

Cytaty

Powiązane dokumenty

• Composite – Dekorator może być traktowany jako zubożony Kompozyt (tylko jeden komponent), ale podstawowym celem Dekoratora nie jest agregacja, a dodanie funkcjonalności.

chronione metody D oraz klas pochodnych po D. prywatne

• Napisać program implementujący pojęcie macierzy za pomocą wzorca dekoratora, mostu, kompozytu oraz metody wytwórczej.. Program powinien umożliwiać repre- zentację macierzy

Zaimplementować klasę wizytującą ze zwracaniem napisu zawierącego macierz oraz dodatkowo informację czy jest to macierz kwadratowa czy nie. • Utworzyć interpreter do

[r]

public class FlipDownCommand implements Command { private Light theLight;{. public FlipDownCommand(Light light) { this.theLight

wyróżniamy 3 typy wstrzykiwania zależności: wstrzykiwanie konstruktora – zależności są dostarczane przez konstruktor, wstrzykiwanie settera – klient posiada metodę

Wzorzec Adapter konwertuje interfejs jednej klasy na interfejs innej klasy.. Używamy tego wzorca, jeśli chcemy, żeby dwie niezwiązane ze sobą klasy współpracowały ze sobą