抽象工厂模式(选择产品簇)

回顾众多工厂模式

简单工厂模式

对简单工厂模式还不了解的可以查看下我的历史文章 简单工厂模式,简单工厂模式的核心是使用工厂实现选择创建产品实现,这应该很好理解。

简单工厂模式

工厂方法模式

对工厂方法模式还不了解的可以查看下我的历史文工厂方法模式。工厂方法模式的核心是将选择创建产品实现的过程,延迟到子类去完成,这也是相对比较好理解的模式。

工厂方法模式

抽象工厂模式的理念

第一节的回顾,我们复习了简单工厂与工厂方法模式。由此我们也会使用 UML 图线画出对抽象工厂模式的理解,然后我们在对比三张 UML 图来分析他们之间的设计是如何演进或取舍的。

抽象工厂模式

抽象工厂模式强调了产品簇的概念,那么什么是产品簇呢?在抽象工厂模式中,产品簇是指工厂生产出的产品们之间彼此具备强关联。比如:AK47工厂生产的 AK47步枪、AK47专配的子弹,一旦 AK47装错了子弹是无法正常开枪的(甚至会炸膛)。


抽象工厂模式与工厂方法模式的差异

刚开始接触抽象工厂模式的时候,我就是不能理解抽象工厂模式与工厂方法模式的区别,也许屏幕前的你也有一样的困惑。但经过一段时间时间的思考与实践,我逐渐的明白它们之间的差异。

移动手机大卖场

工厂方法模式生产产品的形式就像 移动手机大卖场(上图)。卖场里面只卖手机,这些手机里面可能有 三星、华为、OPPO、金立、苹果、HTC、Nokia 等类别,但无论如何它都没有跳出手机的范畴。因此想买手机的客户,直接去大卖场总能挑选到心仪的手机,因为手机(工厂生产的产品实例)太多了。

苹果零售店

而抽象工厂模式生产产品的形式则更像是苹果零售店(上图),除了卖手机之外还提供Mac、路由器、耳机、iPad、iPod 等电子产品。作为一个对品质(逼格)有要求的客户,会直接去苹果零售店里购买手机,一时兴起再买一台 Mac 也是可以的。

无论手机大卖场苹果零售店都是可以买到手机的,但是针对的客户群体显然是不一样的。同样的,工厂方法模式 与 抽象工厂模式 针对的客户群体也是不一样的,不过它们都拥有 生产产品 这一职责。

  • 工厂方法模式:让单一产品线上的产品更易被扩展
  • 抽象工厂模式:让多条 {单一产品线上的产品更易被扩展} 的同时,能够让多条线上的产品保持约束

工厂方法&抽象工厂

如上图,横向有两条产品线(手机、电脑),纵向有三个产品簇(苹果、三星、小米)。当我们不需要考虑产品簇时,为工厂方法模式。反之,就是抽象工厂模式。


怎么来实现抽象工厂模式

目录

可以发现在目录层级上与工厂方法模式并没有区别,差异在工厂的组织形式上。

先定义产品接口

public interface IPhone {

    void screen();

    void micro();

    void battery();

    void usb();

    // XXX just for test
    void printLog();
}

产品实现类

苹果手机,及电脑的实现类就省略了。

public class XiaoMiPhoneImpl implements IPhone {
    private String TAG = "小米手机";

    @Override
    public void screen() {
        System.out.println(TAG + "屏幕");
    }

    @Override
    public void micro() {
        System.out.println(TAG + "麦克风");
    }

    @Override
    public void battery() {
        System.out.println(TAG + "电池");
    }

    @Override
    public void usb() {
        System.out.println(TAG + "usb插口");
    }

    @Override
    public void printLog() {
        screen();
        micro();
        battery();
        usb();
    }

}

定义工厂接口

实际生活中可能无法提取工厂接口,毕竟每个工厂生产的产品不可能都保持一致。在这里定义接口的目的是想强调:这是纵向的产品簇。

public interface IFactory {

    void createPhone();

    void createComputer();
}

定义小米工厂

public class XiaoMiFactoryImpl implements IFactory {
    @Override
    public void createPhone() {
        IPhone phone = new XiaoMiPhoneImpl();
        phone.printLog();
    }

    @Override
    public void createComputer() {
        IComputer computer = new XiaoMiComputerImpl();
        computer.printLog();
    }
}

客户端调用测试

public class Client {

    public static void main(String[] args) {

        IFactory factory = new XiaoMiFactoryImpl();
        factory.createPhone();
        factory.createComputer();

    }
}

测试结果

小米手机屏幕
小米手机麦克风
小米手机电池
小米手机usb插口
小米电脑屏幕
小米电脑键盘
小米电脑鼠标
小米电脑usb

在理解了思想的前提下,编写这些代码是相当简单的。

抽象工厂模式在Android源码中的应用

com.android.internal.policy.IPolicy

IPolicy 是产生窗口屏幕相关对象的抽象接口,在 android 源码中,Policy 是 IPolicy 的唯一一个实现,用于生成对象集合。Policy 创建了一系列的对象(PhoneWindow、PhoneLayoutInflater、PhoneWindowManager、PhoneFallbackEventHandler)。从UML图就可以看出,IPolicy算是一个典型的抽象工厂,只不过在源码中只有一个具体的工厂实现。

