抽象工厂
抽象工厂模式提供了一种封装一组具有共同主题的单个工厂的方法,而无需指定它们的具体类。在正常使用中,客户端软件创建一个抽象工厂的具体实现,然后使用工厂的通用接口来创建属于该主题的具体对象。客户端不知道(也不关心)它从每个这些内部工厂获得哪些具体对象,因为它只使用它们的产品的通用接口。这种模式将一组对象的实现细节与它们的通用使用分开,并依赖于对象组合,因为对象创建是在工厂接口中公开的方法中实现的。一个例子是抽象工厂类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.