Wzorzec metody fabrycznej - Factory method pattern

W programowaniu klasy oparte The pattern sposób fabryka jest kreacyjnych wzór że metody zastosowania fabrycznych do czynienia z problemem tworzenia obiektów bez konieczności określić dokładną klasę obiektu, który zostanie utworzony. Odbywa się to przez tworzenie obiektów przez wywołanie metody fabrycznej — określonej w interfejsie i zaimplementowanej przez klasy podrzędne lub zaimplementowanej w klasie bazowej i opcjonalnie zastępowanej przez klasy pochodne — zamiast przez wywołanie konstruktora .

Przegląd

Wzorzec projektowy Factory Method jest jednym z dwudziestu trzech dobrze znanych wzorców projektowych „Gang of Four”, które opisują sposób rozwiązywania powtarzających się problemów projektowych w celu projektowania elastycznego i wielokrotnego użytku oprogramowania zorientowanego obiektowo, czyli obiektów, które są łatwiejsze do wdrożenia, zmieniać, testować i ponownie wykorzystywać.

Wzorzec projektowy Factory Method rozwiązuje takie problemy jak:

  • W jaki sposób można utworzyć obiekt, aby podklasy mogły przedefiniować klasę do utworzenia instancji?
  • Jak klasa może odroczyć tworzenie instancji do podklas?

Wzorzec projektowy Factory Method opisuje sposób rozwiązywania takich problemów:

  • Zdefiniuj oddzielną operację ( metoda fabryczna ) do tworzenia obiektu.
  • Utwórz obiekt, wywołując metodę fabryki .

Umożliwia to pisanie podklas zmieniających sposób tworzenia obiektu (w celu przedefiniowania klasy do utworzenia instancji).
Zobacz także diagram klas UML poniżej.

Definicja

„Zdefiniuj interfejs do tworzenia obiektu, ale pozwól podklasom decydować, którą klasę utworzyć. Metoda Factory pozwala klasie odroczyć instancję, której używa, do podklas”. ( Gang Czterech )

Tworzenie obiektu często wymaga złożonych procesów, których nie można uwzględnić w komponowanym obiekcie. Stworzenie obiektu może prowadzić do znacznego powielania kodu, może wymagać informacji nie dostępnych dla obiektu komponowania może nie zapewniać wystarczającego poziomu abstrakcji, albo może inaczej nie być częścią komponowania obiektu obaw . Wzorzec projektowy metody fabryki obsługuje te problemy, definiując osobną metodę tworzenia obiektów, którą podklasy można następnie zastąpić w celu określenia typu pochodnego produktu, który zostanie utworzony.

Wzorzec metody fabryki opiera się na dziedziczeniu, ponieważ tworzenie obiektów jest delegowane do podklas, które implementują metodę fabryki do tworzenia obiektów.

Struktura

Diagram klas UML

Przykładowy diagram klas UML dla wzorca projektowego Factory Method.

Na powyższym diagramie klas UML klasa , Creatorktóra wymaga Productobiektu, nie tworzy Product1bezpośrednio instancji klasy. Zamiast tego Creatorodnosi się do oddzielnego factoryMethod()tworzenia obiektu produktu, co czyni Creatorniezależną od tego, która konkretna klasa jest instancja. Podklasy of Creatormogą przedefiniować klasę do utworzenia. W tym przykładzie Creator1podklasa implementuje abstrakcję factoryMethod(), tworząc instancję Product1klasy.

Przykład

Gra w labirynt może być rozgrywana w dwóch trybach, jeden ze zwykłymi pokojami, które są połączone tylko z sąsiednimi pokojami, a drugi z magicznymi pokojami, które umożliwiają losowy transport graczy.

Struktura

Nowa WikiFactoryMethod.png

Roomjest klasą bazową produktu końcowego ( MagicRoomlub OrdinaryRoom). MazeGamedeklaruje abstrakcyjną metodę fabryki do wytworzenia takiego produktu bazowego. MagicRoomi OrdinaryRoomsą podklasami produktu bazowego implementującego produkt końcowy. MagicMazeGamei OrdinaryMazeGamesą podklasami MazeGamewdrażania metody fabrycznej wytwarzającej produkty końcowe. W ten sposób metody fabryczne oddzielają wywołujące ( MazeGame) od implementacji konkretnych klas. To sprawia, że ​​„nowy” Operator jest zbędny, pozwala na przestrzeganie zasady otwarte/zamknięte i sprawia, że ​​produkt końcowy jest bardziej elastyczny w przypadku zmiany.

Przykładowe realizacje

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

W powyższym kodzie widać tworzenie jednego interfejsu o nazwie IPersonoraz dwóch implementacji o nazwie Villageri CityPerson. Na podstawie typu przekazanego do Factoryobiektu zwracamy oryginalny konkretny obiekt jako interfejs IPerson.

Metoda fabryczna to tylko dodatek do Factoryklasy. Tworzy obiekt klasy za pośrednictwem interfejsów, ale z drugiej strony pozwala również podklasie decydować, która klasa zostanie utworzona.

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

Widać, że zastosowaliśmy MakeProductw ConcreteFactory. W rezultacie możesz MakeProduct()z niego łatwo zadzwonić , aby uzyskać IProduct. Możesz również napisać własną logikę po pobraniu obiektu w konkretnej metodzie Factory. GetObject jest abstrakcyjny w interfejsie Factory.

Jawa

Ten przykład Javy jest podobny do tego z książki Wzorce projektowe .

MazeGame używa Rooms, ale odpowiedzialność za tworzenie pokoi nakłada na swoje podklasy, które tworzą konkretne klasy. W zwykłym trybie gry można wykorzystać tę metodę szablonu:

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

W powyższym fragmencie MazeGamekonstruktor jest metodą szablonową, która tworzy pewną wspólną logikę. Odnosi się do makeRoommetody fabrycznej, która hermetyzuje tworzenie pomieszczeń w taki sposób, że inne pomieszczenia mogą być używane w podklasie. Aby zaimplementować inny tryb gry z magicznymi pokojami, wystarczy zastąpić makeRoommetodę:

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

Kolejny przykład w PHP jest następujący, tym razem używając implementacji interfejsów w przeciwieństwie do podklas (jednak to samo można osiągnąć poprzez podklasowanie). Należy zauważyć, że metoda fabryki może być również zdefiniowana jako publiczna i wywoływana bezpośrednio przez kod klienta (w przeciwieństwie do powyższego przykładu Java).

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

Pyton

Tak samo jak w przykładzie 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()

Zastosowania

Zobacz też

Bibliografia

Linki zewnętrzne