Il pattern ABSTRACT FACTORY
Abstract Factory L intento è quello di fornire una interfaccia per creare famiglie di oggetti in relazione o dipendenti tra loro, senza dovere specificare le loro classi concrete. La differenza col pattern Factory Method è che mentre in questo la costruzione degli oggetti è delegata ad una sottoclasse, nel pattern abstract factory si usa il meccanismo della composizione. In realtà gli oggetti delegati utilizzano a loro volta factory method per effettuare la costruzione 2
Il problema affrontato Il pattern Abstract Factory è adeguato a risolvere queste tipologie di problemi: Un sistema dovrebbe essere indipende da come sono create le sue strutture, da come sono composte e rappresentate Una classe non può anticipare la classe di oggetti che va a creare Un sistema deve usare uno solo insieme di famiglie di prodotti Un famiglia di prodotti collegati tra loro è progettata per funzionare assieme, e bisogna rispettare questo vincolo Si vuole fornire una libreria di oggetti nascondendo le implementazioni 3
Struttura 4
Partecipanti AbstractFactory dichiara una interfaccia per le operazioni che crea oggetti product astratti ConcreteFactory implementa le operazioni per creare oggetti concreti product di Abstract Product AbstractProduct dichiara l intefaccia per il tipo di oggetto Product ConcreteProduct definisce un oggetto Product creato dalla factory corrispondente Factory concreta Concrete factory: implementa la interfaccia AbstractProduct Client Usa solo le interfacce AbstractFactory e AbstractProduct in luogo degli oggetti concreti 5
COLLABORAZIONI Normalmente viene creata una singola istanza di ConcreteFactory (Singleton Pattern), che crea oggetti Product aventi una particolare implementazione Per creare differenti oggetti Product, I client dovrebbero usare differenti factory concrete AbstractFactory delega la creazione degli oggetti Product alle sue ConcreteFactory 6
Abstract Factory Esempio Maze Riprendiamo l esempio del costruttore di labirinti (la classe Factory Maze Default), : public class MazeFactory { public Maze makemaze() {return new Maze(); public Room makeroom(int n) {return new Room(n); public Wall makewall() {return new Wall(); public Door makedoor(room r1, Room r2) { return new Door(r1, r2); In realtà la Factory ha un insieme di Factory Method (per ciascun elemento del labirinto) 7
Una scelta naturale è quello di creare un metodo createmaze() cui passare la factory concreta come parametro, per cambiare agevolmente la factory e delegare a questa la costruzione concreta del labirinto 8
public class MazeGame { public Maze createmaze(mazefactory factory) { Maze maze = factory.makemaze(); Room r1 = factory.makeroom(1); Room r2 = factory.makeroom(2); Door door = factory.makedoor(r1, r2); maze.addroom(r1); maze.addroom(r2); r1.setside(mazegame.north, factory.makewall()); r1.setside(mazegame.east, door); r1.setside(mazegame.south, factory.makewall()); r1.setside(mazegame.west, factory.makewall()); r2.setside(mazegame.north, factory.makewall()); r2.setside(mazegame.east, factory.makewall()); r2.setside(mazegame.south, factory.makewall()); r2. setside(mazegame.west, door); return maze; 9
public class EnchantedMazeFactory extends MazeFactory { public Room makeroom(int n) {return new EnchantedRoom(n); public Wall makewall() {return new EnchantedWall(); public Door makedoor(room r1, Room r2) {return new EnchantedDoor(r1, r2); Partecipanti AbstractFactory => MazeFactory ConcreteFactory => EnchantedMazeFactory (MazeFactory) AbstractProduct => MapSite ConcreteProduct => Wall, Room, Door, EnchantedWall, EnchantedRoom, EnchantedDoor Client => MazeGame 10
Conseguenze/Benefici/dettagli di implementazioni Conseguencze Benefici Isola i clienti dall implementazione delle classi concrete Rende possibile interscambiare facilmente le famiglie di prodotti, perché ogni factory concreta genera una famiglia di prodotti Rafforza il raggruppamento di prodotti in famiglie Svantaggi Supportare nuovi tipi di prodotti richiede cambiare l interfaccia AbstractFactory Dettagli di implementazione Tipicamente è richiesta una singola istanza di una particolare factory concreta, quindi è richiesto il pattern singleton 11
Riepilogo Factory: come vengono creati i prodotti? Metodo 1: Factory Methods public abstract class Factory { public abstract Product1 createproduct1(); public abstract Product2 createproduct2(); //FACTORY CONCRETA (estende sotto classe) public class ConcreteFactory extends Factory { public Product1 createproduct1() {return new concreteproduct1 (); public Product2 createproduct2() {return new concreteproduct2 (); 12
Esempio di Abstract Factory: Socket Le Socket sono l'astrazione fondamentela per la comunicazione in rete Java forinsce una implementazione delle Socket proprio basata sul pattern ABSTRACT FACTORY, mediante le classi Socket e ServerSocket nel package java.net La classe Socket delega l'implementazione ad una sua classe SockeImpl, creata da un classe SocketImplFactory sempre contenuta in Socket 13
public class Socket { // Implementazione: SocketImpl impl; // La factory: private static SocketImplFactory factory; // impostazione della factory public static synchronized void setsocketimplfactory(socketimplfactory fac) throws IOException { if (factory!= null) { throw new SocketException("factory already defined"); factory = fac; //== la socket concreta è prodotta dalla factory protected Socket() { impl = (factory!= null)? factory.createsocketimpl() : new PlainSocketImpl(); //==... 14
Utilizzo della factory da parte del client // Crea la factory concreta ConcreteFactory cf = new ConcreteFactory(); // quindi i prodotti Product1 p1 = cf.createproduct1(); Product2 p2 = cf.createproduct2(); Il client può ovviamente selezionare la factory da utilizzare. Sia che si usi il Factory Method, sia che si usi l'abstract Factory il codice client non cambia 15
Metodo 2 STILE ABSTRACT FACTORY public abstract class AbstractFactory { protected FactoryProduct1 factory1; protected FactoryProduct2 factory2; public Product1 createproduct1 { return factory1.createproduct1(); public Product2 createproduct2 { return factory2.createproduct2(); public class Factory extends AbstractFactory { public Factory() { FactoryProduct1 = new Factory_Product1(); FactoryProduct2 = new Factory_Product2(); 16
Metodo 3: Usa Factory senza sotto classi (composizione) public class Factory { private FactoryProduct1 factory1; private FactoryProduct2 factory2; public void setfactory1(factoryproduct1 f1) { factory1 =f1; public void setfactory2(factoryproduct2 f2) { factory2 = f2; public Product1 createproduct1() {return factory1.createproduct1(); public Product2 createproduct2() {return factory2.createproduct2(); 17
Esempio 18