zoukankan      html  css  js  c++  java
  • 面向程序猿的设计模式 ——GoF《设计模式》读书总结(壹)抽象工厂&生成器


    第一部分:创建型模式



    创建型模式抽象了实例化过程

    它们帮助一个系统独立于怎样创建、组合和表示它的那些对象。(把一些小的对象组装成大对象,这个工作由专门的类对象来做)

    一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化托付给还有一个对象。

     

    随着系统演化得越来越依赖于对象复合而不是类继承,创建型模式变得更为重要。由于系统定义一个较小的行为集,这些行为能够被组合成随意数目的更复杂的行为。

    (把行为分散在各类中。用对象组合的方式,依据须要组合成大类)

     

    这些创建型模式有两个特点

    1、它们都将关于该系统使用哪些详细的类的信息封装起来。

    2、它们隐藏了这些类的实例是怎样被创建和组装在一起的。

     

    创建型模式在什么被创建谁创建它,如何被创建。以及何时创建这些方面给你非常大的灵活性。

     


    (一)、抽象工厂(Abstract Factory)——对象创建型模式


    意图:

    提供一个 创建一系列相关或相互依赖对象的 接口,而无需指定它们详细的类。(用一个工厂类创建系统中相关连的对象)

     

    引子:

    考虑一个支持多种视感风格用户界面工具包。不同的视感风格为滚动栏、窗体和button等窗体组件定义不同的外观和行为。为保证视感风格间的可移植性,一个应用不应该为一个特定的视感外观编码它的窗体组件。

     

    (考虑系统中哪些是未来可能变化的,一组相关的窗体组件。那么我们就封装变化,专门定义一个类来生成这一组相关的窗体组件。)

     

    为解决这一问题,我们能够定义一个抽象的WidgetFactory类,这个类声明了一个用来创建每一类基本窗体组件的接口。每一类窗体组件都有一个抽象类。而详细子类则实现了窗体组件特定的视感风格。

    WidgetFactory接口有一个返回新窗体组件对象的操作。

    客户调用这些操作以获得窗体组件实例,但客户并不知道他们正在使用的是哪些详细类。这样客户就不依赖于一般的视感风格。

     



    每一种视感标准都相应于一个详细的WidgetFactory子类。(比如。MotifWidgetFactory的CreateSrollBar实例化并返回一个Motif滚动栏。

    客户仅通过WidgetFactory接口创建窗体组件,他们并不知道哪些类实现了特定视感的窗体组件。换言之。客户仅与抽象类定义的接口交互,而不使用特定的详细类的接口

    (客户不知道用的是哪一套创建方案)

    对于客户来说。其仅仅知道WidgetFactory、Window、SrollBar这三个抽象类。

      

    适用:

    1、一个系统要独立于它的产品的创建、组合和表示时

    2、一个系统要由多个产品系列中的一个来配置时。

    3、当你强调一系列相关的产品对象的设计以便进行联合使用时。

     

    特点:

    1、它分离了详细的类

    由于一个工厂封装创建产品的责任和过程,它将客户与类的实现分离。

    客户通过它们的抽象接口操纵实例。产品的类名也不出如今客户代码中。(比方:详细的产品类名ProductA1。客户使用产品的抽象类名AbstractProductA)


    2、它使得易于交换产品系列

    一个抽象工厂创建了一个完整的产品系列,仅仅需改变详细的工厂就可以使用不同的产品配置,整个产品系列会立马改变。


    3、难以支持新种类的产品

    难以扩展抽象工厂以生产新种类的产品。这是由于AbstractProduct接口确定了能够被创建的产品集合。

    支持新种类的产品就须要扩展该工厂接口,这将涉及AbstractProduct类及其全部子类的改变。

     


    代码演示样例:

    为一个电脑游戏创建一个迷宫,我们将忽略很多迷宫中的细节以及一个迷宫游戏中有一个还是多个游戏者。我们仅关注迷宫是如何被创建的。

    我们将一个迷宫定义为一系列房间,一个房间知道它的邻居。可能的邻居要么是还有一个房间。要么是一堵墙,或者是到还有一个房间的一扇门。

    类Room、Door和Wall定义了我们全部的样例中使用到的构件。

     

    我们将使用Abstract Factory模式创建这个迷宫。

    类MazeFactory能够创建迷宫组件。它建造房间、墙壁和房间之间的门。 建造迷宫的程序将MazeFactory作为一个參数,这样程序猿就能指定要创建的房间、墙壁和门等类。

     

    class MazeFactory

    {

    public:

       MazeFactory() ;

       

        virtual  Maze*   MakeMaze() const

        {    return new Maze ;  }

        virtual  Maze*   MakeWall() const

        {    return new Wall ;  }

        virtual  Maze*   MakeRoom(int n) const

        {    return new Room(n) ;  }

        virtual  Maze*   MakeDoor(Room* r1, Room* r2) const

        {    return new Door(r1, r2) ;  }

    } ;

     

    //我们创建MazeFactory的子类EnchantedMazeFactory。这是一个创建施了魔法的迷宫的工厂。

    //EnchantedMazeFactory将重定义不同成员函数并返回Room,Wall等不同的子类。

    class EnchantedMazeFactory : public MazeFactory

    {

    public:

       EnchantedMazeFactory() ;

       

        virtual  Room*   MakeRoom(int n) const

        {    return new   EnchantedRoom(n) ;    }

     

        virtual  Maze*   MakeDoor(Room* r1, Room* r2) const

        {    return new   DoorNeedingSpell(r1, r2) ;  }

    } ;

     

    //我们创建MazeFactory的子类BombedMazeFactory。这是一个炸弹毁坏过的迷宫的工厂

    //Room的子类RoomWithABomb,Wall的子类BombedWall来表示炸过的房间和墙壁

    class BombedMazeFactory : public MazeFactory

    {

    public:

       BombedMazeFactory() ;

       

        virtual  Maze*   MakeWall() const

        {    return   new   BombedWall ;  }

        virtual  Maze*   MakeRoom(int n) const

        {    return   new   RoomWithABomb(n) ;  }

    } ;

      

    客户使用:

    客户的CreateMaze函数。以MazeFactory为參数

    Maze* MazeGame::CreateMaze (MazeFactory&factory)

    {

        Maze*  aMaze = factory.MakeMaze() ;

        Room*  r1= factory.MakeRoom(1) ;

        Room*  r2= factory.MazeRoom(2) ;

        Door*  aDoor = factory.MakeDoor(r1, r2) ;

       

       aMaze->AddRoom(r1) ;

       aMaze->AddRoom(r2) ;

       

        //用户自己组装(工厂仅仅负责创建)

       r1->SetSide(North, factory.MakeWall()) ;

        //......

       

        return  aMaze ;

    }

     

    为创建一个包括炸弹的简单迷宫,我们仅用BombedMazeFactory调用CreateMaze。

    MazeGame   game ;

    BombedMazeFactory   factory ;

     

    game.CreateMaze(factory) ;

     

    通过抽象工厂来创建对象(面向接口编程),而不是自己直接创建,为什么要假手工厂来创建对象?由于灵活性,当我们要其他风格的对象时,换工厂即可了。

    (由于接口不变。我们做的修改小)

     

     

    关键词:工厂

     

     

     

    (二)、生成器(Builder)——对象创建型模式

     

    意图:

    将一个复杂对象的构建与它的表示分离。使得相同的构建过程能够创建不同的表示

    【即把组装与生成分离,由一个类(生成器Builder)负责生成所需的配件,由还有一个类(向导器Director)负责组装配件成产品】

     

    ConcreteBuilder创建该产品的内部表示并定义它的装配过程。

     


    生成器逐步的构造它们的产品。因此Builder类接口必须足够普遍,以便为各种类型的详细生成器构造产品。

    且注意:在Builder中缺省的方法为空,而不是纯虚函数,把它们定义为空方法,这使客户仅仅重定义他们所感兴趣的操作。

    不同的生成器提供不同的小配件给向导器

     


    协作:

    1、客户创建Director对象,并用它所想要的Builder对象进行配置。

    2、一旦产品部件被生成(导向器调Builder),导向器就会通知生成器。

    (生成部件)

    3、生成器处理导向器的请求。并将部件加入到该产品中。(组装)

    4、客户从生成器中检索(获取)产品。

     


    特点:

    1、它使你能够改变一个产品的内部表示

    Builder对象提供给导向器一个构造产品的抽象接口。该接口使得生成器能够隐藏这个产品的表示和内部结构。它同一时候也隐藏了该产品是怎样装配的。


    2、它将构造代码和表示代码分开

    客户不须要知道定义产品内部结构的类的全部信息,这些类是不出如今Builder接口中的。每一个实际的生成器包括了创建和装配一个特定产品的全部代码。


    3、它使你可对构造过程进行更精细的控制

    Builder模式与一下子就生成产品的创建型模式不同。它是在导向者的控制下一步一步构造产品的

    仅当该产品完毕时导向者才从生成器中取回它。(导向者控制装配的顺序)

     


    代码举例:

    以创建迷宫为例:

     

    一个CreateMaze函数专门用来创建迷宫,它以类MazeBuilder的一个生成器对象作为參数。

    class MazeBuilder

    {

    public:

        virtual  void   BuildMaze() { }

        virtual  void   BuildRoom(int room) { }

        virtual  void   BuildDoor(int roomFrom, int roomTo) { }

       

        virtual  Maze*   GetMaze() {  return 0;  }

    protected:

       MazeBuilder() ;

    } ;

    该接口可创建:1、迷宫(空架子) 2、有一个特定房间号的房间 3、在有号码的房间之间的

    GetMaze操作返回这个迷宫给客户。 MazeBuilder的子类将重定义这些操作。返回它们所创建的迷宫。

     

    注意MazeBuilder自己并不创建迷宫。它的主要目的不过为创建迷宫定义一个接口。MazeBuilder的子类做实际工作

     

    class StandardMazeBuilder : public MazeBuilder

    {

    public:

       StandardMazeBuilder() ;

       

        virtual  void   BuildMaze() ;

        virtual  void   BuildRoom(int) ;

        virtual  void   BuildDoor(int, int) ;

       

        virtualMaze*   GetMaze() ;

     

    private:

       Direction   CommonWall(Room*, Room*) ;

        Maze*  _currentMaze ;

    } ;

     

    void StandardMazeBuilder::BuildMaze()   //向构造一个空的Maze。再逐步往里面加东西。

    {

       _currentMaze = new Maze ;

    }

    void StandardMazeBuilder::GetMaze()

    {

       return  _currentMaze  ;

    }

     

    //BuildRoom操作创建一个房间并建造它周围的墙壁

    void StandardMazeBuilder::BuildRoom(int n)

    {

        if(!_currentMaze->RoomNo(n))

        {

           Room*  room = new Room(n) ;

           _currentMaze->AddRoom(room) ;

           

            //创建Room周围的墙壁

           room->SetSide(North, new Wall) ;

           room->SetSide(South, new Wall) ;

           room->SetSide(East, new Wall) ;

           room->SetSide(West, new Wall) ;   

        }

    }

    //.......

     

    导向器上场:其定义了组装的流程(向建一个Maze框架,再填充两个Room,再在两房子之间填充Door)

    Maze* MazeGame::CreateMaze(MazeBuilder& builder)

    {

       builder.BuildMaze() ;

       

       builder.BuildRoom(1) ;

       builder.BuildRoom(2) ;

       builder.BuildDoor(1, 2) ;

     

        returnbuilder.GetMaze() ;

    }

     

    用户使用:用户如今能够用CreateMaze和StandardMazeBuilder来创建一个迷宫

    Maze* maze ;

    MazeGame game ;

    StandardMazeBuilder builder ;

     

    game.CreateMaze(builder) ;//CreateMaze依据其定义的流程去指导builder逐步创建、组装好产品。

    maze = builder.GetMaze() ;

     

     

    抽象工厂VS 生成器


    两者的目的不同,抽象工厂就是生产一些相关的产品给用户,用户怎么用这些产品其不关心。生成器就是生产一些小部件,由导向器来装配这些部件,组装成一个产品。

    抽象工厂是一个工厂。一个工厂里生产各种产品(这些产品都是相关的)。它提供给用户的是多个接口,每一个接口产生一个产品。

     

    抽象工厂与生成器相似。由于它也能够创建复杂对象。基本的差别是Builder模式着重于一步步构造一个复杂对象。而抽象工厂着重于多个系列的产品对象(简单的或是复杂的)。

    Builder在最后一步返回产品,而对于抽象工厂来说。产品是马上返回的

     

     

    关键词:装配

     










  • 相关阅读:
    Python使用requests发送请求
    Python安装requests
    Python Windows开发环境搭建
    django学习笔记urls(1)
    Spring boot activiti工作流,权限管理框架
    Redis入门实战(7)-SpringDataRedis操作redis
    Redis入门实战(6)-Redisson操作redis
    Redis入门实战(5)-lettuce操作redis
    Redis入门实战(4)-Jedis操作redis
    Redis入门实战(3)-命令行
  • 原文地址:https://www.cnblogs.com/mthoutai/p/6952892.html
Copyright © 2011-2022 走看看