zoukankan      html  css  js  c++  java
  • 设计模式笔记(3)抽象工厂模式(创建型)

    Gof定义

    提供一个借口,让该接口负责创建一系列相关或者相互依赖的对象,无需指定他们具体的类。

    动机

    在软件系统中经常面临着一系列相互依赖的对象的创建的工作,同时由于需求的变化,往往存在着更对系列对象的创建。

    常规的对象创建的方法,直接使用new关键字

    Road road = new Road();

    这样直接new会有一个问题,不能应对具体实例化类型的变化,比如说有不同的Road类型需要被实例化的时候就需要来更改此处的代码。解决这个问题的办法就是封装变化点,变化点也是相对而言的,比如在项目中有些地方会经常更具客户的需求而作改动,我们就可以将其封装起来。拿上面的那行代码来说,因为可能有不同类型的Road需要被创建,变化点就是“对象的创建”。

    一种比较简单的解决方法,看下面的代码

    public class RoadFactory
    {
        public static Road CreateRoad()
        {
            return new Road();
        }
    }
    //调用的代码
    public class Test
    {
        static void Main(string[] args)
        {
            Road road = RoadFactory.CreateRoad();
        }
    }

    现在需求有了改变,需要创建一种另一种类型的路HighRoad,只需更改工厂类的代码就可以,调用的地方不用做修改,如下

    public class RoadFactory
    {
        public static Road CreateRoad()
        {
            return new HighRoad();
        }
    } 

    现在需求又有了改变,比如在游戏的开发场景中,我们需要构建道路、房屋、丛林等等对象,按照上面的思路我 可以写成下面这样的代码

    //客户程序
      public class Test
      {
          static void Main(string[] args)
          {
              Road road = GameObjectFactory.CreateRoad();
              Building building = GameObjectFactory.CreateBuilding();
              Jungle jungle = GameObjectFactory.CreateJungle();
          }
      }
      
      public class GameObjectFactory
      {
          public static Road CreateRoad()
          {
              return new Road(); 
          }
          public static Building CreateBuilding()
          {
              return new Building();
          }
          public static Jungle CreateJungle()
          {
              return new Jungle();
          }
      }

    以上的代码属于一种简单工厂的实现,将对象的创建放到一个单独的工厂类中,实现了和客户程序的分离,不过 不能应对不同系列对象的变化,比如有不同风格的游戏场景,对于要实现不同风格场景中的道路、房屋等对象的 创建,上面的代码就难以实现。对于有不同系列对象这样的需求,变化点转移到了工厂类的内部,上面的代码中 对象的创建是在工厂类中的静态方法中写死的,不能应对变化,如果我们另外创建一个能适应其他系列的工厂类, 那么客户程序就会发生改变,这不是我们希望看到的。要解决此类问题必须使用面向对象的技术来封装变化点。

    结构图

    2009-09-28_220757

    上面的结构图中AbstractFactory为创建对象的抽象工厂类,AbstractProductA、AbstractProductB为具 体对象的抽象类,拿到上面的例子中就是道路、房屋、丛林等的抽象。他们的子类ProductA1、ProductB1就是 指不同风格系列的是实现,这也正是抽象工厂模式要解决的问题。图中还可以看出客户程序依赖的是抽象的部分 而没有涉及到具体的实现,也就是说当需求改变的时候,客户程序是不用变的。下面就来看代码是如何实现。

    首先创建抽象工厂类

    public abstract class FacilitiesFactory
    {
        public abstract Road CreateRoad();
        public abstract Building CreateBuilding();
        public abstract Jungle CreateJungle();
    }

    因为游戏场景中有道路、房屋、丛林这三种对象,需要创建这三个对象的抽象类,如果有更多的对象,需要增加其他对象的抽象类。

    //道路
    public abstract class Road
    { 
    
    }
    //房屋
    public abstract class Building
    { 
    
    }
    //丛林
    public abstract class Jungle
    { 
    
    }

    下面的客户程序代码都是对抽象类之间的操作

    //客户程序
    public class GameManage
    {
        FacilitiesFactory _facilitiesFactory;
        Road _road;
        Building _building;
        Jungle _jungle;
        public GameManage(FacilitiesFactory facilitiesFactory)
        {
            _facilitiesFactory = facilitiesFactory;
        }
    
        /// <summary>
        /// 创建一些基础设施的对象
        /// </summary>
        public void BuildGameFacilities()
        {
            _road       = _facilitiesFactory.CreateRoad();
            _building   = _facilitiesFactory.CreateBuilding();
            _jungle     = _facilitiesFactory.CreateJungle();
        }
    
        public void Play()
        { 
            //执行一些操作
        }
    }

    在应用程序中使用上面的代码

    public class App
    {
        static void Main()
        {
            GameManage g = new GameManage(
    ?
    );
            g.BuildGameFacilities();
            g.Play();
        }
    }

    上面的代码中?的地方应该传入什么参数呢?从GameManage类的构造函数中可以看出是一个抽象工厂类型,大 家都知道,抽象类是不能创建实例的,所以问号的地方应该是AbstractFactory类的子类。假设现在有一个现代 风格系列的游戏场景,就需要创建一组现在的道路、房屋、丛林,如下:

    //道路
    public class ModernRoad:Road
    {
    
    }
    //房屋
    public class ModernBuilding:Building
    {
    
    }
    //丛林
    public class ModernJungle:Jungle
    {
    
    }

    下面创建现代风格的工厂类

    public class ModernFacilitiesFactory:FacilitiesFactory
    {
        public override Building CreateBuilding()
        {
            return new ModernBuilding();
        }
        public override Road CreateRoad()
        {
            return new ModernRoad();
        }
        public override Jungle CreateJungle()
        {
            return new ModernJungle();
        }
    }

    现在App类的代码可以改成如下

    public class App
    {
        static void Main()
        {
            GameManage g = new GameManage(new ModernFacilitiesFactory());
            g.BuildGameFacilities();
            g.Play();
        }
    }

    结构图

    2009-09-28_234052

    如果有新的需求,需要一套古典风格或者是梦幻风格的的游戏场景,只需要

    1 创建一套新的相应风格的对象继承道路、房屋、丛林等的抽象类

    2 创建一个相应风格的工厂继承抽象工厂

    3 App类中调用的地方给GameManage类传入相应的工厂类的实例。

    整个过程中,只是在外部扩展一些类,GameManage并没有做任何的改变

    抽象工厂模式的几个要点:

    • 如果没有对应多系列对象构建的需求变化,没有必要使用抽象工厂模式,使用静态工厂完全可以达到要求
    • 系列对象指的是这些对象之间的相互依赖,或作用的关系,如游戏开发场景中的道路与房屋的依赖等
    • 抽象工厂模式主要在于应对新系列的需求变动。其确定在于难以应对新对象的变动
    • 抽象工厂模式经常和工厂方法模式组合起来使用,来应对对象创建的需求变化

    返回开篇(索引)

  • 相关阅读:
    读书笔记:《与爱因斯坦月球漫步》
    在职工象棋赛上弃子拿下一盘
    桥牌笔记:精准叫牌法摘要
    桥牌笔记:探查牌型分布
    在64位环境下开发程序时常遇到的一个错误:无法加载文件或程序集
    我的记忆图像编码
    【转载】新闻周刊:《2012:31个让你变聪明的方法》
    【转译】希望将来孩子评价父亲的35句话
    桥牌笔记:安全打法,保持将牌控制
    被Html的Button标签耍了一次
  • 原文地址:https://www.cnblogs.com/oec2003/p/1607308.html
Copyright © 2011-2022 走看看