内容简介:上一篇文章详细学习了单例模式的多种写法,今天来学习一下如下三种模式:简单工厂、工厂方法、抽象工厂模式,其实简单工厂模式不属于 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章 简单工厂模式
以上所述就是小编给大家介绍的《一文搞懂三种工厂模式》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Sovereign Individual
James Dale Davidson、William Rees-Mogg / Free Press / 1999-08-26 / USD 16.00
Two renowned investment advisors and authors of the bestseller The Great Reckoning bring to light both currents of disaster and the potential for prosperity and renewal in the face of radical changes ......一起来看看 《The Sovereign Individual》 这本书的介绍吧!