跳轉到內容

抽象工廠

25% developed
華夏公益教科書,開放書籍,開放世界

計算機科學設計模式
抽象工廠
介面卡

抽象工廠模式提供了一種封裝一組具有共同主題的單個工廠的方法,而無需指定它們的具體類。在正常使用中,客戶端軟體建立一個抽象工廠的具體實現,然後使用工廠的通用介面來建立屬於該主題的具體物件。客戶端不知道(也不關心)它從每個這些內部工廠獲得哪些具體物件,因為它只使用它們的產品的通用介面。這種模式將一組物件的實現細節與它們的通用使用分開,並依賴於物件組合,因為物件建立是在工廠介面中公開的方法中實現的。一個例子是抽象工廠類DocumentCreator,它提供建立多個產品的介面(例如createLetter()createResume())。該系統將擁有任意數量的DocumentCreator類的派生具體版本,例如FancyDocumentCreatorModernDocumentCreator,每個版本都有createLetter()createResume()的不同實現,它們將建立一個對應的物件,例如FancyLetterModernResume。這些產品中的每一個都派生自一個簡單的抽象類,例如LetterResume,客戶端知道這些類。客戶端程式碼將獲取DocumentCreator的適當例項,並呼叫其工廠方法。每個生成的都是從同一個DocumentCreator實現建立的,並且將共享一個共同的主題(它們將都是花哨的或現代的物件)。客戶端只需要知道如何處理抽象的LetterResume類,而不需要知道它從具體工廠獲得的特定版本。

工廠是程式碼中構建物件的具體類的所在位置。使用該模式的目的是將物件的建立與它們的用法隔離開,並建立相關物件的族,而無需依賴它們的具體類。這允許引入新的派生型別,而無需更改使用基類的程式碼。

使用這種模式可以在不更改使用它們的程式碼的情況下,甚至在執行時,互換具體實現。然而,使用這種模式,就像使用類似的設計模式一樣,可能會導致不必要的複雜性和在最初編寫程式碼時的額外工作。此外,更高層次的隔離和抽象會導致系統更難除錯和維護。因此,與所有軟體設計一樣,必須仔細評估權衡取捨。

定義

抽象工廠模式的本質是“提供一個介面來建立相關或依賴物件的族,而無需指定它們的具體類”。

用法

工廠決定要建立的實際具體物件型別,並且在此處實際建立物件(例如,在 C++ 中,透過new運算子)。但是,工廠只返回對建立的具體物件的抽象指標。

透過讓客戶端請求工廠物件建立所需抽象型別物件並返回指向該物件的抽象指標,這將客戶端程式碼與物件建立隔離開來。

由於工廠只返回抽象指標,因此客戶端程式碼(從工廠請求物件的程式碼)不知道(並且不負擔)剛剛建立的物件的實際具體型別。然而,具體物件的型別(以及因此的具體工廠)是抽象工廠知道的;例如,工廠可能從配置檔案中讀取它。客戶端不需要指定型別,因為它已經在配置檔案中指定了。特別是,這意味著

  • 客戶端程式碼完全不知道具體型別,不需要包含任何與之相關的標頭檔案或類宣告。客戶端程式碼只處理抽象型別。具體型別的物件確實是由工廠建立的,但客戶端程式碼只通過它們的抽象介面訪問這些物件。
  • 新增新的具體型別是透過修改客戶端程式碼以使用不同的工廠來完成的,這種修改通常是一行程式碼,在一個檔案中。然後,不同的工廠建立不同具體型別的物件,但仍然返回與以前相同抽象型別的指標 - 因此將客戶端程式碼與更改隔離開來。這比修改客戶端程式碼來例項化新型別要容易得多,這將需要更改程式碼中建立新物件的每個位置(以及確保所有這些程式碼位置也都瞭解新具體型別,例如透過包含具體類標頭檔案)。如果所有工廠物件都全域性儲存在一個單例物件中,並且所有客戶端程式碼都透過單例訪問適當的工廠來建立物件,那麼更改工廠就像更改單例物件一樣容易。

結構

類圖

GuiFactory介面上的createButton方法返回Button型別的物件。返回的Button的具體實現取決於哪個GuiFactory實現正在處理方法呼叫。

注意,為了簡潔起見,此類圖只顯示建立一種型別物件的類關係。

Lepus3圖表 (圖例)

UML圖

實現

輸出應該是“我是 WinButton”或“我是 OSXButton”,具體取決於使用了哪種工廠。請注意,應用程式不知道它被賦予了哪種 GUIFactory,甚至不知道工廠建立了哪種 Button。

C# 實現
/* 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();
        }
    }
}
C++ 實現
/* 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;
}
Java 實現
/* 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();
    }
}
Objective-C 實現
/* 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;
}
Lua 實現
--[[
    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()
PHP 實現

此抽象工廠建立書籍。

/*
 * 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/>";
}
Scala 實現

此抽象工廠建立音樂家。

// 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")
  }
}
Python 實現
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()
Delphi 實現
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.


Clipboard

待辦事項
新增更多使用示例。


計算機科學設計模式
抽象工廠
介面卡


您對本頁有任何疑問嗎?
在此處提問


在此書上建立一個新頁面


華夏公益教科書