zoukankan      html  css  js  c++  java
  • 设计模式之工厂模式(抽象工厂、工厂方法、简单工厂)

    1 前言

    工厂模式分类:

    抽象工厂(Abstract Factory):提供一个创建一系列相关或者相互依赖对象的接口,而无需指定它们具体的类。抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道或关心实际产出的具体产品是什么。这样一来,客户就能从具体的产品中被解耦。

    工厂方法(Factory Method):定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

    简单工厂(Simple Factory):简单工厂其实不是一个设计模式,比较像一种编程习惯,就是提供一个静态函数,根据不同的参数动态创建同一个类的不同子类对象。

    工厂模式针对的是一个产品等级结构 ,抽象工厂模式针对的是面向多个产品等级结构的。

    2 抽象工厂

    2.1 定义

      提供一个创建一系列相关或者相互依赖对象的接口,而无需指定它们具体的类。抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道或关心实际产出的具体产品是什么。这样一来,客户就能从具体的产品中被解耦。

    2.2 适用场景

      在以下情况可以使用Abstract Factory模式

    • 一个系统要独立于它的产品的创建、组合和表示时
    • 一个系统要由多个产品系列中的一个来配置时
    • 当你要强调一系列相关的产品对象的设计以便进行联合使用时
    • 当你提供一个产品类库,而只想显示它们的接口而不是实现时

    2.3 UML图

                      抽象工厂模式UML图a)

    • AbstractFactory  

      ——声明一个创建抽象产品对象的操作接口。

    • ConcreteFactory

      ——实现创建具体产品对象的操作。

    • AbstractProduct

      ——为一类产品对象声明一个接口。

    • ConcreteProduct

      ——定义一个将被相应的具体工厂创建的产品对象。

      ——实现AbstractProduct接口。

      在这里指的是ProductA1,ProductA2,ProductB1,ProductB2。

    • Client

      ——仅适用由AbstractFactory和AbstractProduct类声明的接口。

      一般而言,有多少个产品等级结构,就会在工厂角色中发现多少个工厂方法。每一个产品等级结构中有多少个具体的产品,就有多少个产品族,也就会在工厂等级结构中发现多少个具体工厂。

    2.4 代码示例

       《设计模式:可复用面向对象软件的基础》中一书中,给出了一个应用工厂方法设计迷宫的例子,这里修改为应用抽象工厂模式来实现。

      首先,给出了枚举类型Direction表明东南西北四个方向,定义抽象产品AbstractProduct接口,包含一个纯虚函数Enter,用于在迷宫间移动位置。

    enum Direction {North, South, East, West};
    
    //AbstractProduct
    class MapSite{
    public:
        virtual void Enter() = 0;
    };

      接下来,基于抽象产品接口MapSite实现ConcreteProduct具体产品类:

      定义迷宫房间Room类(ProductA);

    //ProductA
    class Room : public MapSite{
    public:
        Room(int roomNo);//初始化房间号
    
        MapSite *GetSide(Direction) const;//获取当前房间四周具体产品ConcreteProduct类MapSite
        void SetSide(Direction, MapSite*);//设置当前房间四周具体产品ConcreteProduct类MapSite
       virtual void Enter(); 
    private: 
       MapSite* _side[4]; 
       int _roomNumber; 
    };
    

       定义迷宫的墙Wall类(ProductB);

    //ProductB
    class Wall : public MapSite{
    public:
        Wall();
    
        virtual void Enter();
    };

       定义迷宫的门Door类(ProductC);

    //ProductC
    class Door : public MapSite{
    public:
        Door(Room * = 0, Room * = 0);//初始化门两边的房间
    
        virtual void Enter();
        Room* OtherSideFrom(Room *);
    private:
        Room * _room1;
        Room * _room2;
        bool _isOpen;
    };

      定义迷宫Maze类(ProductD); 

    //ProductD
    class Maze{
    public:
        Maze();
    
        void AddRoom(Room *);
        Room* RoomNo(int) const;
    private:
        //...
    };
    

      在完成抽象产品接口和具体产品类定义后,接下来定义抽象工厂接口AbstractFactory类MazeFactory,对外提供接口,用于生成上述定义的具体产品类ProductA——Room,ProductB——Wall,ProductC——Door,ProductD——Maze。

    //AbstractFactory
    class MazeFactory{
    public:
        MazeFactory();
    
        virtual Maze* MakeMaze() const
        {return new Maze;}
        virtual Wall* MakeWall() const 
        {return new Wall;}
        virtual Room* MakeRoom(int n) const
        {return new Room(n);}
        virtual Door* MakeDoor(Room* r1, Room* r2) const
        {return new Door(r1,r2);}
    };
    

      在定义好抽象工厂接口、抽象产品接口、具体产品后,就可以由Client来动态创建和处理各个对象的关系,这里创建一个包含两个房间的简单迷宫。

    //Client
    Maze * MazeGame::CreateMaze(MazeFactory &factory){
        Maze * aMaze = factory.MakeMaze();
        Room *r1 = factory.MakeRoom(1);
        Room *r2 = factory.MakeRoom(2);
        Door *aDoor = factory.MakeDoor(r1,r2);
    
        aMaze->AddRoom(r1);
        aMaze->AddRoom(r2);
    
        r1->SetSide(North, factory.MakeWall());
        r1->SetSide(East, aDoor);
        r1->SetSide(South,factory.MakeWall());
        r1->SetSide(West, factory.MakeWall());
        r2->SetSide(North, factory.MakeWall());
        r2->SetSide(East, factory.MakeWall());
        r2->SetSide(South, factory.MakeWall());
        r2->SetSide(West, aDoor);
    
        return aMaze;
    }
    

      如果需要创建带各种特效或者属性的Room、Door、Wall ,只需要继承具体产品类ProductA、ProductB、ProductC、ProductD,以及抽象工厂AbstractFactory接口MazeFactory。

      如果要生成魔法迷宫,只需要继承MazeFactory生成ConcreteFactory类EnchantedMazeFacotry,

    //ConcreteFactory
    class EnchantedMazeFactory:public MazeFactory{
    public:
        EnchantedMazeFacotry();
    
        virtual Room* MakeRoom(int n) const
        {return new EnchantedRoom(n, castSpell());}
        virtual Door* MakeDoor(Room* r1, Room* r2) const
        {return new DoorNeedingSpell(r1,r2);}
    protected:
        Spell* CastSpell() const;
    };

       调用CreateMaze就可以生成带魔法房间和符咒门的迷宫了。。。

       先创建一个工厂生产器FactoryProducer:

    MazeFactory *FactoryProducer(string type){
        if(type == "Maze"){
            return new MazeFactory;
        }
        else if(type == "EnchantedMaze"){
            return new EnchantedMazeFactory;
        }
    }

      客户端只需要针对抽象接口编程即可:

    MazeFacoty *factory = FactoryProducer("Maze");
    Maze *maze = CreateMaze(factory);
    
    factory = FactoryProducer("EnchantedMaze");
    maze = CreateMaze(factory);

      同理还可以,由爆炸工厂生成带爆炸门和爆炸房间的迷宫。。。

    Wall * BombedMazeFactory::MakeWall() const {
        return new BombedWall;
    }
    Room* BombedMazeFactory::MakeRoom(int n) const{
        return new RoomWithABomb(n);
    }
    

       如果嫌上面例子麻烦, 下面这个例子(截图自wikipedia官网),简单明了的阐释了抽象工厂模式:

      

    2.5 优缺点

    优点:

    1、抽象工厂模式隔离了具体类的生产,使得客户并不需要知道什么被创建。

    2、当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

    3、增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。

     

    缺点:

    增加新的产品等级结构很复杂,需要修改抽象工厂和所有的具体工厂类,对“开闭原则”的支持呈现倾斜性。

    3 工厂方法

    3.1 定义

    定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

    3.2 适用场景

    在下列情况下可以使用Factory Method模式:

    • 当一个类不知道它所必须创建的对象的类的时候。
    • 当一个类希望由它的子类来指定它所创建的对象的时候。
    • 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。

    3.3 UML图

                        UML结构

    • Product

      ——定义工厂方法所创建的对象的接口。

    • ConcreteProduct

      ——实现Product接口。

    • Creator

      ——声明工厂方法,该方法返回一个Product类型的对象。Creator也可以定义一个工厂方法的却省实现,它返回一个缺省的ConcreteProduct对象。

      ——可以调用该工厂方法以创建一个Product对象。

    • ConcreteCreator

      ——重定义工厂方法以返回一个ConcreteProduct实例。

    3.4 代码示例

       与抽象工厂不同的时,在抽象工厂中,只需要将抽象工厂接口Factory传到client中,然后就能生成不同的Product,然而在工厂方法中,是通过Creator来实现的。Creator中FactoryMethod是一些产品虚函数,用于生成具体的产品的,而AnOperation成员函数,相当于抽象工厂中的Client,调用ConcreteCreator的虚函数FactoryMethod来生成真正的产品。

      修改抽象产品中迷宫例子,将CreateMaze函数和MazeFactory合并到一块,并将CreateMaze参数去掉,改名为MazeGame,这样就是工厂方法的实现了,这里MazeGame就是Creator,如果需要扩展生成更多的产品,只需要继承MazeGame,实现ConcreteCreator,然后重载虚函数FactoryMethod,就能创建更多的产品。

    class MazeGame{
    public:
      Maze * CreateMaze();
      //factory method
      virtual Maze* MakeMaze() const
        {return new Maze;}
        virtual Wall* MakeWall() const 
        {return new Wall;}
        virtual Room* MakeRoom(int n) const
        {return new Room(n);}
        virtual Door* MakeDoor(Room* r1, Room* r2) const
        {return new Door(r1,r2);}
    };
    

      CreateMaze是AnOperation的具体实现。

    Maze * MazeGame::CreateMaze(){
        Maze * aMaze = MakeMaze();
        Room *r1 = MakeRoom(1);
        Room *r2 = MakeRoom(2);
        Door *aDoor = MakeDoor(r1,r2);
    
        aMaze->AddRoom(r1);
        aMaze->AddRoom(r2);
    
        r1->SetSide(North, MakeWall());
        r1->SetSide(East, aDoor);
        r1->SetSide(South,MakeWall());
        r1->SetSide(West, MakeWall());
        r2->SetSide(North, MakeWall());
        r2->SetSide(East, MakeWall());
        r2->SetSide(South, MakeWall());
        r2->SetSide(West, aDoor);
    
        return aMaze;
    }
    

      EnchantedMazeGame是ConcreteCreator的具体实现。

    class EnchantedMazeGame:public MazeGame{
    public:
        EnchantedMazeFacotry();
    
        virtual Room* MakeRoom(int n) const
        {return new EnchantedRoom(n, castSpell());}
        virtual Door* MakeDoor(Room* r1, Room* r2) const
        {return new DoorNeedingSpell(r1,r2);}
    protected:
        Spell* CastSpell() const;
    };
    

      Client生成具体的产品调用方式为:

    //Factory Producer
    MazeGame *CreateMaze(string type){
      if(type == "Maze"){
            return new MazeGame;
        }  
        else if(type == "EnchantedMaze"){
            return new EnchantedMazeGame ;
        }
    }
    //Creator
    MazeGame *creator= CreateMaze("Maze");
    Maze *maze = creator->CreateMaze();
    //ConcreteCreator
    creator= CreateMaze("EnchantedMaze");
    maze = creator->CreateMaze();
    

      

    3.5 优缺点

     优缺点除了抽象工厂的外,不能很好的支持多种产品。

    4 简单工厂

    4.1 定义

      简单工厂其实不是一个设计模式,比较像一种编程习惯,就是提供一个静态函数,根据不同的参数动态创建同一个类的不同子类对象。

    4.2 适用场景

    4.3 UML图

    • Product

      ——产品类,具有一些有用的实现,这些实现是虚函数,可以被子类覆盖。

    • ProductFactory

      ——产品工厂,是一个静态函数,根据不同的参数信息,生成不同的产品对象。

    4.4 代码示例

       MazeGame和EnchantedMazeGame为UML图中的Product和ConcreteProduct。

    class MazeGame{
    public:
      virtual Maze* MakeMaze() const
        {return new Maze;}
        virtual Wall* MakeWall() const
        {return new Wall;}
        virtual Room* MakeRoom(int n) const
        {return new Room(n);}
        virtual Door* MakeDoor(Room* r1, Room* r2) const
        {return new Door(r1,r2);}
    };
    
    class EnchantedMazeGame:public MazeGame{
    public:
        EnchantedMazeFacotry();
     
        virtual Room* MakeRoom(int n) const
        {return new EnchantedRoom(n, castSpell());}
        virtual Door* MakeDoor(Room* r1, Room* r2) const
        {return new DoorNeedingSpell(r1,r2);}
    protected:
        Spell* CastSpell() const;
    };
    

      用简单工厂的方法重写CreateMaze如下,根据不同的type类型创建不同的迷宫:

    Maze * CreateMaze(string type){
    	Maze * aMaze = NULL;
    	if(type == "Maze"){
    		aMaze = new MazeGame;
    	}
    	else if(type == "EnchantedMaze"){
    		aMaze = new EnchantedMazeGame;
    	}
    
        Room *r1 = aMaze->MakeRoom(1);
        Room *r2 = aMaze->MakeRoom(2);
        Door *aDoor = aMaze->MakeDoor(r1,r2);
     
        aMaze->AddRoom(r1);
        aMaze->AddRoom(r2);
     
        r1->SetSide(North, MakeWall());
        r1->SetSide(East, aDoor);
        r1->SetSide(South,MakeWall());
        r1->SetSide(West, MakeWall());
        r2->SetSide(North, MakeWall());
        r2->SetSide(East, MakeWall());
        r2->SetSide(South, MakeWall());
        r2->SetSide(West, aDoor);
     
        return aMaze;
    }
    

       

    4.5 优缺点

       缺点是为违背了编码“高内聚低耦合”的原则,将很多细节暴露了给客户,如CreateMaze中函数,房间、墙、门是如何组成迷宫的细节都暴露了。

    5 如何选择三种设计模式

    参考资料:

    a) https://en.wikipedia.org/wiki/Abstract_factory_pattern

    b) 《设计模式:可复用面向对象软件的基础》

    c) http://www.runoob.com/design-pattern/abstract-factory-pattern.html

    d) https://zh.wikipedia.org/wiki/%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82

  • 相关阅读:
    PHP识别二维码功能,php-zbarcode 安装
    《架构即未来》读后感(三)
    MVC设计模式案例分析
    SOA
    《架构即未来》读后感(二)
    基于网络拓扑及告警的故障根因定位系统实现及算法研究赛题需求分析
    《架构即未来》读后感(一)
    《大型网站技术架构》读后感(二)
    《一线架构师实践指南》读后感(三)
    《大型网站技术架构》读后感(三)
  • 原文地址:https://www.cnblogs.com/chenyangchun/p/7183795.html
Copyright © 2011-2022 走看看