• 设计模式-工厂方法模式


    简单工厂模式中,我们发现存在很多问题:

    • 由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
    • 要新增产品类的时候,就要修改工厂类的代码,违反了开放封闭原则(对扩展的开放,对修改的关闭)。
    • 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。

    为了解决上述的问题,我们学习一种新的设计模式:工厂方法模式。

    模式定义

    定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

    设计原则

    依赖倒置原则:要依赖抽象,不要依赖具体类。

    听起来像是针对接口编程,不针对实现编程,但是这个原则说明了:不能让高层组件依赖底层组件,而且,不管高层或底层组件,两者都应该依赖于抽象。例如,下图中 Pizza 是抽象类,PizzaStore 和 Pizza 子类都依赖于 Pizza 这个抽象类。

    工厂方法模式对象依赖图

    模式类图

    工厂方法模式类图

    工厂方法模式实例

    问题描述:

    每个地区的 PizzaStore 卖的 Pizza 虽然种类相同,但是都有自己的风味。一个客户点了纽约的 cheese 种类的 Pizza 和在芝加哥点的相同种类的 Pizza 是不同的。要求设计出满足条件的 PizzaStore。

    问题的解决方案类图

    PizzaStore 有 orderPizza() 方法,顾客可以用它来下单。下单之后需要先使用 createPizza() 来制作 Pizza,这里的 createPizza() 就是 factoryMethod(),不同的 PizzaStore 子类实现了不同的 createPizza()。
    工厂方法模式类图

    首先定义产品接口-披萨

    package com.wpx.factorymethod;
    
    /**
     * 定义产品接口-披萨
     */
    public interface Pizza {
        public void make();
    }
    

    再定义具体的产品类-纽约风味奶酪披萨、纽约风味蔬菜披萨、芝加哥风味奶酪披萨、芝加哥风味蔬菜披萨

    package com.wpx.factorymethod;
    
    /**
     * 具体的产品类,实现产品接口-纽约风味奶酪披萨
     */
    public class NYStyleCheesePizza implements Pizza{
        @Override
        public void make() {
            System.out.println("制作纽约风味奶酪披萨");
        }
    }
    
    
    package com.wpx.factorymethod;
    
    /**
     * 具体的产品类,实现产品接口-纽约风味蔬菜披萨
     */
    public class NYStyleVeggiePizza implements Pizza {
        @Override
        public void make() {
            System.out.println("制作纽约风味蔬菜披萨");
        }
    }
    
    
    package com.wpx.factorymethod;
    
    /**
     * 具体的产品类,实现产品接口-芝加哥风味奶酪披萨
     */
    public class ChicagoStyleCheesePizza implements Pizza{
        @Override
        public void make() {
            System.out.println("制作芝加哥风味奶酪披萨");
        }
    }
    
    package com.wpx.factorymethod;
    
    /**
     * 具体的产品类,实现产品接口-芝加哥风味蔬菜披萨
     */
    public class ChicagoStyleVeggiePizza implements Pizza{
        @Override
        public void make() {
            System.out.println("制作芝加哥风味蔬菜披萨");
        }
    }
    
    

    定义工厂接口

    package com.wpx.factorymethod;
    
    /**
     * 定义工厂接口
     */
    public interface PizzaStore {
        public Pizza orderPizza(String item);
    }
    
    

    接着定义两个具体的工厂类,实现工厂接口

    package com.wpx.factorymethod;
    
    /**
     * 具体的工厂类,实现工厂接口-纽约工厂
     */
    public class NYPizzaStore implements PizzaStore {
        @Override
        public Pizza orderPizza(String item) {
            Pizza pizza = null;
            if (item.equals("乳酪比萨")) {
                pizza = new NYStyleCheesePizza();
            } else if (item.equals("蔬菜披萨")) {
                pizza = new NYStyleVeggiePizza();
            } else {
                throw new UnsupportedOperationException();
            }
            pizza.make();
            return pizza;
        }
    }
    
    package com.wpx.factorymethod;
    
    /**
     * 具体的工厂类,实现工厂接口-芝加哥工厂
     */
    public class ChicagoPizzaStore implements PizzaStore {
        @Override
        public Pizza orderPizza(String item) {
            Pizza pizza = null;
            if (item.equals("乳酪比萨")) {
                pizza = new ChicagoStyleCheesePizza();
            } else if (item.equals("蔬菜披萨")) {
                pizza = new ChicagoStyleVeggiePizza();
            } else {
                throw new UnsupportedOperationException();
            }
            pizza.make();
            return pizza;
        }
    }
    
    

    最后对工厂方法模式进行测试,先从纽约工厂那里来一份乳酪披萨,再从芝加哥工厂订一份乳酪披萨。

    package com.wpx.factorymethod;
    
    /**
     * 测试工厂方法模式
     */
    public class PizzaDemo {
        public static void main(String[] args) {
            PizzaStore nyStore = new NYPizzaStore();
            nyStore.orderPizza("乳酪比萨");
            PizzaStore chicagoStore = new ChicagoPizzaStore();
            chicagoStore.orderPizza("乳酪比萨");
        }
    }
    
    

    运行结果

    制作纽约风味奶酪披萨
    制作芝加哥风味奶酪披萨
    
    Process finished with exit code 0
    
    

    JDK中的工厂方法模式

    Collection接口中的一段代码:

    Iterator<E> iterator();  
    

    继承Collction的List、Set等中有:

    Iterator<E> iterator();  
    

    接下来再看ArrayList中的代码:

        public Iterator<E> iterator() {
            return new Itr();
        }
    
        private class Itr implements Iterator<E> {
            int cursor;       // index of next element to return
            int lastRet = -1; // index of last element returned; -1 if no such
            int expectedModCount = modCount;
    
            public boolean hasNext() {
                return cursor != size;
            }
    
            @SuppressWarnings("unchecked")
            public E next() {
                checkForComodification();
                int i = cursor;
                if (i >= size)
                    throw new NoSuchElementException();
                Object[] elementData = ArrayList.this.elementData;
                if (i >= elementData.length)
                    throw new ConcurrentModificationException();
                cursor = i + 1;
                return (E) elementData[lastRet = i];
            }
    
            public void remove() {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    ArrayList.this.remove(lastRet);
                    cursor = lastRet;
                    lastRet = -1;
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
    
            final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
        }
    
    

    Itr类实现了Interator接口

    public interface Iterator<E> {
    
        boolean hasNext();
    
        E next();
    
        void remove();
    }
    

    通过查看JDK源码,我们就会明白这里是怎么应用的工厂方法模式了其中Iterator是抽象产品角色,Itr是Iterator下面的一个具体产品角色,List类应该是抽象工厂角色,ArrayList应该是具体工厂角色。如果我们需要在List下自定义一个集合类并给出相应的迭代方式,那么我们只需要添加一个实现List接口的集合类,然后在增加一个迭代类实现Iterator接口就可以了。并不需要去修改JDK源码的内容,满足了开-闭原则。

    总结

    优点:

    • 更符合开-闭原则:新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可
    • 符合单一职责原则:每个具体工厂类只负责创建对应的产品
    • 不使用静态工厂方法,可以形成基于继承的等级结构

    缺点:

    • 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销
    • 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类
    • 一个具体工厂只能创建一种具体产品
  • 相关阅读:
    java网络编程【b站狂神课程笔记】
    算法设计与分析
    NP问题/NP完全问题(NP-complete problem)如何判断是否是NP完全问题
    递归的三部解题曲 关联leetcode 104. 二叉树最大深度练习
    修改typora偏好设置实现自动上传图片 关联PicGo + Gitee(码云) + typora实现markdown图床
    Typescript常见面试题
    INTEL Trusted Execution Technology (TXT) -- 基本原理
    北京大学肖臻老师《区块链技术与应用》笔记
    JavaGUI编程之贪吃蛇小游戏原码
    LeetCode 21.合并两个有序链表
  • 原文地址:https://www.cnblogs.com/wupeixuan/p/8647003.html
Copyright © 2020-2023  润新知