zoukankan      html  css  js  c++  java
  • 工厂模式之抽象工厂模式

    图显示有问题。可直接看:

    点击打开链接

    场景问题

      举个生活中常见的样例——组装电脑,我们在组装电脑的时候。通常须要选择一系列的配件,比方CPU、硬盘、内存、主板、电源、机箱等。

    为讨论使用简单点,仅仅考虑选择CPU和主板的问题。

      其实。在选择CPU的时候。面临一系列的问题,比方品牌、型号、针脚数目、主频等问题。仅仅有把这些问题都确定下来,才干确定详细的CPU。

      相同,在选择主板的时候。也有一系列问题。比方品牌、芯片组、集成芯片、总线频率等问题,也仅仅有这些都确定了,才干确定详细的主板。

      选择不同的CPU和主板,是每一个客户在组装电脑的时候,向装机公司提出的要求,也就是我们每一个人自己拟定的装机方案。

      在终于确定这个装机方案之前。还须要总体考虑各个配件之间的兼容性。比方:CPU和主板,假设使用Intel的CPU和AMD的主板是根本无法组装的。

    由于Intel的CPU针脚数与AMD主板提供的CPU插口不兼容。就是说假设使用Intel的CPU根本就插不到AMD的主板中,所以装机方案是总体性的。里面选择的各个配件之间是有关联的。

      对于装机project师而言。他仅仅知道组装一台电脑,须要对应的配件,可是详细使用什么样的配件,还得由客户说了算。也就是说装机project师仅仅是负责组装,而客户负责选择装配所须要的详细的配件。因此,当装机project师为不同的客户组装电脑时,仅仅须要依据客户的装机方案,去获取对应的配件,然后组装就可以。

    使用简单工厂模式的解决方式

      考虑客户的功能。须要选择自己须要的CPU和主板。然后告诉装机project师自己的选择,接下来就等着装机project师组装电脑了。

      对装机project师而言,仅仅是知道CPU和主板的接口,而不知道详细实现,非常明显能够用上简单工厂模式或工厂方法模式。为了简单,这里选用简单工厂。

    客户告诉装机project师自己的选择。然后装机project师会通过对应的工厂去获取对应的实例对象。

    源码

    CPU接口与详细实现

    publicinterface Cpu {
    publicvoid calculate();
    }
    publicclass IntelCpu implements Cpu {
    /**
         * CPU的针脚数
    */
    privateint pins = 0;
    public  IntelCpu(int pins){
    this.pins = pins;
        }
        @Override
    publicvoid calculate() {
            System.out.println("Intel CPU的针脚数:" + pins);
        }
    }
    publicclass AmdCpu implements Cpu {
    privateint pins = 0;
    public  AmdCpu(int pins){
    this.pins = pins;
        }
        @Override
    publicvoid calculate() {
            System.out.println("AMD CPU的针脚数:" + pins);
        }
    }

    主板接口与详细实现

    publicinterface Mainboard {
    publicvoid installCPU();
    }
    publicclass IntelMainboard implements Mainboard {
    /**
         * CPU插槽的孔数
    */
    privateint cpuHoles = 0;
    // 构造方法,传入CPU插槽的孔数
    public IntelMainboard(int cpuHoles){
    this.cpuHoles = cpuHoles;
        }
        @Override
    publicvoid installCPU() {
            System.out.println("Intel主板的CPU插槽孔数是:" + cpuHoles);
        }
    }
    publicclass AmdMainboard implements Mainboard {
    privateint cpuHoles = 0;
        //构造方法,传入CPU插槽的孔数
    public AmdMainboard(int cpuHoles){
    this.cpuHoles = cpuHoles;
        }
        @Override
    publicvoid installCPU() {
            System.out.println("AMD主板的CPU插槽孔数是:" + cpuHoles);
        }
    }

    CPU与主板工厂类

    publicclass CpuFactory {
    publicstatic Cpu createCpu(int type){
            Cpu cpu = null;
    if(type == 1){
                cpu = new IntelCpu(755);
            }elseif(type == 2){
                cpu = new AmdCpu(938);
            }
    return cpu;
        }
    }
    publicclass MainboardFactory {
    publicstatic Mainboard createMainboard(int type){
            Mainboard mainboard = null;
    if(type == 1){
                mainboard = new IntelMainboard(755);
            }elseif(type == 2){
                mainboard = new AmdMainboard(938);
            }
    return mainboard;
        }
    }

    装机project师类与客户类例如以下:

    publicclass ComputerEngineer {
        //定义组装机须要的CPU
    private Cpu cpu = null;
        //定义组装机须要的主板
    private Mainboard mainboard = null;
    publicvoid makeComputer(int cpuType , int mainboard){
    /**
             * 组装机器的基本步骤
    */
    //1:首先准备好装机所须要的配件
            prepareHardwares(cpuType, mainboard);
    //2:组装机器
    //3:測试机器
    //4:交付客户
        }
    privatevoid prepareHardwares(int cpuType , int mainboard){
    //这里要去准备CPU和主板的详细实现。为了演示样例简单。这里仅仅准备这两个
    //但是,装机project师并不知道怎样去创建,怎么办呢?
    //直接找对应的工厂获取
    this.cpu = CpuFactory.createCpu(cpuType);
    this.mainboard = MainboardFactory.createMainboard(mainboard);
    //測试配件是否好用
    this.cpu.calculate();
    this.mainboard.installCPU();
        }
    }
    publicclass Client {
    publicstaticvoid main(String[]args){
            ComputerEngineer cf = new ComputerEngineer();
            cf.makeComputer(1,1);
        }
    }

    执行结果例如以下:
    Intel CPU的针脚数:755
    Intel主板的CPU插槽孔数是:755
    上面的实现,尽管通过简单工厂方法攻克了:对于装机project师,仅仅知CPU和主板的接口,而不知道详细实现的问题。但另一个问题没有解决,那就是这些CPU对象和主板对象事实上是有关系的,须要相互匹配的。而上面的实现中,并没有维护这样的关联关系,CPU和主板是由客户随意选择。这是有问题的。

    比方在client调用makeComputer时,传入參数为(1,2),执行结果如下:

    Intel CPU的针脚数:755
    AMD主板的CPU插槽孔数是:938
    观察上面结果就会看出问题。客户选择的是Intel的CPU针脚数为755。而选择的主板是AMD,主板上的CPU插孔是938,根本无法组装。这就是没有维护配件之间的关系造成的。该怎么解决问题呢?

    引进抽象工厂模式

    每个模式都是针对一定问题的解决方式。抽象工厂模式与工厂方法模式的最大差别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则须要面对多个产品等级结构。

    在学习抽象工厂详细实例之前,应该明确两个重要的概念:产品族和产品等级

    所谓产品族。是指位于不同产品等级结构中,功能相关联的产品组成的家族。比方AMD的主板、芯片组、CPU组成一个家族,Intel的主板、芯片组、CPU组成一个家族。而这两个家族都来自于三个产品等级:主板、芯片组、CPU。一个等级结构是由同样的结构的产品组成,示意图例如以下:

      显然。每个产品族中含有产品的数目,与产品等级结构的数目是相等的。产品的等级结构与产品族将产品依照不同方向划分,形成一个二维的坐标系。

    横轴表示产品的等级结构,纵轴表示产品族,上图共同拥有两个产品族,分布于三个不同的产品等级结构中。仅仅要指明一个产品所处的产品族以及它所属的等级结构,就能够唯一的确定这个产品。

      上面所给出的三个不同的等级结构具有平行的结构。因此。假设採用工厂方法模式,就势必要使用三个独立的工厂等级结构来对付这三个产品等级结构。

    因为这三个产品等级结构的相似性,会导致三个平行的工厂等级结构。随着产品等级结构的数目的添加,工厂方法模式所给出的工厂等级结构的数目也会随之添加。例如以下图:

    那么,能否够使用同一个工厂等级结构来对付这些同样或者极为相似的产品等级结构呢?当然能够的。并且这就是抽象工厂模式的优点。

    同一个工厂等级结构负责三个不同产品等级结构中的产品对象的创建。

      能够看出。一个工厂等级结构能够创建出分属于不同产品等级结构的一个产品族中的全部对象。显然,这时候抽象工厂模式比简单工厂模式、工厂方法模式更有效率。相应于每个产品族都有一个详细工厂。

    而每个详细工厂负责创建属于同一个产品族,可是分属于不同等级结构的产品。

    抽象工厂模式结构

      抽象工厂模式是对象的创建模式。它是工厂方法模式的进一步推广。

      如果一个子系统须要一些产品对象,而这些产品又属于一个以上的产品等级结构。

    那么为了将消费这些产品对象的责任和创建这些产品对象的责任切割开来,能够引进抽象工厂模式。这种话。消费产品的一方不须要直接參与产品的创建工作,而仅仅须要向一个公用的工厂接口请求所须要的产品。

      通过使用抽象工厂模式。能够处理具有同样(或者相似)等级结构中的多个产品族中的产品对象的创建问题。

    例如以下图所看到的:

      因为这两个产品族的等级结构同样。因此使用同一个工厂族也能够处理这两个产品族的创建问题。这就是抽象工厂模式。

      依据产品角色的结构图。就不难给出工厂角色的结构设计图。

      能够看出。每个工厂角色都有两个工厂方法,分别负责创建分属不同产品等级结构的产品对象。下图ComputerEngineer依赖于AbstractFactory、Cpu和MainBoard三个接口

    源码

      前面演示样例中CPU接口和CPU实现类,主板接口和主板实现类。都不须要变化。

      前面演示样例中创建CPU的工厂类和创建主板的工厂类,都不再须要。

      新加的抽象工厂类和实现类:

    publicinterface AbstractFactory {
    /**
         * 创建CPU对象
    */
    public Cpu createCpu();
    /**
         * 创建主板对象
    */
    public Mainboard createMainboard();
    }
    publicclass IntelFactory implements AbstractFactory {
        @Override
    public Cpu createCpu() {
    returnnew IntelCpu(755);
        }
        @Override
    public Mainboard createMainboard() {
    returnnew IntelMainboard(755);
        }
    }
    publicclass AmdFactory implements AbstractFactory {
        @Override
    public Cpu createCpu() {
    returnnew IntelCpu(938);
        }
        @Override
    public Mainboard createMainboard() {
    returnnew IntelMainboard(938);
        }
    }

      装机project师类跟前面的实现相比,基本的变化是:从client不再传入选择CPU和主板的參数,而是直接传入客户已经选择好的产品对象。

    这样就避免了单独去选择CPU和主板所带来的兼容性问题,客户要选就是一套。就是一个系列。

    publicclass ComputerEngineer {
    private Cpu cpu = null;
    private Mainboard mainboard = null;
    publicvoid makeComputer(AbstractFactory af){
            prepareHardwares(af);
        }
    privatevoid prepareHardwares(AbstractFactory af){
    this.cpu = af.createCpu();
    this.mainboard = af.createMainboard();
    this.cpu.calculate();
    this.mainboard.installCPU();
        }
    }

    client代码:

    publicclass Client {
    publicstaticvoid main(String[]args){
    //创建装机project师对象
            ComputerEngineer cf = new ComputerEngineer();
    //客户选择并创建须要使用的产品对象
            AbstractFactory af = new IntelFactory();
    //告诉装机project师自己选择的产品,让装机project师组装电脑
            cf.makeComputer(af);
        }
    }

      抽象工厂的功能是为一系列相关对象或相互依赖的对象创建一个接口。一定要注意,这个接口内的方法不是随意堆砌的。而是一系列相关或相互依赖的方法。

    比方上面样例中的主板和CPU。都是为了组装一台电脑的相关对象。不同的装机方案,代表一种详细的电脑系列。


      因为抽象工厂定义的一系列对象一般是相关或相互依赖的,这些产品对象就构成了一个产品族。也就是抽象工厂定义了一个产品族。

    这就带来很大的灵活性。切换产品族的时候。仅仅要提供不同的抽象工厂实现就能够了。也就是说如今是以一个产品族作为一个总体被切换。

    在什么情况下应当使用抽象工厂模式

      1.一个系统不应当依赖于产品类实例怎样被创建、组合和表达的细节,这对于全部形态的工厂模式都是重要的。

    2.这个系统的产品有多于一个的产品族,而系统仅仅消费当中某一族的产品。

    3.同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。(比方:Intel主板必须使用Intel CPU、Intel芯片组)

    4.系统提供一个产品类的库。全部的产品以相同的接口出现,从而使client不依赖于实现。

    抽象工厂模式的起源

      抽象工厂模式的起源或者最早的应用,是用于创建分属于不同操作系统的视窗构建。

    比方:命令按键(Button)与文字框(Text)都是视窗构建,在UNIX操作系统的视窗环境和Windows操作系统的视窗环境中,这两个构建有不同的本地实现,它们的细节有所不同。

      在每个操作系统中,都有一个视窗构建组成的构建家族。在这里就是Button和Text组成的产品族。而每个视窗构件都构成自己的等级结构,由一个抽象角色给出抽象的功能描写叙述,而由详细子类给出不同操作系统下的详细实现。


      能够发如今上面的产品类图中,有两个产品的等级结构,各自是Button等级结构和Text等级结构。同一时候有两个产品族。也就是UNIX产品族和Windows产品族。

    UNIX产品族由UNIX Button和UNIX Text产品构成;而Windows产品族由Windows Button和Windows Text产品构成。

        系统对产品对象的创建需求由一个project的等级结构满足。当中有两个详细project角色,即UnixFactory和WindowsFactory。UnixFactory对象负责创建Unix产品族中的产品,而WindowsFactory对象负责创建Windows产品族中的产品。这就是抽象工厂模式的应用,抽象工厂模式的解决方式例如以下图:

      显然,一个系统仅仅可以在某一个操作系统的视窗环境下执行,而不能同一时候在不同的操作系统上执行。

    所以。系统实际上仅仅能消费属于同一个产品族的产品。

      在现代的应用中,抽象工厂模式的使用范围已经大大扩大了,不再要求系统仅仅能消费某一个产品族了。因此。能够不必理会前面所提到的原始用意。

    抽象工厂模式的长处

    • 分离接口和实现

      client使用抽象工厂来创建须要的对象,而client根本就不知道详细的实现是谁,client仅仅是面向产品的接口编程而已。也就是说。client从详细的产品实现中解耦。

    • 使切换产品族变得easy

      由于一个详细的工厂实现代表的是一个产品族,比方上面样例的从Intel系列到AMD系列仅仅须要切换一下详细工厂。

    • 添加产品族变的easy

      假设出现新的产品族,仅仅需新增抽象工厂的一个实现类用于处理新产品族,其他实现类都不需改变、

    抽象工厂模式的缺点

    • 不太easy扩展新的产品(不是产品族,而是产品等级,如显卡)

      假设须要给整个产品族加入一个新的产品,那么就须要改动抽象工厂,这样就会导致改动全部的工厂实现类。

  • 相关阅读:
    adb稳定性monkey测试(转载)
    Cookie、sessionStorage、localStorage的异同
    Vue-eBookReader 学习笔记(初始化部分)
    ValueError: Max value is 14 解决方案
    Chrome 报错: Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.
    Bootstrap使用方法
    Vue笔记
    3D相册 复仇者联盟
    奔跑的少年
    钟表练习 html+css实现
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/6949551.html
Copyright © 2011-2022 走看看