• 《设计模式》学习笔记(4)——抽象工厂模式(Abstract Factory)


    1、意图
        提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

    2、别名
        Kit

    3、适用性
        以下情况下可以使用此模式:

        • 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。

        • 这个系统有多于一个的产品族,而系统只消费其中某一产品族。

        • 同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。

        • 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。

    4、从上一篇的例子引申出来的一个问题:
        上一篇我们说到了使用硬编码的方法创建一个迷宫。这次我们需要将上面的代码复用,来创建一个被施了魔法的迷宫,这个施了魔法的迷宫由施过魔法的房间,需要符咒才能通过的门以及普通的墙壁构成。那么我们来看抽象工厂是如何方便解决这个问题的。

    5、例子的结构图示、代码及注释 
           
      
        在这里,构建迷宫的元素在MazeFactory中创建,对应的代码如下:

    namespace My.Reading.DesignPatterns.AbstractFactory
    {
        
    public class MazeFactory
        
    {
            
    public MazeFactory()
            
    {

            }


            
    public virtual Maze MakeMaze()
            
    {
                
    return new Maze();
            }


            
    public virtual Room MakeRoom(int roomNo)
            
    {
                
    return new Room(roomNo);
            }


            
    public virtual Wall MakeWall()
            
    {
                
    return new Wall();
            }


            
    public virtual Door MakeDoor(Room r1, Room r2)
            
    {
                
    return new Door(r1, r2);
            }

        }

    }


    注意这里并没有将MazeFactory声明为abstract,因为在这个例子中,MazeFactory既作为AbstractFactory,EnchantedMazeFactory就是继承自它;同时又作为ConcreteFactory,它仍然需要创建普通的Maze对象。那么同样它的派生类的代码如下:

    namespace My.Reading.DesignPatterns.AbstractFactory
    {
        
    public class EnchantedMazeFactory : MazeFactory
        
    {
            
    public EnchantedMazeFactory()
            
    {

            }


            
    public override Room MakeRoom(int roomNo)
            
    {
                
    return new EnchantedRoom(roomNo);
            }


            
    public override Door MakeDoor(Room r1, Room r2)
            
    {
                
    return new DoorNeedingSpell(r1, r2);
            }

        }

    }


    由于Wall使用的仍然是普通的Wall,因此没有必要覆盖抽象工厂的虚方法。同时为了方便,没有使用EnchantedMaze而仍然使用Maze,只是在添加Room和Door的时候添加的是EnchantedRoom和DoorNeedingSpell。

    Maze由于在上一篇已经定义过了,我们不需要改变原有的代码,我们现在只需要将与原来不同的类EnchantedRoom和DoorNeedingSpell重新定义。代码如下:

    namespace My.Reading.DesignPatterns.AbstractFactory
    {
        
    public class DoorNeedingSpell : Door
        
    {
            
    public DoorNeedingSpell(Room r1, Room r2) : base(r1, r2)
            
    {

            }


            
    public override void Enter()
            
    {
                
    throw new NotImplementedException();
            }


            
    public override void ShowSelfInformation()
            
    {
                Console.WriteLine(
    "I am the door that needs spell in namespace AbstractFactory!");
            }

        }


        
    public class EnchantedRoom : Room
        
    {
            
    public EnchantedRoom(int roomNo) : base(roomNo)
            
    {

            }


            
    public override void Enter()
            
    {
                
    throw new NotImplementedException();
            }


            
    public override void ShowSelfInformation()
            
    {
                Console.WriteLine(
    "I am the enchanted room in namespace AbstractFactory!");
            }

        }

    }


    那么,现在我们使用工厂作为参数来构建迷宫。如果我们使用普通迷宫,则传入创建普通迷宫的工厂;如果我们使用施了魔法的迷宫,则传入创建施了魔法魔法的迷宫的工厂。

    namespace My.Reading.DesignPatterns.AbstractFactory
    {
        
    public class MazeGame
        
    {
            
    public MazeGame()
            
    {

            }


            
    public Maze CreateMaze(MazeFactory factory)
            
    {
                Maze aMaze 
    = factory.MakeMaze();
                Room r1 
    = factory.MakeRoom(1);
                Room r2 
    = factory.MakeRoom(2);
                Door theDoor 
    = factory.MakeDoor(r1, r2);

                aMaze.AddRoom(r1);
                aMaze.AddRoom(r2);

                r1.SetSide(Direction.North, factory.MakeWall());
                r1.SetSide(Direction.East, theDoor);
                r1.SetSide(Direction.South, factory.MakeWall());
                r1.SetSide(Direction.West, factory.MakeWall());

                r2.SetSide(Direction.North, factory.MakeWall());
                r2.SetSide(Direction.East, factory.MakeWall());
                r2.SetSide(Direction.South, factory.MakeWall());
                r2.SetSide(Direction.West, theDoor);

                
    return aMaze;
            }

        }

    }

    这样我们便避开了使用硬编码的方法,使得程序更容易扩展。

    6、实现抽象工厂的一些有用技术:
        1)将工厂作为单件:一个应用中一般每个产品系列只需一个ConcreteFactory,因此工厂最好实现为一个Singleton。
        2)创建产品。抽象工厂仅仅声明了创建产品的接口,真正创建是由子类实现。那么通常有可能为每个产品定义一个工厂方法。但如果是有多个可能的产品系列,具体工厂也可以使用Phototype模式来实现。其实这样的创建产品主要是为了削弱抽象工厂对产品族的约束。
     
    PS:原文是用C++描述的,很多地方只是示意性的代码。因此有些地方可能会有错误,欢迎指正。

    此文章对应的代码下载:点击此处下载
  • 相关阅读:
    AS3中的xml
    HTML5 tools, Animation tools Adobe Edge Preview
    重新审视php+mssql
    SVN合并分支,从主干合并到分支
    AIR HTML相关资料[js部分]
    USACO 1.1friday
    H.High String
    POJ 3670 Eating Together
    HDU 1203:I NEED A OFFER!
    CodeForces #1 B. Spreadsheets
  • 原文地址:https://www.cnblogs.com/gamix/p/135585.html
Copyright © 2020-2023  润新知