使用场景

当创建逻辑复杂时,使用工厂模式来封装对象的创建过程,将对象的创建和使用分离。
主要有以下的场景:

  1. 无法预知对象确切类别以及依赖关系
    1. 将创建产品的代码与实际使用产品的代码分离,从而能在不影响其他代码的情况下扩展产品创建部分代码。
  2. 复用现有对象来节省系统资源,而不是每次都重新创建对象
    1. 将之前创建的对象缓存起来

参考标准:

  • 封装变化:创建逻辑有可能变化,封装成工厂类后,创建逻辑的变更对调用者透明
  • 代码复用:创建代码抽离到独立的工厂类之后可以复用
  • 隔离复杂性:封装复杂的创建逻辑,调用者无需了解如何创建对象
  • 控制复杂度:将创建代码抽离出来,让原本的函数或类职责更单一,代码更简洁

原本丑陋的代码:

  1. const TEACHER_TYPE = {
  2. CODING: 'coding',
  3. MUSIC: 'music'
  4. };
  5. class Teacher {
  6. constructor(type, name, instrument, programmingLanguage) {
  7. this.name = name;
  8. if (type === TEACHER_TYPE.CODING) {
  9. this.programmingLanguage = programmingLanguage;
  10. } else if (type === TEACHER_TYPE.MUSIC){
  11. this.instrument = instrument;
  12. }
  13. }
  14. }

在工厂模式中,将创建对象的过程单独封装
最大的好处应该是让你的代码具有更强的可扩展性吧。。。

  1. class Teacher {
  2. constructor(properties) {
  3. this.name = properties.name;
  4. }
  5. }
  6. class CodingTeacher extends Teacher {
  7. constructor(properties) {
  8. super(properties);
  9. this.programmingLanguage = properties.programmingLanguage;
  10. }
  11. }
  12. class MusicTeacher extends Teacher {
  13. constructor(properties) {
  14. super(properties);
  15. this.instrument = properties.instrument;
  16. }
  17. }
  18. class TeacherFactory {
  19. static getTeacher(type, properties) {
  20. switch (type) {
  21. case TEACHER_TYPE.CODING:
  22. return new CodingTeacher(properties);
  23. case TEACHER_TYPE.MUSIC:
  24. return new MusicTeacher(properties);
  25. default:
  26. throw new Error('Wrong teacher type chosen');
  27. }
  28. }
  29. }
  30. const codingTeacher = TeacherFactory.getTeacher(TEACHER_TYPE.CODING, {
  31. programmingLanguage: 'JavaScript',
  32. name: 'John'
  33. });
  34. const musicTeacher = TeacherFactory.getTeacher(TEACHER_TYPE.MUSIC, {
  35. instrument: 'Guitar',
  36. name: 'Andy'
  37. });

工厂方法

