一般来说,抽象工厂模式(Abstract Factory)是我们学习设计模式所要接触的第一个模式。抽象工厂所要解决的问题也比较典型,比如你有多套产品线,每套产品线都有自己的风格,并且都可以生产多个具体产品。举个例子,比如你是一名装修设计师,对于一间房子的装修,有多种风格,古典中式风格、西式风格或现代风格,而每种风格下,都有自己的壁纸样式、灯具样式以及沙发样式等等,这种情况就可以使用抽象工厂。类似的例子还有,你设计不同观感(look-and-feel)的用户界面,有windows风格,有金属风格,每种风格下都有自己的标题栏、菜单、按钮等等,对于这些具体项目的创建,使用抽象工厂就很方便。

所以,抽象工厂意图提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。我们以不同观感的用户界面的设计为例,具体解释一下如何实现。我们有两种风格,分别叫Motif和Windows,每种风格下需要创建它们的菜单栏和按钮:

  Motif Windows
Menu Motif Menu Windows Menu
Button Motif Button Windows Button

这个Demo涉及的类文件比较多,多达10个。首先是Menu和Button的接口,以及它们分别对应的实体类:

  • IMenu, Menu接口, 这是对具有不同风格的同一个Item的抽象
  • IButton, Button接口
  • MotifMenu, Motif 风格下的Menu
  • MotifButton, Motif 风格下的Button
  • WindowsMenu, Windows 风格下的Menu
  • WindowsButton, Windows 风格下的Button

其次是两个不同风格的Factory以及Factory的抽象:

  • AbstractUIFactory
  • MotifUIFactory
  • WindowsUIFactory

最后是Demo代码:

  • AbstractFactoryDemo

pic

public class AbstractFactoryDemo {
    public static void main(String[] args) {
        AbstractUIFactory motifFactory = new MotifUIFactory();
        IMenu motifMenu = motifFactory.createMenu();
        IButton motifButton = motifFactory.createButton();

        System.out.println("Below are Motif look-and-feel: ");
        System.out.printf("> %s, %s\n", motifMenu.menuName(), motifButton.buttonName());

        AbstractUIFactory windowsFactory = new WindowsUIFactory();
        IMenu windowsMenu = windowsFactory.createMenu();
        IButton windowsButton = windowsFactory.createButton();

        System.out.println("Below are Windows look-and-feel: ");
        System.out.printf("> %s, %s\n", windowsMenu.menuName(), windowsButton.buttonName());
    }
}
public interface AbstractUIFactory {
    IMenu createMenu();

    IButton createButton();
}
public interface IButton {
    String buttonName();
}
public interface IMenu {
    String menuName();
}
public class MotifButton implements IButton{
    @Override
    public String buttonName() {
        return "Motif button";
    }
}
public class MotifMenu implements IMenu{
    @Override
    public String menuName() {
        return "Motif menu";
    }
}
public class MotifUIFactory implements AbstractUIFactory{
    @Override
    public IMenu createMenu() {
        return new MotifMenu();
    }

    @Override
    public IButton createButton() {
        return new MotifButton();
    }
}
public class WindowsButton implements IButton{
    @Override
    public String buttonName() {
        return "Windows button";
    }
}
public class WindowsMenu implements IMenu{
    @Override
    public String menuName() {
        return "Windows menu";
    }
}
public class WindowsUIFactory implements AbstractUIFactory{
    @Override
    public IMenu createMenu() {
        return new WindowsMenu();
    }

    @Override
    public IButton createButton() {
        return new WindowsButton();
    }
}

最终输出结果:

Below are Motif look-and-feel: 
> Motif menu, Motif button
Below are Windows look-and-feel: 
> Windows menu, Windows button

多说一句,司马迁写历史用了一种非常聪明的方法,就是写人、写故事,这样历史就活了起来,生动了许多。学习设计模式应该也是这样。我们应该去记一个个典型的例子,通过例子去理解它背后的那套抽象的机制,而不是直接就是类图或者说解决意图,这些是很重要,但也很难理解,特别容易打击初学者的学习积极性。