欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

设计模式-工厂方法模式

程序员文章站 2022-03-10 17:39:50
在简单工厂模式中,我们发现存在很多问题: 由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。 要新增产品类的时候,就要修改工厂类的代码,违反了开放封闭原则(对扩展的开放,对修改的关闭)。 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。 为了解决上... ......

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

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

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

模式定义

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

设计原则

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

听起来像是针对接口编程,不针对实现编程,但是这个原则说明了:不能让高层组件依赖底层组件,而且,不管高层或底层组件,两者都应该依赖于抽象。例如,下图中 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源码的内容,满足了开-闭原则。

总结

优点:

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

缺点:

  • 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销
  • 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类
  • 一个具体工厂只能创建一种具体产品