企业微信20210520-114024.png

  1. // 创建者类声明的工厂方法必须返回一个产品类的对象。创建者的子类通常会提供
  2. // 该方法的实现。
  3. class Dialog is
  4. // 创建者还可提供一些工厂方法的默认实现。
  5. abstract method createButton():Button
  6. // 请注意,创建者的主要职责并非是创建产品。其中通常会包含一些核心业务
  7. // 逻辑,这些逻辑依赖于由工厂方法返回的产品对象。子类可通过重写工厂方
  8. // 法并使其返回不同类型的产品来间接修改业务逻辑。
  9. method render() is
  10. // 调用工厂方法创建一个产品对象。
  11. Button okButton = createButton()
  12. // 现在使用产品。
  13. okButton.onClick(closeDialog)
  14. okButton.render()
  15. // 具体创建者将重写工厂方法以改变其所返回的产品类型。
  16. class WindowsDialog extends Dialog is
  17. method createButton():Button is
  18. return new WindowsButton()
  19. class WebDialog extends Dialog is
  20. method createButton():Button is
  21. return new HTMLButton()
  22. // 产品接口中将声明所有具体产品都必须实现的操作。
  23. interface Button is
  24. method render()
  25. method onClick(f)
  26. // 具体产品需提供产品接口的各种实现。
  27. class WindowsButton implements Button is
  28. method render(a, b) is
  29. // 根据 Windows 样式渲染按钮。
  30. method onClick(f) is
  31. // 绑定本地操作系统点击事件。
  32. class HTMLButton implements Button is
  33. method render(a, b) is
  34. // 返回一个按钮的 HTML 表述。
  35. method onClick(f) is
  36. // 绑定网络浏览器的点击事件。
  37. class Application is
  38. field dialog: Dialog
  39. // 程序根据当前配置或环境设定选择创建者的类型。
  40. method initialize() is
  41. config = readApplicationConfigFile()
  42. if (config.OS == "Windows") then
  43. dialog = new WindowsDialog()
  44. else if (config.OS == "Web") then
  45. dialog = new WebDialog()
  46. else
  47. throw new Exception("错误!未知的操作系统。")
  48. // 当前客户端代码会与具体创建者的实例进行交互,但是必须通过其基本接口
  49. // 进行。只要客户端通过基本接口与创建者进行交互,你就可将任何创建者子
  50. // 类传递给客户端。
  51. method main() is
  52. this.initialize()
  53. dialog.render()
  1. /**
  2. * The Creator class declares the factory method that is supposed to return an
  3. * object of a Product class. The Creator's subclasses usually provide the
  4. * implementation of this method.
  5. */
  6. abstract class Creator {
  7. /**
  8. * Note that the Creator may also provide some default implementation of the
  9. * factory method.
  10. */
  11. public abstract factoryMethod(): Product;
  12. /**
  13. * Also note that, despite its name, the Creator's primary responsibility is
  14. * not creating products. Usually, it contains some core business logic that
  15. * relies on Product objects, returned by the factory method. Subclasses can
  16. * indirectly change that business logic by overriding the factory method
  17. * and returning a different type of product from it.
  18. */
  19. public someOperation(): string {
  20. // Call the factory method to create a Product object.
  21. const product = this.factoryMethod();
  22. // Now, use the product.
  23. return `Creator: The same creator's code has just worked with ${product.operation()}`;
  24. }
  25. }
  26. /**
  27. * Concrete Creators override the factory method in order to change the
  28. * resulting product's type.
  29. */
  30. class ConcreteCreator1 extends Creator {
  31. /**
  32. * Note that the signature of the method still uses the abstract product
  33. * type, even though the concrete product is actually returned from the
  34. * method. This way the Creator can stay independent of concrete product
  35. * classes.
  36. */
  37. public factoryMethod(): Product {
  38. return new ConcreteProduct1();
  39. }
  40. }
  41. class ConcreteCreator2 extends Creator {
  42. public factoryMethod(): Product {
  43. return new ConcreteProduct2();
  44. }
  45. }
  46. /**
  47. * The Product interface declares the operations that all concrete products must
  48. * implement.
  49. */
  50. interface Product {
  51. operation(): string;
  52. }
  53. /**
  54. * Concrete Products provide various implementations of the Product interface.
  55. */
  56. class ConcreteProduct1 implements Product {
  57. public operation(): string {
  58. return '{Result of the ConcreteProduct1}';
  59. }
  60. }
  61. class ConcreteProduct2 implements Product {
  62. public operation(): string {
  63. return '{Result of the ConcreteProduct2}';
  64. }
  65. }
  66. /**
  67. * The client code works with an instance of a concrete creator, albeit through
  68. * its base interface. As long as the client keeps working with the creator via
  69. * the base interface, you can pass it any creator's subclass.
  70. */
  71. function clientCode(creator: Creator) {
  72. // ...
  73. console.log('Client: I\'m not aware of the creator\'s class, but it still works.');
  74. console.log(creator.someOperation());
  75. // ...
  76. }
  77. /**
  78. * The Application picks a creator's type depending on the configuration or
  79. * environment.
  80. */
  81. console.log('App: Launched with the ConcreteCreator1.');
  82. clientCode(new ConcreteCreator1());
  83. console.log('');
  84. console.log('App: Launched with the ConcreteCreator2.');
  85. clientCode(new ConcreteCreator2());

