Fabrikkmetodemønster - Factory method pattern
I klassebasert programmering er fabrikkmetodemønsteret et skapningsmønster som bruker fabrikkmetoder for å håndtere problemet med å lage objekter uten å måtte spesifisere den eksakte klassen til objektet som skal opprettes. Dette gjøres ved å lage objekter ved å kalle en fabrikkmetode - enten spesifisert i et grensesnitt og implementert av underordnede klasser, eller implementert i en basisklasse og eventuelt overstyrt av avledede klasser - i stedet for å ringe en konstruktør .
Oversikt
Designmetoden Factory Method er et av de tjuetre velkjente "Gang of Four" designmønstrene som beskriver hvordan man løser tilbakevendende designproblemer for å designe fleksibel og gjenbrukbar objektorientert programvare, det vil si objekter som er lettere å implementere, endre, teste og gjenbruke.
Fabrikkmetodens designmønster løser problemer som:
- Hvordan kan et objekt opprettes slik at underklasser kan omdefinere hvilken klasse som skal instantieres?
- Hvordan kan en klasse utsette instantiering til underklasser?
Fabrikkmetodens designmønster beskriver hvordan du løser slike problemer:
- Definer en egen operasjon ( fabrikkmetode ) for å lage et objekt.
- Lag et objekt ved å kalle en fabrikkmetode .
Dette gjør at skriving av underklasser kan endre måten et objekt blir opprettet på (for å omdefinere hvilken klasse som skal instantieres).
Se også UML -klassediagrammet nedenfor.
Definisjon
"Definer et grensesnitt for å lage et objekt, men la underklasser bestemme hvilken klasse som skal instantieres. Fabrikkmetoden lar en klasse utsette instantiering den bruker til underklasser." ( Gang Of Four )
Å lage et objekt krever ofte komplekse prosesser som ikke er hensiktsmessige å inkludere i et komponerende objekt. Objektets opprettelse kan føre til en betydelig duplisering av kode, kan kreve informasjon som ikke er tilgjengelig for komponentobjektet, kanskje ikke gi et tilstrekkelig abstraksjonsnivå, eller ellers ikke være en del av komponentobjektets bekymringer . Fabriksmetodedesignmønsteret håndterer disse problemene ved å definere en egen metode for å lage objektene, som underklasser deretter kan overstyre for å spesifisere den avledede typen produkt som skal opprettes.
Fabrikkmetodemønsteret er avhengig av arv, ettersom objektopprettelse er delegert til underklasser som implementerer fabrikkmetoden for å lage objekter.
Struktur
UML klassediagram
I det ovennevnte UML -klassediagrammet vil Creatorklassen som krever et Productobjekt ikke instantiere Product1klassen direkte. I stedet Creatorrefererer referansen til et separat for factoryMethod()å lage et produktobjekt, som gjør det Creatoruavhengige av hvilken betongklasse som blir instantiert. Underklasser av Creatorkan omdefinere hvilken klasse som skal instantieres. I dette eksemplet Creator1implementerer underklassen det abstrakte factoryMethod()ved å instantere Product1klassen.
Eksempel
Et labyrintspill kan spilles i to moduser, ett med vanlige rom som bare er forbundet med tilstøtende rom, og ett med magiske rom som lar spillere transporteres tilfeldig.
Struktur
Roomer grunnklassen for et sluttprodukt ( MagicRoomeller OrdinaryRoom). MazeGameerklærer den abstrakte fabrikkmetoden for å produsere et slikt basisprodukt. MagicRoomog OrdinaryRoomer underklasser av basisproduktet som implementerer sluttproduktet. MagicMazeGameog OrdinaryMazeGameer underklasser for MazeGameimplementering av fabrikkmetoden som produserer de endelige produktene. Dermed frakobler fabrikkmetoder innringere ( MazeGame) fra implementeringen av betongklassene. Dette gjør den "nye" operatøren overflødig, tillater overholdelse av Open/closed -prinsippet og gjør sluttproduktet mer fleksibelt i tilfelle endringer.
Eksempelimplementeringer
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();
}
}
}
I koden ovenfor kan du se opprettelsen av ett kalt grensesnitt IPersonog to implementeringer kalt Villagerog CityPerson. Basert på typen som sendes inn i Factoryobjektet, returnerer vi det originale betongobjektet som grensesnitt IPerson.
En fabrikkmetode er bare et tillegg til Factoryklassen. Det skaper objektet for klassen gjennom grensesnitt, men på den annen side lar den også underklassen bestemme hvilken klasse som blir instantiert.
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;
}
}
Du kan se at vi har brukt MakeProducti concreteFactory. Som et resultat kan du enkelt ringe MakeProduct()fra den for å få IProduct. Du kan også skrive din tilpassede logikk etter å ha fått objektet i betongfabrikkmetoden. GetObject er gjort abstrakt i fabrikkgrensesnittet.
Java
Dette Java -eksemplet ligner det i boken Design Patterns .
MazeGame bruker rom, men det legger ansvaret for å lage rom til sine underklasser som lager de konkrete klassene. Den vanlige spillmodusen kan bruke denne malmetoden:
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();
}
I utdraget ovenfor er MazeGamekonstruktøren en malmetode som lager noen vanlig logikk. Det refererer til makeRoomfabrikkmetoden som omslutter opprettelsen av rom slik at andre rom kan brukes i en underklasse. For å implementere den andre spillmodusen som har magiske rom, er det nok å overstyre makeRoommetoden:
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
Et annet eksempel i PHP følger, denne gangen ved å bruke grensesnittimplementeringer i motsetning til underklassering (det samme kan imidlertid oppnås gjennom subklassering). Det er viktig å merke seg at fabrikkmetoden også kan defineres som offentlig og kalles direkte av klientkoden (i motsetning til Java -eksemplet ovenfor).
/* 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();
Python
Samme som Java -eksempel.
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()
Bruker
- I ADO.NET er IDbCommand.CreateParameter et eksempel på bruk av fabrikkmetoden for å koble parallelle klassehierarkier.
- I Qt er QMainWindow :: createPopupMenu en fabrikkmetode deklarert i et rammeverk som kan overstyres i applikasjonskoden .
- I Java brukes flere fabrikker i pakken javax.xml.parsers . f.eks. javax.xml.parsers.DocumentBuilderFactory eller javax.xml.parsers.SAXParserFactory.
- I HTML5 DOM API inneholder dokumentgrensesnittet en createElement -fabrikkmetode for å lage spesifikke elementer i HTMLElement -grensesnittet.
Se også
- Design Patterns , den svært innflytelsesrike boken
- Designmønster , oversikt over designmønstre generelt
- Abstrakt fabrikkmønster , et mønster som ofte implementeres ved bruk av fabrikkmetoder
- Byggemønster , et annet skapningsmønster
- Malmetodemønster , som kan kalle fabrikkmetoder
- Joshua Blochs idé om en statisk fabrikkmetode , som han sier ikke har noen direkte ekvivalent i Design Patterns .
Referanser
- Martin Fowler ; Kent Beck ; John Brant ; William Opdyke ; Don Roberts (juni 1999). Refactoring: Forbedring av utformingen av eksisterende kode . Addison-Wesley. ISBN 0-201-48567-2.
- Gamma, Erich ; Helm, Richard ; Johnson, Ralph; Vlissides, John (1994). Designmønstre: Elementer av gjenbrukbar objektorientert programvare . Addison-Wesley. ISBN 0-201-63361-2.
- Cox, Brad J. (1986). Objektorientert programmering: en evolusjonær tilnærming . Addison-Wesley. ISBN 978-0-201-10393-9.
- Cohen, Tal; Gil, Joseph (2007). "Bedre konstruksjon med fabrikker" (PDF) . Journal of Object Technology . Bertrand Meyer . 6 (6): 103. doi : 10.5381/jot.2007.6.6.a3 . Hentet 2007-03-12 .
Eksterne linker
- Implementering av fabrikkdesignmønster i Java
- Fabrikkmetode i UML og i LePUS3 (et designbeskrivelsesspråk )
- Vurder statiske fabrikkmetoder av Joshua Bloch
