1、简单工厂模式(静态工厂方法模式)

1.1 定义

定义一个工厂类,根据传入的参数不同返回不同的实例,被创建的实例具有共同的父类或接口。23种设计模式并不包括简单工厂模式,它更像一种编程习惯。

1.2 模式结构

工厂(Factory)模式 - 图1

简单工厂模式由三部分组成:

  • Creator(工厂类):担任这个角色的是简单工厂模式的核心,含有与应用紧密相关的商业逻辑。工厂类在客户端的直接调用下创建产品对象。
  • AbstractProduct(抽象产品):担任这个角色的类是由简单工厂模式所创建的对象的父类,或它们共同拥有的接口。
  • ConcreteProduct(具体产品):简单工厂模式所创建的任何对象都是这个角色的实例。

1.3 实例

1.3.1 电视机父类TV(AbstractProduct)

  1. public abstract class TV {
  2. abstract void openTV();
  3. }

1.3.2 海尔电视机和长虹电视机(ConcreteProduct)

  1. public class HaierTV extends TV {
  2. public void openTV() {
  3. System.out.println("open haier TV");
  4. }
  5. }
  1. public class ChanghongTV extends TV {
  2. public void openTV() {
  3. System.out.println("open changhong TV");
  4. }
  5. }

1.3.3 工厂类(Creator)

  1. public class Factory {
  2. public static TV watchTV(String type) throws Exception {
  3. if (type.equals("Haier")) {
  4. return new HaierTV();
  5. } else if (type.equals("Changhong")) {
  6. return new ChanghongTV();
  7. } else {
  8. throw new IllegalArgumentException("no correspond TV");
  9. }
  10. }
  11. }

1.3.4 客户端调用

  1. public class Client {
  2. public static void main(String[] args) throws Exception {
  3. TV tv = Factory.watchTV("Haier");
  4. tv.openTV();
  5. }
  6. }

1.4 适用场景
  • 工厂类负责创建的对象比较小。
  • 客户端只关心传入工厂类的参数,不关心对象的创建过程。

1.5 在JDK中的应用

Calendar.class中的部分源码:

  1. private static Calendar createCalendar(TimeZone zone,
  2. Locale aLocale)
  3. {
  4. CalendarProvider provider =
  5. LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
  6. .getCalendarProvider();
  7. if (provider != null) {
  8. try {
  9. return provider.getInstance(zone, aLocale);
  10. } catch (IllegalArgumentException iae) {
  11. // fall back to the default instantiation
  12. }
  13. }
  14. Calendar cal = null;
  15. if (aLocale.hasExtensions()) {
  16. String caltype = aLocale.getUnicodeLocaleType("ca");
  17. if (caltype != null) {
  18. switch (caltype) {
  19. case "buddhist":
  20. cal = new BuddhistCalendar(zone, aLocale);
  21. break;
  22. case "japanese":
  23. cal = new JapaneseImperialCalendar(zone, aLocale);
  24. break;
  25. case "gregory":
  26. cal = new GregorianCalendar(zone, aLocale);
  27. break;
  28. }
  29. }
  30. }
  31. if (cal == null) {
  32. // If no known calendar type is explicitly specified,
  33. // perform the traditional way to create a Calendar:
  34. // create a BuddhistCalendar for th_TH locale,
  35. // a JapaneseImperialCalendar for ja_JP_JP locale, or
  36. // a GregorianCalendar for any other locales.
  37. // NOTE: The language, country and variant strings are interned.
  38. if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
  39. cal = new BuddhistCalendar(zone, aLocale);
  40. } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
  41. && aLocale.getCountry() == "JP") {
  42. cal = new JapaneseImperialCalendar(zone, aLocale);
  43. } else {
  44. cal = new GregorianCalendar(zone, aLocale);
  45. }
  46. }
  47. return cal;
  48. }

1.6 优缺点

1.6.1 优点

  • 工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工具类用于创建对象。
  • 客户端无须知道所创建的具体产品类的类名,只需要知道产品类所对应的参数即可,对应一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。
  • 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
  • 当需要引入新的产品是不需要修改客户端的代码,只需要添加相应的产品类并修改工厂类就可以了,所以说从产品的角度上简单工厂模式是符合“开-闭”原则的。

1.6.2 缺点

  • 由于工厂类集中了所有产品创建逻辑,工厂类一般被我们称作“全能类”或者“上帝类”,因为所有的产品创建它都完成,这看似是好事,但仔细想想是有问题的。这样一旦不能正常工作,整个系统都要受到影响。
  • 系统扩展困难,一旦添加新产品就要修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。所以说从工厂的角度来说简单工厂模式是不符合“开-闭”原则的。
  • 由于使用静态工厂方法,造成工厂角色无法形成基于继承的等级结构。

2、工厂方法模式(虚拟构造器模式、多态工厂模式)

2.1 定义

工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。

2.2 模式结构

工厂(Factory)模式 - 图2

工厂方法模式由四部分组成:

  • AbstractCreater(抽象工厂):担任这个角色的是工厂方法模式的核心,与调用者直接交互用来提供产品。任何在模式中创建对象的工厂类必须继承或者实现这个接口。
  • ConcreteCreator(具体工厂):担任这个角色的是实现了抽象工厂接口的具体实现类。具体工厂角色含有与应用密切相关的逻辑,并且受到应用程序的调用以创建产品对象。
  • AbstractProduct(抽象产品):工厂方法模式所创建的对象的超类。主要目的是定义产品的规范,所有的产品实现都必须遵循这些规范。
  • ConcreteProduct(具体产品):实现了抽象产品角色所声明的接口,工厂方法所创建的每一个对象都是某个具体产品角色的实例。