抽象工厂

  • 抽象工厂(抽象类,它不能被用于生成具体实例):用于声明最终目标产品的共性。在一个系统里,抽象工厂可以有多个,每一个抽象工厂对应的这一类的产品,被称为“产品族”。
  • 具体工厂(用于生成产品族里的一个具体的产品):继承自抽象工厂、实现了抽象工厂里声明的那些方法,用于创建具体的产品的类。
  • 抽象产品(抽象类,它不能被用于生成具体实例):上面我们看到,具体工厂里实现的接口,会依赖一些类,这些类对应到各种各样的具体的细粒度产品(比如操作系统、硬件等),这些具体产品类的共性各自抽离,便对应到了各自的抽象产品类。
  • 具体产品(用于生成产品族里的一个具体的产品所依赖的更细粒度的产品):比如我们上文中具体的一种操作系统、或具体的一种硬件等。 ```typescript // 抽象工厂接口声明了一组能返回不同抽象产品的方法。这些产品属于同一个系列 // 且在高层主题或概念上具有相关性。同系列的产品通常能相互搭配使用。系列产 // 品可有多个变体,但不同变体的产品不能搭配使用。 interface GUIFactory is method createButton():Button method createCheckbox():Checkbox

// 具体工厂可生成属于同一变体的系列产品。工厂会确保其创建的产品能相互搭配 // 使用。具体工厂方法签名会返回一个抽象产品,但在方法内部则会对具体产品进 // 行实例化。 class WinFactory implements GUIFactory is method createButton():Button is return new WinButton() method createCheckbox():Checkbox is return new WinCheckbox()

// 每个具体工厂中都会包含一个相应的产品变体。 class MacFactory implements GUIFactory is method createButton():Button is return new MacButton() method createCheckbox():Checkbox is return new MacCheckbox()

// 系列产品中的特定产品必须有一个基础接口。所有产品变体都必须实现这个接口。 interface Button is method paint()

// 具体产品由相应的具体工厂创建。 class WinButton implements Button is method paint() is // 根据 Windows 样式渲染按钮。

class MacButton implements Button is method paint() is // 根据 macOS 样式渲染按钮

// 这是另一个产品的基础接口。所有产品都可以互动,但是只有相同具体变体的产 // 品之间才能够正确地进行交互。 interface Checkbox is method paint()

class WinCheckbox implements Checkbox is method paint() is // 根据 Windows 样式渲染复选框。

class MacCheckbox implements Checkbox is method paint() is // 根据 macOS 样式渲染复选框。

// 客户端代码仅通过抽象类型(GUIFactory、Button 和 Checkbox)使用工厂 // 和产品。这让你无需修改任何工厂或产品子类就能将其传递给客户端代码。 class Application is private field factory: GUIFactory private field button: Button constructor Application(factory: GUIFactory) is this.factory = factory method createUI() is this.button = factory.createButton() method paint() is button.paint()

// 程序会根据当前配置或环境设定选择工厂类型,并在运行时创建工厂(通常在初 // 始化阶段)。 class ApplicationConfigurator is method main() is config = readApplicationConfigFile()

  1. if (config.OS == "Windows") then
  2. factory = new WinFactory()
  3. else if (config.OS == "Mac") then
  4. factory = new MacFactory()
  5. else
  6. throw new Exception("错误!未知的操作系统。")
  7. Application app = new Application(factory)
  1. ```typescript
  2. /**
  3. * The Abstract Factory interface declares a set of methods that return
  4. * different abstract products. These products are called a family and are
  5. * related by a high-level theme or concept. Products of one family are usually
  6. * able to collaborate among themselves. A family of products may have several
  7. * variants, but the products of one variant are incompatible with products of
  8. * another.
  9. */
  10. interface AbstractFactory {
  11. createProductA(): AbstractProductA;
  12. createProductB(): AbstractProductB;
  13. }
  14. /**
  15. * Concrete Factories produce a family of products that belong to a single
  16. * variant. The factory guarantees that resulting products are compatible. Note
  17. * that signatures of the Concrete Factory's methods return an abstract product,
  18. * while inside the method a concrete product is instantiated.
  19. */
  20. class ConcreteFactory1 implements AbstractFactory {
  21. public createProductA(): AbstractProductA {
  22. return new ConcreteProductA1();
  23. }
  24. public createProductB(): AbstractProductB {
  25. return new ConcreteProductB1();
  26. }
  27. }
  28. /**
  29. * Each Concrete Factory has a corresponding product variant.
  30. */
  31. class ConcreteFactory2 implements AbstractFactory {
  32. public createProductA(): AbstractProductA {
  33. return new ConcreteProductA2();
  34. }
  35. public createProductB(): AbstractProductB {
  36. return new ConcreteProductB2();
  37. }
  38. }
  39. /**
  40. * Each distinct product of a product family should have a base interface. All
  41. * variants of the product must implement this interface.
  42. */
  43. interface AbstractProductA {
  44. usefulFunctionA(): string;
  45. }
  46. /**
  47. * These Concrete Products are created by corresponding Concrete Factories.
  48. */
  49. class ConcreteProductA1 implements AbstractProductA {
  50. public usefulFunctionA(): string {
  51. return 'The result of the product A1.';
  52. }
  53. }
  54. class ConcreteProductA2 implements AbstractProductA {
  55. public usefulFunctionA(): string {
  56. return 'The result of the product A2.';
  57. }
  58. }
  59. /**
  60. * Here's the the base interface of another product. All products can interact
  61. * with each other, but proper interaction is possible only between products of
  62. * the same concrete variant.
  63. */
  64. interface AbstractProductB {
  65. /**
  66. * Product B is able to do its own thing...
  67. */
  68. usefulFunctionB(): string;
  69. /**
  70. * ...but it also can collaborate with the ProductA.
  71. *
  72. * The Abstract Factory makes sure that all products it creates are of the
  73. * same variant and thus, compatible.
  74. */
  75. anotherUsefulFunctionB(collaborator: AbstractProductA): string;
  76. }
  77. /**
  78. * These Concrete Products are created by corresponding Concrete Factories.
  79. */
  80. class ConcreteProductB1 implements AbstractProductB {
  81. public usefulFunctionB(): string {
  82. return 'The result of the product B1.';
  83. }
  84. /**
  85. * The variant, Product B1, is only able to work correctly with the variant,
  86. * Product A1. Nevertheless, it accepts any instance of AbstractProductA as
  87. * an argument.
  88. */
  89. public anotherUsefulFunctionB(collaborator: AbstractProductA): string {
  90. const result = collaborator.usefulFunctionA();
  91. return `The result of the B1 collaborating with the (${result})`;
  92. }
  93. }
  94. class ConcreteProductB2 implements AbstractProductB {
  95. public usefulFunctionB(): string {
  96. return 'The result of the product B2.';
  97. }
  98. /**
  99. * The variant, Product B2, is only able to work correctly with the variant,
  100. * Product A2. Nevertheless, it accepts any instance of AbstractProductA as
  101. * an argument.
  102. */
  103. public anotherUsefulFunctionB(collaborator: AbstractProductA): string {
  104. const result = collaborator.usefulFunctionA();
  105. return `The result of the B2 collaborating with the (${result})`;
  106. }
  107. }
  108. /**
  109. * The client code works with factories and products only through abstract
  110. * types: AbstractFactory and AbstractProduct. This lets you pass any factory or
  111. * product subclass to the client code without breaking it.
  112. */
  113. function clientCode(factory: AbstractFactory) {
  114. const productA = factory.createProductA();
  115. const productB = factory.createProductB();
  116. console.log(productB.usefulFunctionB());
  117. console.log(productB.anotherUsefulFunctionB(productA));
  118. }
  119. /**
  120. * The client code can work with any concrete factory class.
  121. */
  122. console.log('Client: Testing client code with the first factory type...');
  123. clientCode(new ConcreteFactory1());
  124. console.log('');
  125. console.log('Client: Testing the same client code with the second factory type...');
  126. clientCode(new ConcreteFactory2());

参考资料

  1. 44 | 工厂模式(上):我为什么说没事不要随便用工厂模式创建对象?
  2. TypeScript 工厂方法模式讲解和代码示例
  3. Factories and their implementation in TypeScript
  4. JavaScript Object Oriented Patterns: Factory Pattern
  5. 工厂模式(factory Method)的本质是什么?为什么引入工厂模式?
  6. 工厂模式(简单工厂)——- 区分“变与不变”