设计模式之抽象工厂模式

2019-08-07 16:15:40 浏览数 (1)

简介

抽象工厂模式(Abstract Factory Pattern)隶属于设计模式中的创建型模式,用于产品族的构建。

定义

抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。

抽象工厂模式中有一个产品族的概念,理解了产品族就算是抽象工厂入门了。所谓产品族,就是一系列相关的产品对象组成的一族对象,它们是独立的同时也是互相关联的,它们共同组合在一起可以变成一个更大的产品。

角色

抽象工厂模式中存在四种角色,跟工厂方法类似,分别是抽象工厂接口,具体工厂实现,抽象产品接口,具体产品实现。

不同之处在于抽象产品接口会存在多个,而且它们之间存在依赖关系,可以组装起来。

模式结构

在工厂方法模式中,我们举了造车的例子,其实造车这个例子扩展一下也非常适合于讲解抽象工厂模式,不过为了区别起见,我们使用组装电脑来演示,大家可以考虑一下如何扩展造车的例子来适合于抽象工厂模式。

现在组装电脑不多见了,一般都是笔记本或一体机,以前大家去卖场里单独买 CPU,主板,显示器等组件可以组装出一台电脑来,在我们的系统里,我们有各种组件,用户可以选择不同的组件来组装自己喜欢的电脑。

但有一点需要注意,主板和 CPU 是需要兼容才能使用的,不是随便搭在一起就成为一台电脑了。

代码语言:javascript复制
// 产品接口
public interface Version {
    // 主板和 cpu 的型号
    public int getVersion();
}
public interface MainBoard extends Version{
    public void setCpu();
    public void run();
}
public interface Cpu extends Version {
    public void doLogic();
}

// 具体产品实现
public class GigaMainBoard implements MainBoard {
    // 假设cpu 与 主板 型号匹配才能运行
    private int version;
    private Cpu cpu;
    
    public GigaMainBoard(int version) {
        this.version = version;
    }
    
    public int getVersion() {
        return this.version;
    }
    
    public void run() {
        System.out.println("GigaMainboard is running.");
    }
    
    // cpu 在装到主板上是会有型号检查
    public void setCpu(Cpu cpu) {
        if (cpu.getVersion() != this.version) {
            throw new IllegalArgumentsException("型号不匹配");
        }
        this.cpu = cpu;
    }
}

public class OtherMainBoard implements MainBoard {
    // 假设cpu 与 主板 型号匹配才能运行
    private int version;
    private Cpu cpu;
    
    public GigaMainBoard(int version) {
        this.version = version;
    }
    
    public void run() {
        System.out.println("OtherMainBoard is running");
    }
    
    public int getVersion() {
        return this.version;
    }
    
    // cpu 在装到主板上是会有型号检查
    public void setCpu(Cpu cpu) {
        if (cpu.getVersion() != this.version) {
            throw new IllegalArgumentsException("型号不匹配");
        }
        this.cpu = cpu;
    }
}

public class IntelCpu implements Cpu {
    private int version;
    public IntelCpu(int version) {
        this.version = version;
    }
    
    public int getVersion() {
        return this.version;
    }
    
    public void doLogic() {
        System.out.println("IntelCpu is doing logic");
    }
}

public class AmdCpu implements Cpu {
    private int version;
    public IntelCpu(int version) {
        this.version = version;
    }
    
    public int getVersion() {
        return this.version;
    }
    
    public void doLogic() {
        System.out.println("AmdCpu is doing logic");
    }
}

如果由用户自行组装,很可能由于用户不明白型号的问题导致组装起来的电脑不能正常运行,而且这些细节也不需要用户操心。

此时我们的抽象工厂模式就登场了,抽象工厂就像是卖场工作人员提供给你的几种套餐一样,定义了组装电脑的套餐,用户可以直接使用套餐里的组件然后组装成电脑,这样就绝对不会出现兼容问题了。

代码语言:javascript复制
// 抽象工厂接口
public interface ComponentAbstractFactory {
    public MainBoard getMainBoard();
    public Cpu getCpu();
}

// 具体工厂实现

// 套餐1
public class Combo1 implements ComponentAbstractFactory {
    public MainBoard getMainBoard() {
        return new GigaMainBoard(100);
    }
    
    public Cpu getCpu() {
        return new IntelCpu(100);
    }
}

// 套餐2
public class Combo2 implements ComponentAbstractFactory {
    public MainBoard getMainBoard() {
        return new OtherMainBoard(1000);
    }
    
    public Cpu getCpu() {
        return new AmdCpu(1000);
    }
}

有了套餐用户不需要担心不兼容,可以愉快的组装电脑然后使用了。

代码语言:javascript复制
// 电脑接口
public interface Computer {
    // 装好主板,cpu 装在主板上
    public void setMainBoard(MainBoard mainBoard);
    //其他的需要放置的东西
    public void otherSettings();
    
    public void run();
}
// 组装电脑
public class ComboComputer implements Computer {
    // 装好主板,cpu 装在主板上
    public void setMainBoard(MainBoard mainBoard){
        //..
    }
    //其他的需要设置的东西
    public void otherSettings() {
        //....
    }
    
    public void run() {
        mainBoard.run();
    }
}
public class Client {
    public static void main(String[] args) {
        Computer computer = new ComboComputer();
        ComponentAbstractFactory factory = new ComponentAbstractFactory();
        
        MainBoard mainBoard = factory.getMainBoard();
        Cpu cpu = factory.getCpu();
        mainBoard.setCpu(cpu);
        
        computer.setMainBoard(mainBoard);
        
        computer.run();
    }
}

大家有没有发现,用户为了组装一台电脑出来,虽然不需要了解cpu 型号之类的细节的,其实还是要了解cpu 怎么装到主板上,主板又怎么装到电脑上,然后才能得到一台完整的电脑,这里又可以用到前一节说的工厂方法模式来帮助用户将电脑组装起来了。

使用场景

  1. 适合于产品之间相互关联、相互依赖且相互约束的地方
  2. 需要动态切换产品族的地方

优缺点

优点:

  1. 抽象工厂模式将产品族的依赖与约束关系放到抽象工厂中,便于管理。
  2. 职责解耦,用户不需要关心一堆自己不关心的细节,由抽象工厂来负责组件的创建
  3. 切换产品族容易,只需要增加一个具体工厂实现,客户端选择另一个套餐就可以了

缺点:

  1. 抽象工厂模式类增加的速度很快,有一个产品族就需要增加一个具体工厂实现,比较繁琐
  2. 产品族难以扩展产品。当产品族中增加一个产品时,抽象工厂接口中需要增加一个函数,对应的所有具体工厂实现都需要修改,修改放大严重。
  3. 抽象工厂并未完全屏蔽创建细节,给出的都是组件。对于这种情况可以结合工厂模式或简单工厂模式一起使用。

最佳实践

  1. 大家应该已经发现了,其实抽象工厂模式如果只有一个组件的话,其实是退化到了工厂方法模式,也就是没有了产品族的概念,只剩一个产品了,因此简单工厂,工厂方法,抽象工厂这三者之间是有内在联系的,区别只是产品的复杂度。
  2. 抽象工厂的本质是选择产品族,因此大家可以根据这个特征来识别是否可以应用抽象工厂。

0 人点赞