抽象工廠
抽象工廠模式提供了一種封裝一組具有共同主題的單個工廠的方法,而無需指定它們的具體類。在正常使用中,客戶端軟體建立一個抽象工廠的具體實現,然後使用工廠的通用介面來建立屬於該主題的具體物件。客戶端不知道(也不關心)它從每個這些內部工廠獲得哪些具體物件,因為它只使用它們的產品的通用介面。這種模式將一組物件的實現細節與它們的通用使用分開,並依賴於物件組合,因為物件建立是在工廠介面中公開的方法中實現的。一個例子是抽象工廠類DocumentCreator,它提供建立多個產品的介面(例如createLetter()和createResume())。該系統將擁有任意數量的DocumentCreator類的派生具體版本,例如FancyDocumentCreator或ModernDocumentCreator,每個版本都有createLetter()和createResume()的不同實現,它們將建立一個對應的物件,例如FancyLetter或ModernResume。這些產品中的每一個都派生自一個簡單的抽象類,例如Letter或Resume,客戶端知道這些類。客戶端程式碼將獲取DocumentCreator的適當例項,並呼叫其工廠方法。每個生成的都是從同一個DocumentCreator實現建立的,並且將共享一個共同的主題(它們將都是花哨的或現代的物件)。客戶端只需要知道如何處理抽象的Letter或Resume類,而不需要知道它從具體工廠獲得的特定版本。
工廠是程式碼中構建物件的具體類的所在位置。使用該模式的目的是將物件的建立與它們的用法隔離開,並建立相關物件的族,而無需依賴它們的具體類。這允許引入新的派生型別,而無需更改使用基類的程式碼。
使用這種模式可以在不更改使用它們的程式碼的情況下,甚至在執行時,互換具體實現。然而,使用這種模式,就像使用類似的設計模式一樣,可能會導致不必要的複雜性和在最初編寫程式碼時的額外工作。此外,更高層次的隔離和抽象會導致系統更難除錯和維護。因此,與所有軟體設計一樣,必須仔細評估權衡取捨。
定義
抽象工廠模式的本質是“提供一個介面來建立相關或依賴物件的族,而無需指定它們的具體類”。
用法
工廠決定要建立的實際具體物件型別,並且在此處實際建立物件(例如,在 C++ 中,透過new運算子)。但是,工廠只返回對建立的具體物件的抽象指標。
透過讓客戶端請求工廠物件建立所需抽象型別物件並返回指向該物件的抽象指標,這將客戶端程式碼與物件建立隔離開來。
由於工廠只返回抽象指標,因此客戶端程式碼(從工廠請求物件的程式碼)不知道(並且不負擔)剛剛建立的物件的實際具體型別。然而,具體物件的型別(以及因此的具體工廠)是抽象工廠知道的;例如,工廠可能從配置檔案中讀取它。客戶端不需要指定型別,因為它已經在配置檔案中指定了。特別是,這意味著
- 客戶端程式碼完全不知道具體型別,不需要包含任何與之相關的標頭檔案或類宣告。客戶端程式碼只處理抽象型別。具體型別的物件確實是由工廠建立的,但客戶端程式碼只通過它們的抽象介面訪問這些物件。
- 新增新的具體型別是透過修改客戶端程式碼以使用不同的工廠來完成的,這種修改通常是一行程式碼,在一個檔案中。然後,不同的工廠建立不同具體型別的物件,但仍然返回與以前相同抽象型別的指標 - 因此將客戶端程式碼與更改隔離開來。這比修改客戶端程式碼來例項化新型別要容易得多,這將需要更改程式碼中建立新物件的每個位置(以及確保所有這些程式碼位置也都瞭解新具體型別,例如透過包含具體類標頭檔案)。如果所有工廠物件都全域性儲存在一個單例物件中,並且所有客戶端程式碼都透過單例訪問適當的工廠來建立物件,那麼更改工廠就像更改單例物件一樣容易。
結構
類圖

GuiFactory介面上的createButton方法返回Button型別的物件。返回的Button的具體實現取決於哪個GuiFactory實現正在處理方法呼叫。
注意,為了簡潔起見,此類圖只顯示建立一種型別物件的類關係。
Lepus3圖表 (圖例)

UML圖

