首页 > 代码库 > 设计模式学习03—抽象工厂模式

设计模式学习03—抽象工厂模式

1、动机与定义

     工厂模式中,一个工厂只能提供一个或一类产品,当产品种类较多,形成产品系列(比如我们要创建跨平台的按钮,菜单,文本框等等一系列GUI控件:
     
     单纯使用工厂模式会产生大量工厂,而且后期维护也不方便,我们可以从产品中找到规律,如果产品等级相对固定,以后只会新增产品族,那么我们就可以把整个产品族放到一个工厂创建,以后新增其他系统产品族也非常方便,如下图:
     
     这种模式就是抽象工厂,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。

2、结构与类图

     通用类图如下:
     文字开头那个创建GUI的例子类图如下,此处只创建了Button和TextArea:

     
     产品类和接口代码如下(为了演示方便,省略逻辑等等):
interface Button {

}

interface TextArea {

}

class WindowButton implements Button {

}

class LinuxButton implements Button {

}

class WindowTextArea implements TextArea {

}

class LinuxTextArea implements TextArea {

}
     工厂类代码:
public interface IGuiFactory {
    // 产品Button创建方法
    Button createButton();

    // 产品TextArea创建方法
    TextArea createTextArea();

    // 有n个产品等级,就n个创建方法
}

class LinuxGuiFactory implements IGuiFactory {

    @Override
    public Button createButton() {
        return new LinuxButton();
    }

    @Override
    public TextArea createTextArea() {
        return new LinuxTextArea();
    }
}

class WindowsGuiFactory implements IGuiFactory {

    @Override
    public Button createButton() {
        return new WindowButton();
    }

    @Override
    public TextArea createTextArea() {
        return new WindowTextArea();
    }

}
      客户端代码:
public class Client {

    public static void main(String[] args) {
        // 为了演示,使用new方式创建工厂,最好做成单例的或先初始化好
        IGuiFactory factory = new WindowsGuiFactory();
        Button button = factory.createButton();
        TextArea textArea = factory.createTextArea();
        System. out.println(button);
        System. out.println(textArea);
    }
}
     有多少个产品族,就创建多少个工厂实现,有多少个产品等级,工厂中就要对外提供多少个创建产品方法。

3、适用场景及效果(优缺点)

     其实工厂模式的优点,抽象工厂基本都有的,这里说说抽象工厂特有的优点:
     1、约束产品族内部元素,可以控制产品族内部各产品间关系,数量等,比如产品族内的产品有A、B,需要生产1个A时生成2个B,此时因为他们都在一个工厂实现类中,所以非常容易控制。
     2、扩展产品族和工厂非常容易,比如上面例子中要扩展一类系统GUI,比如说android的,增加一套GUI控件和android的控件工厂就行。
     3、减少客户端判断,当一个产品族中的产品放到一起工作时,抽象工厂能确保客户端始终只是用同一个产品族中的产品,而不用根据当前环境判断使用哪个产品了,比如使用了WindowsGuiFactory,使用控件时,客户端完全不用判断是哪种操作系统了,只需要用WindowsGuiFactory获取就行了。
     缺点也是显而易见的,最大缺点就是扩展产品等级非常困难,还是上面的例子,要新增一种控件,要把所有的工厂都要加一个方法,严重违反了开闭原则,修改接口,所有客户端代码也要影响,所以抽象工厂一定要确保产品等级无变化或非常少的变化。
     其实这也是设计上的一种权衡,有的时候要想让一方面扩展、使用非常容易,就会导致另一方面非常难扩展,如果业务上有这样的倾斜性,我们就可以做这种设计,没有绝对好的设计,只有适合的设计。
     适用场景,当系统所提供的工厂所需生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构中属于不同类型的具体产品时我们会考虑使用抽象工厂模式。结合上面的优缺点,举几个具体的适用场景:
     1、系统中有1个或多个产品族时,而每次只是用1个产品族,如有些桌面程序需要更换主题,皮肤等功能;
     2、系统提供一个产品类的库时,所有产品以接口方式出现,可以考虑使用抽象工厂;
     3、系统同一类产品族要放到一起使用,关联到一起这种约束最好在设计中体现,可以使用抽象工厂。

4、模式扩展

     1、工厂模式转化,当产品等级只有1个时,抽象工厂可以退化成普通工厂,如果实现比较简单,产品类不多时,普通工厂退化成简单工厂。
     2、工厂组合,抽象工厂、普通工厂当产品过多时,都会生成很多工厂类,可以考虑使用简单工厂再将工厂类封装一下,客户端获取工厂时也会更方便。其实,工厂模式是程序中用的最广的一种模式,经常和其他模式组合使用,没有哪个模式能100%使用某个环境,只有模式的灵活变通,多模式混合才会产生优秀的设计。