设计模式-工厂方法模式

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

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

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

模式定义#

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

设计原则#

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

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

工厂方法模式对象依赖图

模式类图#

工厂方法模式类图

工厂方法模式实例#

问题描述:

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

问题的解决方案类图#

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

首先定义产品接口-披萨

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

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

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

定义工厂接口

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

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

Copy
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; } }
Copy
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; } }

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

Copy
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("乳酪比萨"); } }

运行结果

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

JDK中的工厂方法模式#

Collection接口中的一段代码:

Copy
Iterator<E> iterator();

继承Collction的List、Set等中有:

Copy
Iterator<E> iterator();

接下来再看ArrayList中的代码:

Copy
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接口

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

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

总结#

优点:

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

缺点:

  • 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销
  • 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类
  • 一个具体工厂只能创建一种具体产品
posted @   武培轩  阅读(535)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示
CONTENTS