實現
輸出應該是“我是 WinButton”或“我是 OSXButton”,具體取決於使用了哪種工廠。請注意,應用程式不知道它被賦予了哪種 GUIFactory,甚至不知道工廠建立了哪種 Button。
/* GUIFactory example -- */
using System;
using System.Configuration;
namespace AbstractFactory {
public interface IButton {
void Paint();
}
public interface IGUIFactory {
IButton CreateButton();
}
public class OSXButton : IButton { // Executes fourth if OS:OSX
public void Paint() {
System.Console.WriteLine("I'm an OSXButton");
}
}
public class WinButton : IButton { // Executes fourth if OS:WIN
public void Paint() {
System.Console.WriteLine("I'm a WinButton");
}
}
public class OSXFactory : IGUIFactory { // Executes third if OS:OSX
IButton IGUIFactory.CreateButton() {
return new OSXButton();
}
}
public class WinFactory : IGUIFactory { // Executes third if OS:WIN
IButton IGUIFactory.CreateButton() {
return new WinButton();
}
}
public class Application {
public Application(IGUIFactory factory) {
IButton button = factory.CreateButton();
button.Paint();
}
}
public class ApplicationRunner {
static IGUIFactory CreateOsSpecificFactory() { // Executes second
// Contents of App.Config associated with this C# project
//<?xml version="1.0" encoding="utf-8" ?>
//<configuration>
// <appSettings>
// <!-- Uncomment either Win or OSX OS_TYPE to test -->
// <add key="OS_TYPE" value="Win" />
// <!-- <add key="OS_TYPE" value="OSX" /> -->
// </appSettings>
//</configuration>
string sysType = ConfigurationSettings.AppSettings["OS_TYPE"];
if (sysType == "Win") {
return new WinFactory();
} else {
return new OSXFactory();
}
}
static void Main() { // Executes first
new Application(CreateOsSpecificFactory());
Console.ReadLine();
}
}
}
/* GUIFactory example */
#include <iostream>
class Button {
public:
virtual void paint() = 0;
virtual ~Button() { }
};
class WinButton : public Button {
public:
void paint() {
std::cout << "I'm a WinButton";
}
};
class OSXButton : public Button {
public:
void paint() {
std::cout << "I'm an OSXButton";
}
};
class GUIFactory {
public:
virtual Button* createButton() = 0;
virtual ~GUIFactory() { }
};
class WinFactory : public GUIFactory {
public:
Button* createButton() {
return new WinButton();
}
~WinFactory() { }
};
class OSXFactory : public GUIFactory {
public:
Button* createButton() {
return new OSXButton();
}
~OSXFactory() { }
};
class Application {
public:
Application(GUIFactory* factory) {
Button* button = factory->createButton();
button->paint();
delete button;
delete factory;
}
};
GUIFactory* createOsSpecificFactory() {
int sys;
std::cout << "Enter OS type (0: Windows, 1: MacOS X): ";
std::cin >> sys;
if (sys == 0) {
return new WinFactory();
} else {
return new OSXFactory();
}
}
int main() {
Application application(createOsSpecificFactory());
return 0;
}
/* GUIFactory example -- */
interface Button {
void paint();
}
interface GUIFactory {
Button createButton();
}
class WinFactory implements GUIFactory {
public Button createButton() {
return new WinButton();
}
}
class OSXFactory implements GUIFactory {
public Button createButton() {
return new OSXButton();
}
}
class WinButton implements Button {
public void paint() {
System.out.println("I'm a WinButton");
}
}
class OSXButton implements Button {
public void paint() {
System.out.println("I'm an OSXButton");
}
}
class Application {
public Application(GUIFactory factory) {
Button button = factory.createButton();
button.paint();
}
}
public class ApplicationRunner {
public static void main(String[] args) {
new Application(createOsSpecificFactory());
}
public static GUIFactory createOsSpecificFactory() {
int sys = readFromConfigFile("OS_TYPE");
if (sys == 0) return new WinFactory();
else return new OSXFactory();
}
}
/* GUIFactory example -- */
#import <Foundation/Foundation.h>
@protocol Button <NSObject>
- (void)paint;
@end
@interface WinButton : NSObject <Button>
@end
@interface OSXButton : NSObject <Button>
@end
@protocol GUIFactory
- (id<Button>)createButton;
@end
@interface WinFactory : NSObject <GUIFactory>
@end
@interface OSXFactory : NSObject <GUIFactory>
@end
@interface Application : NSObject
- (id)initWithGUIFactory:(id)factory;
+ (id)createOsSpecificFactory:(int)type;
@end
@implementation WinButton
- (void)paint {
NSLog(@"I am a WinButton.");
}
@end
@implementation OSXButton
- (void)paint {
NSLog(@"I am a OSXButton.");
}
@end
@implementation WinFactory
- (id<Button>)createButton {
return [[[WinButton alloc] init] autorelease];
}
@end
@implementation OSXFactory
- (id<Button>)createButton {
return [[[OSXButton alloc] init] autorelease];
}
@end
@implementation Application
- (id)initWithGUIFactory:(id<GUIFactory>)factory {
if (self = [super init]) {
id button = [factory createButton];
[button paint];
}
return self;
}
+ (id<GUIFactory>)createOsSpecificFactory:(int)type {
if (type == 0) {
return [[[WinFactory alloc] init] autorelease];
} else {
return [[[OSXFactory alloc] init] autorelease];
}
}
@end
int main(int argc, char* argv[]) {
@autoreleasepool {
[[Application alloc] initWithGUIFactory:[Application createOsSpecificFactory:0]];// 0 is WinButton
}
return 0;
}
--[[
Because Lua is a highly dynamic Language, an OOP scheme is implemented by the programmer.
The OOP scheme implemented here implements interfaces using documentation.
A Factory Supports:
- factory:CreateButton()
A Button Supports:
- button:Paint()
]]
-- Create the OSXButton Class
do
OSXButton = {}
local mt = { __index = OSXButton }
function OSXButton:new()
local inst = {}
setmetatable(inst, mt)
return inst
end
function OSXButton:Paint()
print("I'm a fancy OSX button!")
end
end
-- Create the WinButton Class
do
WinButton = {}
local mt = { __index = WinButton }
function WinButton:new()
local inst = {}
setmetatable(inst, mt)
return inst
end
function WinButton:Paint()
print("I'm a fancy Windows button!")
end
end
-- Create the OSXGuiFactory Class
do
OSXGuiFactory = {}
local mt = { __index = OSXGuiFactory }
function OSXGuiFactory:new()
local inst = {}
setmetatable(inst, mt)
return inst
end
function OSXGuiFactory:CreateButton()
return OSXButton:new()
end
end
-- Create the WinGuiFactory Class
do
WinGuiFactory = {}
local mt = { __index = WinGuiFactory }
function WinGuiFactory:new()
local inst = {}
setmetatable(inst, mt)
return inst
end
function WinGuiFactory:CreateButton()
return WinButton:new()
end
end
-- Table to keep track of what GuiFactories are available
GuiFactories = {
["Win"] = WinGuiFactory,
["OSX"] = OSXGuiFactory,
}
--[[ Inside an OS config script ]]
OS_VERSION = "Win"
--[[ Using the Abstract Factory in some the application script ]]
-- Selecting the factory based on OS version
MyGuiFactory = GuiFactories[OS_VERSION]:new()
-- Using the factory
osButton = MyGuiFactory:CreateButton()
osButton:Paint()
此抽象工廠建立書籍。
/*
* BookFactory classes
*/
abstract class AbstractBookFactory {
abstract function makePHPBook();
abstract function makeMySQLBook();
}
class OReillyBookFactory extends AbstractBookFactory {
private $context = "OReilly";
function makePHPBook() {
return new OReillyPHPBook;
}
function makeMySQLBook() {
return new OReillyMySQLBook;
}
}
class SamsBookFactory extends AbstractBookFactory {
private $context = "Sams";
function makePHPBook() {
return new SamsPHPBook;
}
function makeMySQLBook() {
return new SamsMySQLBook;
}
}
/*
* Book classes
*/
abstract class AbstractBook {
abstract function getAuthor();
abstract function getTitle();
}
abstract class AbstractMySQLBook extends AbstractBook {
private $subject = "MySQL";
}
class OReillyMySQLBook extends AbstractMySQLBook {
private $author;
private $title;
function __construct() {
$this->author = 'George Reese, Randy Jay Yarger, and Tim King';
$this->title = 'Managing and Using MySQL';
}
function getAuthor() {
return $this->author;
}
function getTitle() {
return $this->title;
}
}
class SamsMySQLBook extends AbstractMySQLBook {
private $author;
private $title;
function __construct() {
$this->author = 'Paul Dubois';
$this->title = 'MySQL, 3rd Edition';
}
function getAuthor() {
return $this->author;
}
function getTitle() {
return $this->title;
}
}
abstract class AbstractPHPBook extends AbstractBook {
private $subject = "PHP";
}
class OReillyPHPBook extends AbstractPHPBook {
private $author;
private $title;
private static $oddOrEven = 'odd';
function __construct() {
//alternate between 2 books
if ('odd' == self::$oddOrEven) {
$this->author = 'Rasmus Lerdorf and Kevin Tatroe';
$this->title = 'Programming PHP';
self::$oddOrEven = 'even';
} else {
$this->author = 'David Sklar and Adam Trachtenberg';
$this->title = 'PHP Cookbook';
self::$oddOrEven = 'odd';
}
}
function getAuthor() {
return $this->author;
}
function getTitle() {
return $this->title;
}
}
class SamsPHPBook extends AbstractPHPBook {
private $author;
private $title;
function __construct() {
//alternate randomly between 2 books
mt_srand((double)microtime() * 10000000);
$rand_num = mt_rand(0, 1);
if (1 > $rand_num) {
$this->author = 'George Schlossnagle';
$this->title = 'Advanced PHP Programming';
}
else {
$this->author = 'Christian Wenz';
$this->title = 'PHP Phrasebook';
}
}
function getAuthor() {
return $this->author;
}
function getTitle() {
return $this->title;
}
}
/*
* Initialization
*/
writeln('BEGIN TESTING ABSTRACT FACTORY PATTERN');
writeln('');
writeln('testing OReillyBookFactory');
$bookFactoryInstance = new OReillyBookFactory;
testConcreteFactory($bookFactoryInstance);
writeln('');
writeln('testing SamsBookFactory');
$bookFactoryInstance = new SamsBookFactory;
testConcreteFactory($bookFactoryInstance);
writeln("END TESTING ABSTRACT FACTORY PATTERN");
writeln('');
function testConcreteFactory($bookFactoryInstance) {
$phpBookOne = $bookFactoryInstance->makePHPBook();
writeln('first php Author: '.$phpBookOne->getAuthor());
writeln('first php Title: '.$phpBookOne->getTitle());
$phpBookTwo = $bookFactoryInstance->makePHPBook();
writeln('second php Author: '.$phpBookTwo->getAuthor());
writeln('second php Title: '.$phpBookTwo->getTitle());
$mySqlBook = $bookFactoryInstance->makeMySQLBook();
writeln('MySQL Author: '.$mySqlBook->getAuthor());
writeln('MySQL Title: '.$mySqlBook->getTitle());
}
function writeln($line_in) {
echo $line_in."<br/>";
}
此抽象工廠建立音樂家。
// concrete implementation
class DrummerFactory extends MusicianAbstractFactory {
def create(): Musician = new Drummer()
}
class GuitarPlayerFactory extends MusicianAbstractFactory {
def create(): Musician = new GuitarPlayer()
}
class GuitarPlayer extends Musician {
def play(song: String) {
println(s"I'm guitar player and I play $song")
}
}
class Drummer extends Musician {
def play(song: String) {
println(s"I'm drummer and I play '$song'")
}
}
object FactoryCreator {
def getFactory(config: Boolean): MusicianAbstractFactory =
if (config) {
new DrummerFactory
} else {
new GuitarPlayerFactory
}
}
// interfaces to import
trait Musician {
def play(song: String)
}
trait MusicianAbstractFactory {
def create(): Musician
}
import implementation.FactoryCreator
object AbstractFactoryTest {
def readFromConfig(): Boolean = true
def main(args: Array[String]) {
val factory = FactoryCreator.getFactory(readFromConfig())
val musician = factory.create()
musician.play("Highway to hell")
}
}
from abc import ABC, abstractmethod
from sys import platform
class Button(ABC):
@abstractmethod
def paint(self):
pass
class LinuxButton(Button):
def paint(self):
return "Render a button in a Linux style"
class WindowsButton(Button):
def paint(self):
return "Render a button in a Windows style"
class MacOSButton(Button):
def paint(self):
return "Render a button in a MacOS style"
class GUIFactory(ABC):
@abstractmethod
def create_button(self):
pass
class LinuxFactory(GUIFactory):
def create_button(self):
return LinuxButton()
class WindowsFactory(GUIFactory):
def create_button(self):
return WindowsButton()
class MacOSFactory(GUIFactory):
def create_button(self):
return MacOSButton()
if platform == "linux":
factory = LinuxFactory()
elif platform == "darwin":
factory = MacOSFactory()
elif platform == "win32":
factory = WindowsFactory()
else:
raise NotImplementedError(f"Not implemented for your platform: {platform}")
button = factory.create_button()
result = button.paint()
print(result)
使用類本身作為工廠的另一種實現
from abc import ABC, abstractmethod
from sys import platform
class Button(ABC):
@abstractmethod
def paint(self):
pass
class LinuxButton(Button):
def paint(self):
return "Render a button in a Linux style"
class WindowsButton(Button):
def paint(self):
return "Render a button in a Windows style"
class MacOSButton(Button):
def paint(self):
return "Render a button in a MacOS style"
if platform == "linux":
factory = LinuxButton
elif platform == "darwin":
factory = MacOSButton
elif platform == "win32":
factory = WindowsButton
else:
raise NotImplementedError(f"Not implemented for your platform: {platform}")
button = factory()
result = button.paint()
print(result)
另一個例子
import platform
class GuiFactory():
"""Abstract Factory"""
@classmethod
def getFactory(Class):
if platform.system() == "Windows":
return WinFactory()
elif platform.system() == "OSX":
return OSXFactory()
elif platform.system() == "Linux":
return LinuxFactory()
class Button:
""" Abstract Product"""
def paint(self):
raise NotImplementedError
@classmethod
def getButton(Class):
return Class.Button()
class WinFactory(GuiFactory):
"""Concrete Factory"""
class Button(GuiFactory.Button):
"""Concrete Product"""
def paint(self):
print "I am a Windows button"
class LinuxFactory(GuiFactory):
"""Concrete Factory"""
class Button(GuiFactory.Button):
"""Concrete Product"""
def paint(self):
print "I am a Linux button"
class OSXFactory(GuiFactory):
"""Concrete Factory"""
class Button(GuiFactory.Button):
"""Concrete Product"""
def paint(self):
print "I am a OSX button"
def main():
"""Application"""
factory = GuiFactory.getFactory()
factory.getButton().paint()
if __name__ == "__main__":
main()
program AbstractFactory;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
(* abstract factory *)
TAbstractFactory = class abstract
procedure Paint(); virtual; abstract;
end;
(* abstract product *)
TGUIFactory = class abstract
function CreateButton(): TAbstractFactory; virtual; abstract;
end;
(* concrete product *)
TOSXButton = class(TAbstractFactory)
public
procedure Paint(); override;
end;
(* concrete product *)
TWinButton = class(TAbstractFactory)
public
procedure Paint(); override;
end;
(* concrete factory *)
TOSXFactory = class(TGUIFactory)
public
function CreateButton(): TAbstractFactory; override;
end;
(* concrete factory *)
TWinFactory = class(TGUIFactory)
public
function CreateButton(): TAbstractFactory; override;
end;
(* client *)
TApplication = class
public
constructor Create(factory: TGUIFactory);
end;
(* Just for OOP style use class. This function is to return button factory only. *)
TApplicationRunner = class
public
class function CreateOsSpecificFactory: TGUIFactory;
end;
{ TOSXButton }
procedure TOSXButton.Paint;
begin
WriteLn('I`m an OSXButton');
end;
{ TWinButton }
procedure TWinButton.Paint;
begin
WriteLn('I`m a WinButton');
end;
{ TOSXFactory }
function TOSXFactory.CreateButton: TAbstractFactory;
begin
Result := TOSXButton.Create;
end;
{ TWinFactory }
function TWinFactory.CreateButton: TAbstractFactory;
begin
Result := TWinButton.Create;
end;
{ TApplication }
constructor TApplication.Create(factory: TGUIFactory);
var
button: TAbstractFactory;
begin
button := factory.CreateButton;
button.Paint;
end;
{ TApplicationRunner }
class function TApplicationRunner.CreateOsSpecificFactory: TGUIFactory;
var
sysType: string;
begin
WriteLn('Enter OS type (Win: Windows, OSX: MacOS X)');
ReadLn(sysType);
if (sysType = 'Win') then
Result := TWinFactory.Create
else
Result := TOSXFactory.Create
end;
var
App: TApplication;
begin
try
{ TODO -oUser -cConsole Main : Insert code here }
App := TApplication.Create(TApplicationRunner.CreateOsSpecificFactory);
WriteLn(#13#10 + 'Press any key to continue...');
ReadLn;
App.Free;
except
on E: Exception do
WriteLn(E.ClassName, ': ', E.Message);
end;
end.