另外Policy的实现还有一点特殊的地方,它使用static域将他需要创建的对象都预先load出来,也就是说当虚拟机加载Policy类的时候,就会加载它创建的对象的class。

定义工厂接口

public interface IPolicy {
    public Window makeNewWindow(Context context);

    public LayoutInflater makeNewLayoutInflater(Context context);

    public WindowManagerPolicy makeNewWindowManager();

    public FallbackEventHandler makeNewFallbackEventHandler(Context context);
}

定义工厂实现类

public class Policy implements IPolicy {
    private static final String TAG = "PhonePolicy";

    private static final String[] preload_classes = {
        "com.android.internal.policy.impl.PhoneLayoutInflater",
        "com.android.internal.policy.impl.PhoneWindow",
        "com.android.internal.policy.impl.PhoneWindow$1",
        "com.android.internal.policy.impl.PhoneWindow$DialogMenuCallback",
        "com.android.internal.policy.impl.PhoneWindow$DecorView",
        "com.android.internal.policy.impl.PhoneWindow$PanelFeatureState",
        "com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState",
    };

    static {
        // For performance reasons, preload some policy specific classes when
        // the policy gets loaded.
        for (String s : preload_classes) {
            try {
                Class.forName(s);
            } catch (ClassNotFoundException ex) {
                Log.e(TAG, "Could not preload class for phone policy: " + s);
            }
        }
    }

    public Window makeNewWindow(Context context) {
        return new PhoneWindow(context);
    }

    public LayoutInflater makeNewLayoutInflater(Context context) {
        return new PhoneLayoutInflater(context);
    }

    public WindowManagerPolicy makeNewWindowManager() {
        return new PhoneWindowManager();
    }

    public FallbackEventHandler makeNewFallbackEventHandler(Context context) {
        return new PhoneFallbackEventHandler(context);
    }
}

定义产品类 (Window

代码太多,咱就不贴了。

定义产品实现类(PhoneWindow

代码太多,咱就不贴了。

为什么不用4个单独的工厂(工厂方法模式)去实现呢?

其实四种不同的产品(Window,WindowManagerPolicy,LayoutInflater,FallbackEventHandler),如果用四个工厂方法也是可以的,但是会缺乏将本来应该有相互关联的产品拆分开了,抽象工厂一定程度上提高了它们之间的内聚。

假如除了Phone外,如果需要加入新的产品簇(比如说TV,智能手表),那么只需要创建一个对应Policy,以及对应的产品系列,然后将PolicyManager的IPolicy指向新产品簇的Policy就好了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏haifeiWu与他朋友们的专栏

复杂业务下向Mysql导入30万条数据代码优化的踩坑记录

从毕业到现在第一次接触到超过30万条数据导入MySQL的场景(有点low),就是在顺丰公司接入我司EMM产品时需要将AD中的员工数据导入MySQL中,因此楼主负...

26440
来自专栏儿童编程

天干地支五行八卦的对应关系

18790
来自专栏儿童编程

我不是算命先生,却对占卜有了疑惑——如何论证“占卜前提”的正确与否

事出有因,我对《周易》感兴趣了很多年。只是觉得特别有趣,断断续续学习了一些皮毛。这几天又偶然接触到了《梅花易数》,觉得很是精彩,将五行八卦天干地支都串联了起来。...

13810
来自专栏华章科技

穿越十年后看互联网+:家电行业的金矿在哪里?

现在市场上炒得火热的智能家居未来出路在何方?做智能家居的创业者应该注意哪些机会?传统家电厂商又到底如何借助互联网进行转型?本文以智能空调为例,用故事的形式,提前...

8210
来自专栏儿童编程

一张图理清《梅花易数》梗概

学《易经》的目的不一定是为了卜卦,但是了解卜卦绝对能够让你更好地了解易学。今天用一张思维导图对《梅花易数》的主要内容进行概括,希望能够给学友们提供帮助。

30840
来自专栏Ken的杂谈

【系统设置】CentOS 修改机器名

17230
来自专栏FSociety

SQL中GROUP BY用法示例

GROUP BY我们可以先从字面上来理解,GROUP表示分组,BY后面写字段名,就表示根据哪个字段进行分组,如果有用Excel比较多的话,GROUP BY比较类...

5.1K20
来自专栏儿童编程

《动物魔法学校》儿童学编程Scratch之“外观”部分

导读:本文通过一个案例《动物魔法学校》来学习Scratch语言的“外观”部分。之后通过一系列其他功能的综合运用对作品功能进行了扩展。

18640
来自专栏儿童编程

儿童创造力教育与编程教育的碰撞——MIT雷斯尼克教授最新理论梗概

儿童编程教育已经在我国各一线二线城市疯狂出现,颇有“烂大街”的趋势。我们不禁要问很多很多问题:

21770
来自专栏儿童编程

声音功能让儿童编程更有创造性

导读:Scratch中声音功能非常强大,除了常规的音效,你甚至可以模拟各种乐器的各个发音、设置节拍、休止……如果你愿意,甚至可以用它创作一个交响乐。我们可以引导...

13440

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励