Model de metodă din fabrică - Factory method pattern

În programarea bazată pe clase , modelul metodei din fabrică este un model de creație care utilizează metode din fabrică pentru a rezolva problema creării obiectelor fără a fi nevoie să specificați clasa exactă a obiectului care va fi creat. Acest lucru se realizează prin crearea de obiecte apelând o metodă din fabrică - fie specificată într-o interfață și implementată de clase de copii, fie implementată într-o clasă de bază și opțională suprascrisă de clase derivate - mai degrabă decât apelând un constructor .

Prezentare generală

Modelul de proiectare Factory Method este unul dintre cele douăzeci și trei de cunoscute modele de proiectare „Gang of Four” care descriu modul de rezolvare a problemelor de proiectare recurente pentru a proiecta software flexibil și reutilizabil orientat obiect, adică obiecte care sunt mai ușor de implementat, schimbați, testați și refolosiți.

Modelul de proiectare a Metodei fabricii rezolvă probleme precum:

  • Cum se poate crea un obiect astfel încât subclasele să poată redefini ce clasă să instanțeze?
  • Cum poate o clasă să amâne instanțierea la subclase?

Modelul de proiectare a Metodei din fabrică descrie modul de soluționare a acestor probleme:

  • Definiți o operație separată ( metoda din fabrică ) pentru crearea unui obiect.
  • Creați un obiect apelând o metodă din fabrică .

Aceasta permite scrierea subclaselor pentru a schimba modul în care este creat un obiect (pentru a redefini ce clasă să instanțieze).
Vedeți și diagrama clasei UML de mai jos.

Definiție

"Definiți o interfață pentru crearea unui obiect, dar lăsați subclasele să decidă ce clasă să instanțeze. Metoda Factory permite unei clase să amâne instanțarea pe care o folosește pentru subclase." ( Gang Of Four )

Crearea unui obiect necesită adesea procese complexe care nu sunt adecvate pentru a fi incluse într-un obiect care compune. Crearea obiectului poate duce la o dublare semnificativă a codului, poate necesita informații care nu sunt accesibile obiectului care compune, poate să nu ofere un nivel suficient de abstractizare sau altfel nu poate face parte din preocupările obiectului care compune . Modelul de proiectare a metodei din fabrică tratează aceste probleme prin definirea unei metode separate pentru crearea obiectelor, pe care subclasele le pot suprascrie pentru a specifica tipul de produs derivat care va fi creat.

Modelul metodei din fabrică se bazează pe moștenire, deoarece crearea obiectelor este delegată subclaselor care implementează metoda din fabrică pentru a crea obiecte.

Structura

Diagrama clasei UML

Image
Un eșantion de diagramă de clasă UML pentru modelul de proiectare Factory Method.

În diagrama de clasă UML de mai sus , Creatorclasa care necesită un Productobiect nu instanțiază Product1clasa direct. În schimb, Creatorse referă la un factoryMethod()obiect separat pentru a crea un obiect produs, ceea ce face Creatorindependentă de clasa concretă care este instanțiată. Subclasele de Creatorpot redefini ce clasă să instanțieze. În acest exemplu, Creator1subclasa implementează abstractul factoryMethod()prin instanțierea Product1clasei.

Exemplu

Un joc de labirint poate fi jucat în două moduri, unul cu camere obișnuite care sunt conectate doar cu camere adiacente și unul cu camere magice care permit jucătorilor să fie transportați la întâmplare.

Structura

Nou WikiFactoryMethod.png

Roomeste clasa de bază pentru un produs final ( MagicRoomsau OrdinaryRoom). MazeGamedeclară metoda fabricii abstracte pentru a produce un astfel de produs de bază. MagicRoomși OrdinaryRoomsunt subclasele produsului de bază care implementează produsul final. MagicMazeGameși OrdinaryMazeGamesunt subclasele de MazeGameimplementare a metodei fabricii care produce produsele finale. Astfel, metodele din fabrică separă apelanții ( MazeGame) de implementarea claselor concrete. Acest lucru face ca „noul” Operator să fie redundant, permite respectarea principiului Deschis / închis și face produsul final mai flexibil în caz de schimbare.

Exemple de implementări

C #

// Empty vocabulary of actual object
public interface IPerson
{
    string GetName();
}

public class Villager : IPerson
{
    public string GetName()
    {
        return "Village Person";
    }
}

public class CityPerson : IPerson
{
    public string GetName()
    {
        return "City Person";
    }
}

public enum PersonType
{
    Rural,
    Urban
}

/// <summary>
/// Implementation of Factory - Used to create objects.
/// </summary>
public class Factory
{
    public IPerson GetPerson(PersonType type)
    {
        switch (type)
        {
            case PersonType.Rural:
                return new Villager();
            case PersonType.Urban:
                return new CityPerson();
            default:
                throw new NotSupportedException();
        }
    }
}

În codul de mai sus puteți vedea crearea unei interfețe numite IPersonși a două implementări numite Villagerși CityPerson. Pe baza tipului trecut în Factoryobiect, returnăm obiectul concret original ca interfață IPerson.

O metodă din fabrică este doar un plus la Factoryclasă. Acesta creează obiectul clasei prin interfețe, dar pe de altă parte, permite, de asemenea, subclasei să decidă ce clasă este instanțiată.

public interface IProduct
{
    string GetName();
    bool SetPrice(double price);
}

