内容简介:上一篇文章详细学习了单例模式的多种写法,今天来学习一下如下三种模式:简单工厂、工厂方法、抽象工厂模式,其实简单工厂模式不属于 GOF 23 种设计模式,不过它实现简单,在有些场景下它也比较适用,所以就首先来看一下它。通常我们使用 new 关键字就可以创建对象,为什么还要使用工厂模式呢?我们以下面这个例子来看一下。如果有一个手机店,出售 IPhone、Samsung、Huawei 品牌的手机。
上一篇文章详细学习了单例模式的多种写法,今天来学习一下如下三种模式:简单工厂、工厂方法、抽象工厂模式,其实简单工厂模式不属于 GOF 23 种设计模式,不过它实现简单,在有些场景下它也比较适用,所以就首先来看一下它。
简单工厂模式
通常我们使用 new 关键字就可以创建对象,为什么还要使用工厂模式呢?我们以下面这个例子来看一下。
如果有一个手机店,出售 IPhone、Samsung、Huawei 品牌的手机。
public class Phone { public void pay() {} public void box() {} } class IPhone extends Phone { } class Samsung extends Phone { } class Huawei extends Phone { } 复制代码
顾客在购买手机的代码可以这样写:
public class PhoneStore { public Phone buyPhone(String type) { Phone phone = null; if ("Iphone".equals(type)) { phone = new IPhone(); } else if ("Samsung".equals(type)) { phone = new Samsung(); } else if ("Huawei ".equals(type)) { phone = new Huawei(); } phone.pay(); phone.box(); return phone; } } 复制代码
如果店铺想要增加竞争力,又添加了几种手机品牌,就需要去修改 buyPhone 方法,在其中继续添加 if-else 语句。
也就是说,如果代码有变化或扩展,就必须重新修改该方法,这就违反了对扩展开放、对修改关闭的原则。而且这样修改对于系统来说,将难以维护和更新。
其实,我们可以将创建对象的代码移到另一个对象,封装成一个工厂类,在添加或改变手机的品牌时,只需要修改该工厂类即可:
public class SimplePhoneFactory { public static Phone createPhone(String type) { Phone phone = null; if ("Iphone".equals(type)) { phone = new IPhone(); } else if ("Samsung".equals(type)) { phone = new Samsung(); } else if ("Huawei ".equals(type)) { phone = new Huawei(); } return phone; } } 复制代码
而 PhoneStore 的代码就可以修改为:
public class PhoneStore { public Phone buyPhone(String type) { Phone phone = SimplePhoneFactory.createPhone(type); phone.pay(); phone.box(); return phone; } } 复制代码
上述模式就是简单工厂模式,也可以利用静态方法来定义工厂,这称为静态工厂。
我们来看一下它的 UML 图:
下面来总结一下,简单工厂模式有哪些优缺点。
优点:
- 如果一个调用者想创建一个对象,只要知道其名称就可以了。
- 如果想增加一个产品,只要实现一个新的扩展自工厂类的子类就可以了。
- 它屏蔽了产品的具体实现,调用者只需要关心产品的接口。
但它也有一些缺点:
- 每次增加产品时,都需要增加一个产品类,使得系统中的类太多,增加了系统的复杂度。
简单工厂模式具体实践
Calendar#createCalendar
该方法部分源码如下:
private static Calendar createCalendar(TimeZone zone, Locale aLocale) ··· if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") { cal = new BuddhistCalendar(zone, aLocale); } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja" && aLocale.getCountry() == "JP") { cal = new JapaneseImperialCalendar(zone, aLocale); } else { cal = new GregorianCalendar(zone, aLocale); } } 复制代码
这里会根据传入的语言和国家来决定生成什么 Calendar。
工厂方法模式
如果手机店规模比较大,希望开设 IPhone、Samsung、Huawei 专卖分店(假设分店自己制作手机),这应该如何扩展呢?
由于购买手机的过程类似,都需要付款、打包;而分店则需要生产其相应品牌的手机。我们可以将 PhoneStore 修改为抽象类 ,将 SimplePhoneFactory 的 createPhone 方法改为抽象方法,放置到 AbstractPhoneStore 中。
public abstract class PhoneStore { public Phone buyPhone() { Phone phone = createPhone(); phone.pay(); phone.box(); return phone; } protected abstract Phone createPhone(); } 复制代码
IPhone、Samsung、Huawei 三种产品分别如下:
public class Phone { public void pay() { } public void box() { } } class IPhone extends Phone { } class Samsung extends Phone { } class Huawei extends Phone { } 复制代码
三种产品对应的 IPhone、Samsung、Huawei 三家分店,它们的具体实现如下:
public class IPhoneStore extends PhoneStore { @Override protected Phone createPhone() { return new IPhone(); } } public class SamsungStore extends PhoneStore { @Override protected Phone createPhone() { return new Samsung(); } } public class HuaweiStore extends PhoneStore { @Override protected Phone createPhone() { return new Huawei(); } } 复制代码
如果我们要 IPhone 手机,代码可以如下:
public class Client { public static void main(String[] args) { PhoneStore phoneStore = new IPhoneStore(); Phone phone = phoneStore.buyPhone(); // phone 为 IPhone } } 复制代码
上述这种模式就是工厂方法模式,它会定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。例如这里创建了一个 PhoneStore 抽象类,但实际上由 IPhoneStore 来决定实例化哪个 Phone 的实现类。
我们可以看到工厂方法模式包括了四个角色:
- Product:抽象产品,对应上面的 Phone;
- ConcreteProduct:具体产品,对应 IPhone、Samsung、Huawei 等;
- Factory:抽象工厂,对应 PhoneStore;
- ConcreteFactory:具体工厂,对应 IPhoneStore、SamsungStore、HuaweiStore。
它的 UML 图如下:
下面总结一下工厂方法模式的优点:
- 用户只需要关心所需产品对应的工厂,而无需关心创建产品的细节;
- 在系统中加入新产品时,无需修改抽象工厂和抽象产品的接口,也无需修改其他的具体工厂和具体产品,只需要添加一个具体的工厂和具体产品就可以了。
缺点:
- 每添加一个新产品就要添加对应的工厂和产品,造成系统中类的个数太多,增加了系统的复杂度。
- 考虑系统的扩展性,需要引入抽象层,增加了系统的抽象性,系统实现的难度也加大。
简单工厂模式与工厂方法模式之间的区别如下:
- 在简单工厂中,是将对象的创建封装在另一个类中;
- 而在工厂方法中,它创建了一个框架,由子类来决定创建哪个对象。
工厂方法模式具体实践
Java 集合的 iterator 方法就是一个工厂方法。部分集合的 UML 图如下:
抽象工厂
该实例中抽象工厂就是 Iterable 接口:
public interface Iterable<T> { Iterator<T> iterator(); } 复制代码
具体工厂
具体工厂在 Java 集合中非常多,这里举两个例子,例如在 ArrayList 中的实现:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable public Iterator<E> iterator() { return new Itr(); } } 复制代码
在 HashMap 中的实现中,entrySet 方法返回一个 EntrySet 对象:
final class EntrySet extends AbstractSet<Map.Entry<K,V>> { public final Iterator<Map.Entry<K,V>> iterator() { return new EntryIterator(); } } 复制代码
抽象产品
抽象产品就是 Iterator 接口
public interface Iterator<E> { boolean hasNext(); E next(); } 复制代码
具体产品
这里的具体产品,以上面说的 ArrayList 中的 Itr 和 EntrySet 中的 EntryIterator 为例。
Itr 对象如下:
private class Itr implements Iterator<E> { public boolean hasNext() { ··· } public E next() { ··· } } 复制代码
EntryIterator 对象如下:
abstract class HashIterator { public final boolean hasNext() { ··· } final Node<K,V> nextNode() { ··· } } final class EntryIterator extends HashIterator implements Iterator<Map.Entry<K,V>> { public final Map.Entry<K,V> next() { return nextNode(); } } 复制代码
抽象工厂模式
如果想要改变商业模式,三家专卖店内不仅可以卖 IPhone、Sansung、Huawei 三种品牌的手机,也可以卖相应品牌的电脑(假设分店自己生产手机、电脑),这应该如何设计呢?(这个例子不太符合实际情况,不过能说明抽象工厂模式的含义,凑合看吧)
店铺可以如下设计:
public abstract class PhoneStore { public Phone buyPhone() { Phone phone = createPhone(); phone.pay(); phone.box(); return phone; } public Computer buyComputer() { Computer computer = createComputer(); computer.pay(); computer.pack(); return computer; } protected abstract Phone createPhone(); protected abstract Computer createComputer(); } 复制代码
三种品牌的手机类如下:
public class Phone { public void pay() { } public void box() { } } class IPhone extends Phone { } class SamsungPhone extends Phone { } class HuaweiPhone extends Phone { } 复制代码
三种品牌的电脑类如下:
public class Computer { public void pay() { } public void pack() { } } class MacComputer extends Computer { } class SamsungComputer extends Computer { } class HuaweiComputer extends Computer { } 复制代码
对于三家相应品牌的专卖店,它们的具体实现如下:
public class IPhoneStore extends PhoneStore { @Override protected Phone createPhone() { return new IPhone(); } @Override protected Computer createComputer() { return new MacComputer(); } } public class SamsungStore extends PhoneStore { @Override protected Phone createPhone() { return new SamsungPhone(); } @Override protected Computer createComputer() { return new SamsungComputer(); } } public class HuaweiStore extends PhoneStore { @Override protected Phone createPhone() { return new HuaweiPhone(); } @Override protected Computer createComputer() { return new HuaweiComputer(); } } 复制代码
如果我们要在 IPhone 专卖店购买手机和电脑,代码可以如下:
public class Test { public static void main(String[] args) { PhoneStore phoneStore = new IPhoneStore(); Phone phone = phoneStore.buyPhone(); Computer computer = phoneStore.buyComputer(); // phone 为 IPhone // computer 为 Mac } } 复制代码
上述的模式就是抽象工工厂模式,它提供了一个接口,用于创建一个产品的家族,而不需要指定具体类。每个具体工厂会创建某个产品家族。
在上述例子,IPhoneStore、SamsungStore、HuaweiStore 就是一个个具体的工厂,它们可以生产对应品牌的手机和电脑。其中 IPhoneStore 这个工厂就是创建 IPhone、MacComputer 这个产品家族。
它的 UML 图如下:
下面总结一下抽象工厂模式的优缺点。
优点:
- 同样地,将用户代码和实际的具体产品解耦,使其无需关心产品的创建细节;
- 使用某个工厂,可以创建一系列相关的产品。如果想要增加一条个产品线,例如上面想要增加一个新的品牌店和其相应的产品,只需要扩展 PhoneStore 工厂,并创建相应的 Phone、Computer 类即可,非常简单。
缺点:
- 限制了所能创建的产品集合,例如上面的 Phone 和 Computer,如果想要增加新的产品,增加 camera,就会比较困难,需要修改抽象工厂的接口,会增加很大的工作量;
- 另外,工厂类和产品类较多,增加了系统的抽象性和复杂度。
抽象工厂模式与工厂方法模式很类似,它们之间的区别如下:
- 在工厂方法模式中,每个具体工厂负责创建一个具体产品。所以,在增加一个具体产品时,也要增加其相应的工厂,需要创建一个继承自抽象工厂的类,并覆盖它的工厂方法。也就是所说的工厂方法使用继承创建对象。
- 而在抽象工厂模式中,每个具体工厂负责创建一个系列的具体产品。所以,只有在新增加一个类型的具体产品时,才需要增加相应的工厂。它可以用来创建一系列具体产品,将这些相关的产品组合起来,也就是所说的使用组合创建对象。
它们之间也有一些关联,就是抽象工厂的方法以工厂方法的方法来实现。在抽象工厂的接口中,每个方法都负责创建一个具体产品,而具体工厂来提供具体的实现。
例如,PhoneStore 中的 createPhone、createComputer 方法由子类实现,这两个方法单独来看都是在创建一个对象,其实也就是一个工厂方法。
抽象工厂模式的实践
JDBC 中的 Connection 就是一个抽象工厂模式,在不同的连接池中有不同的实现,例如 druid 和 dbcp:
由于本人对于 druid 和 dbcp 的实现也不太熟悉,这里就不多解释了,有兴趣的小伙伴可以自己研究一下。
参考资料
- 《Head First 设计模式》
- CyC2018/CS-Notes: 设计模式
- 越努力越幸运-致自己: java设计模式精讲 Debug 方式+内存分析 第4章 简单工厂模式
以上所述就是小编给大家介绍的《一文搞懂三种工厂模式》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 设计模式-简单工厂、工厂方法模式、抽象工厂模式
- 简单工厂模式、工厂模式、抽象工厂模式的解析-iOS
- Java 设计模式之工厂方法模式与抽象工厂模式
- 设计模式-创建型模式-工厂模式(工厂三兄弟) TypeScript
- 设计模式之工厂模式(为什么很多人觉得工厂模式没有用)
- 设计模式 —— 工厂模式
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。