美文网首页设计模式
抽象工厂模式(选择产品簇)

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

作者: 幺鹿 | 来源:发表于2017-03-30 15:35 被阅读13次

    目录

    • 回顾众多工厂模式
    • 抽象工厂模式的理念
    • 抽象工厂模式与工厂方法模式的差异
    • 怎么来实现抽象工厂模式
    • 抽象工厂模式在Android源码中的应用

    回顾众多工厂模式

    简单工厂模式

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

    简单工厂模式

    工厂方法模式

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

    工厂方法模式

    抽象工厂模式的理念

    第一节的回顾,我们复习了简单工厂与工厂方法模式。由此我们也会使用 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就好了。

    相关文章

      网友评论

        本文标题:抽象工厂模式(选择产品簇)

        本文链接:https://www.haomeiwen.com/subject/whvuottx.html