2.3 实例

2.3.1 电视机父类TV(AbstractProduct)

  1. public abstract class TV {
  2. abstract void openTV();
  3. }

2.3.2 海尔电视机和长虹电视机(ConcreteProduct)

  1. public class HaierTV extends TV {
  2. public void openTV() {
  3. System.out.println("open haier TV");
  4. }
  5. }
  1. public class Changhong extends TV {
  2. public void openTV() {
  3. System.out.println('open changhong TV');
  4. }
  5. }

2.3.3 抽象工厂类(AbstractCreater)

  1. public abstract class Factory {
  2. public abstract TV watchTV();
  3. }

2.3.4 具体工厂类(ConcreteCreator)

  1. public class HaierFactory extends Factory {
  2. public TV watchTV() {
  3. return new HaierTV();
  4. }
  5. }
  1. public class ChanghongFactory extends Factory {
  2. public TV watchTV() {
  3. return new ChanghongTV();
  4. }
  5. }

2.3.5 客户端调用

  1. public class Client {
  2. public static void main(String[] args) throws Exception {
  3. Factory factory = new HaierFactory();
  4. TV tv = factory.watchTV();
  5. tv.openTV();
  6. }
  7. }

2.4 适用场景
  • 客户端不需要知道它所场景的对象的类,只需要知道创建具体产品的工厂类。
  • 客户端可以通过子类来指定创建对应的对象。

2.5 优缺点
  • 工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节。
  • 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。
  • 在系统加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品。这样系统的可扩展性变得良好,完全符合“开-闭“原则。

3、抽象工厂模式(Kit模式)

3.1 定义

提供一个创建一系列相关或相互依赖对象的接口,而无须指明它们具体的类。

3.2 产品族和等级等级结构
  • 产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,取子类有海尔电视机、长虹电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构。
  • 产品族:在抽象工厂模式中,产品族是指同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。

工厂(Factory)模式 - 图3

3.3 模式结构

工厂(Factory)模式 - 图4

抽象工厂模式和工厂方法模式类似都是四部分组成的,不同的是,抽象工厂模式中具体工厂不再是只创建一种产品,一个具体的工厂可以创建一个产品族的产品。

3.4 实例

3.4.1 电视机父类TV(AbstractProduct)

  1. public abstract class TV {
  2. abstract void openTV();
  3. }

3.4.2 海尔电视机和长虹电视(ConcreteProduct)

  1. public class HaireTV extends TV {
  2. public void openTV() {
  3. System.out.println("open haire TV");
  4. }
  5. }
  1. public class ChanghongTV extends TV {
  2. public void openTV() {
  3. System.out.println('open changhong TV');
  4. }
  5. }

3.4.3 冰箱父类Refrigerator(AbstractProduct)

  1. public abstract class Refrigerator {
  2. abstract void openRefrigerator();
  3. }

3.4.4 海尔冰箱和长虹冰箱(ConcreteProduct)

  1. public class HaierRefrigerator extends Refrigerator {
  2. public void openRefrigerator() {
  3. System.out.println("open haier refrigerator");
  4. }
  5. }
  1. public class ChanghongRefrigerator extends Refrigerator {
  2. public void openRefrigerator() {
  3. System.out.println("open changhong refrigerator");
  4. }
  5. }

3.4.5 抽象工厂类,定义同一族产品的两个不同等级结构的产品结构(AbstractCreater)

  1. public abstract class Factory {
  2. public abstract TV watchTV();
  3. public abstract Refrigerator takeThings();
  4. }

3.4.6 具体工厂类(ConcreteCreator)

  1. public class HaierFactory extends Factory {
  2. public TV watchTV() {
  3. return new HaierTV();
  4. }
  5. public Refrigerator takeThings() {
  6. return new HaierRefrigerator();
  7. }
  8. }
  1. public class ChanghongFactory extends Factory {
  2. public TV watchTV() {
  3. return new ChanghongTV();
  4. }
  5. public Refrigerator takeThings() {
  6. return new ChanghongRefrigerator();
  7. }
  8. }

3.4.7 客户端调用

  1. public class Client {
  2. public static void main(String[] args) throws Exception {
  3. Factory factory = new HaierFactory();
  4. TV tv = factory.watchTV();
  5. tv.openTV();
  6. Refrigerator refrigerator = factory.takeThings();
  7. refrigerator.openRefrigerator();
  8. }
  9. }

3.5 适用场景
  • 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。
  • 系统的产品有多于一个的产品族,而系统只消费群体某一族的产品。
  • 同属于同一产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。
    *系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖与实现。

3.6 优缺点

3.6.1 优点

  • 抽象工厂模式隔离了具体类的生成,使得客户不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程序上改变整个系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的。
  • 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常使用的设计模式。
  • 增加新的具体工厂和产品族很方便,无须改动其他,符合“开闭”原则。

3.6.2 缺点

  • 在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被场景的产品集合,要支持新种类的产品就意味着要对接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然带来较大的不便。
  • 开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)。