public class Phone : IProduct
{
    private double _price;

    public string GetName()
    {
        return "Apple TouchPad";
    }

    public bool SetPrice(double price)
    {
        _price = price;
        return true;
    }
}

/* Almost same as Factory, just an additional exposure to do something with the created method */
public abstract class ProductAbstractFactory
{
    protected abstract IProduct MakeProduct();

    public IProduct GetObject() // Implementation of Factory Method.
    {
        return this.MakeProduct();
    }
}

public class PhoneConcreteFactory : ProductAbstractFactory
{
    protected override IProduct MakeProduct()
    {
        IProduct product = new Phone();
        // Do something with the object after you get the object.
        product.SetPrice(20.30);
        return product;
    }
}

Puteți vedea că am folosit-o MakeProductîn concreteFactory. Ca urmare, puteți apela cu ușurință de MakeProduct()la acesta pentru a obține IProduct. S-ar putea să vă scrieți și logica personalizată după ce obțineți obiectul în metoda concretă a fabricii. GetObject este făcut abstract în interfața Factory.

Java

Acest exemplu Java este similar cu cel din cartea Design Patterns .

MazeGame folosește Rooms, dar pune responsabilitatea creării de Rooms în subclasele sale care creează clasele concrete. Modul obișnuit de joc ar putea utiliza această metodă șablon:

public abstract class Room {
    abstract void connect(Room room);
}

public class MagicRoom extends Room {
    public void connect(Room room) {}
}

public class OrdinaryRoom extends Room {
    public void connect(Room room) {}
}

public abstract class MazeGame {
     private final List<Room> rooms = new ArrayList<>();

     public MazeGame() {
          Room room1 = makeRoom();
          Room room2 = makeRoom();
          room1.connect(room2);
          rooms.add(room1);
          rooms.add(room2);
     }

     abstract protected Room makeRoom();
}

În fragmentul de mai sus, MazeGameconstructorul este o metodă șablon care face o logică comună. Se referă la makeRoommetoda fabricii care încapsulează crearea de camere, astfel încât alte camere pot fi utilizate într-o subclasă. Pentru a implementa celălalt mod de joc care are camere magice, este suficient să înlocuiți makeRoommetoda:

public class MagicMazeGame extends MazeGame {
    @Override
    protected Room makeRoom() {
        return new MagicRoom();
    }
}

public class OrdinaryMazeGame extends MazeGame {
    @Override
    protected Room makeRoom() {
        return new OrdinaryRoom();
    }
}

MazeGame ordinaryGame = new OrdinaryMazeGame();
MazeGame magicGame = new MagicMazeGame();

PHP

Urmează un alt exemplu în PHP , de data aceasta folosind implementări de interfață spre deosebire de subclasare (cu toate acestea, același lucru se poate realiza prin subclasare). Este important să rețineți că metoda din fabrică poate fi definită și ca publică și apelată direct de codul clientului (spre deosebire de exemplul Java de mai sus).

/* Factory and car interfaces */

interface CarFactory
{
    public function makeCar(): Car;
}

interface Car
{
    public function getType(): string;
}

/* Concrete implementations of the factory and car */

class SedanFactory implements CarFactory
{
    public function makeCar(): Car
    {
        return new Sedan();
    }
}

class Sedan implements Car
{
    public function getType(): string
    {
        return 'Sedan';
    }
}

/* Client */

$factory = new SedanFactory();
$car = $factory->makeCar();
print $car->getType();

Piton

La fel ca exemplul Java.

from abc import ABC, abstractmethod


class MazeGame(ABC):
    def __init__(self) -> None:
        self.rooms = []
        self._prepare_rooms()

    def _prepare_rooms(self) -> None:
        room1 = self.make_room()
        room2 = self.make_room()

        room1.connect(room2)
        self.rooms.append(room1)
        self.rooms.append(room2)

    def play(self) -> None:
        print('Playing using "{}"'.format(self.rooms[0]))

    @abstractmethod
    def make_room(self):
        raise NotImplementedError("You should implement this!")


class MagicMazeGame(MazeGame):
    def make_room(self):
        return MagicRoom()


class OrdinaryMazeGame(MazeGame):
    def make_room(self):
        return OrdinaryRoom()


class Room(ABC):
    def __init__(self) -> None:
        self.connected_rooms = []

    def connect(self, room) -> None:
        self.connected_rooms.append(room)


class MagicRoom(Room):
    def __str__(self):
        return "Magic room"


class OrdinaryRoom(Room):
    def __str__(self):
        return "Ordinary room"


ordinaryGame = OrdinaryMazeGame()
ordinaryGame.play()

magicGame = MagicMazeGame()
magicGame.play()

Utilizări

  • În ADO.NET , IDbCommand.CreateParameter este un exemplu de utilizare a metodei din fabrică pentru a conecta ierarhii de clase paralele.
  • În Qt , QMainWindow :: createPopupMenu este o metodă din fabrică declarată într-un cadru care poate fi suprascris în codul aplicației .
  • În Java , mai multe fabrici sunt utilizate în pachetul javax.xml.parsers . de exemplu, javax.xml.parsers.DocumentBuilderFactory sau javax.xml.parsers.SAXParserFactory.
  • În API-ul HTML5 DOM , interfața Document conține o metodă de fabrică createElement pentru crearea unor elemente specifice ale interfeței HTMLElement.

Vezi si

Referințe

linkuri externe