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
Na powyższym diagramie klas UML klasa , Creator
która wymaga Product
obiektu, nie tworzy Product1
bezpośrednio instancji klasy. Zamiast tego Creator
odnosi się do oddzielnego factoryMethod()
tworzenia obiektu produktu, co czyni Creator
niezależną od tego, która konkretna klasa jest instancja. Podklasy of Creator
mogą przedefiniować klasę do utworzenia. W tym przykładzie Creator1
podklasa implementuje abstrakcję factoryMethod()
, tworząc instancję Product1
klasy.
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
Room
jest klasą bazową produktu końcowego ( MagicRoom
lub OrdinaryRoom
). MazeGame
deklaruje abstrakcyjną metodę fabryki do wytworzenia takiego produktu bazowego. MagicRoom
i OrdinaryRoom
są podklasami produktu bazowego implementującego produkt końcowy. MagicMazeGame
i OrdinaryMazeGame
są podklasami MazeGame
wdraż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 IPerson
oraz dwóch implementacji o nazwie Villager
i CityPerson
. Na podstawie typu przekazanego do Factory
obiektu zwracamy oryginalny konkretny obiekt jako interfejs IPerson
.
Metoda fabryczna to tylko dodatek do Factory
klasy. 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 MakeProduct
w 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 MazeGame
konstruktor jest metodą szablonową, która tworzy pewną wspólną logikę. Odnosi się do makeRoom
metody 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ć makeRoom
metodę:
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
- W ADO.NET , IDbCommand.CreateParameter jest przykładem zastosowania metody fabryki do łączenia równoległego hierarchie klasowe.
- W Qt , QMainWindow::createPopupMenu jest metodą fabryczną zadeklarowaną we frameworku, którą można nadpisać w kodzie aplikacji .
- W Javie kilka fabryk jest używanych w pakiecie javax.xml.parsers . np. javax.xml.parsers.DocumentBuilderFactory lub javax.xml.parsers.SAXParserFactory.
- W interfejsie API HTML5 DOM interfejs Document zawiera metodę fabryki createElement służącą do tworzenia określonych elementów interfejsu HTMLElement.
Zobacz też
- Wzorce projektowe , bardzo wpływowa książka
- Wzorzec projektowy , ogólny przegląd wzorców projektowych
- Abstrakcyjny wzorzec fabryki , wzorzec często realizowany przy użyciu metod fabrycznych
- Wzór budowniczego , kolejny wzór twórczy
- Wzorzec metody szablonu , który może wywoływać metody fabryczne
- Pomysł Joshua Blocha dotyczący statycznej metody fabrycznej , który, jak twierdzi, nie ma bezpośredniego odpowiednika we wzorcach projektowych .
Bibliografia
- Martina Fowlera ; Kent Beck ; Jana Branta ; Williama Opdyke'a ; Don Roberts (czerwiec 1999). Refaktoryzacja: ulepszanie projektu istniejącego kodu . Addisona-Wesleya. Numer ISBN 0-201-48567-2.
- Gamma, Ericha ; Helm, Ryszard ; Johnsona, Ralpha; Vlissides, John (1994). Wzorce projektowe: elementy oprogramowania obiektowego wielokrotnego użytku . Addisona-Wesleya. Numer ISBN 0-201-63361-2.
- Cox, Brad J. (1986). Programowanie obiektowe: podejście ewolucyjne . Addisona-Wesleya. Numer ISBN 978-0-201-10393-9.
- Cohen, Tal; Gil, Józef (2007). „Lepsze budownictwo z fabrykami” (PDF) . Journal of Object Technology . Bertranda Meyera . 6 (6): 103. doi : 10.5381/jot.2007.6.6.a3 . Pobrano 2007-03-12 .
Linki zewnętrzne
- Implementacja wzorca projektowania fabryki w Javie
- Metoda fabryczna w UML i LePUS3 (język opisu projektu)
- Rozważ statyczne metody fabryczne Joshua Bloch