使用场景
当创建逻辑复杂时,使用工厂模式来封装对象的创建过程,将对象的创建和使用分离。
主要有以下的场景:
- 无法预知对象确切类别以及依赖关系
- 将创建产品的代码与实际使用产品的代码分离,从而能在不影响其他代码的情况下扩展产品创建部分代码。
- 复用现有对象来节省系统资源,而不是每次都重新创建对象
- 将之前创建的对象缓存起来
参考标准:
- 封装变化:创建逻辑有可能变化,封装成工厂类后,创建逻辑的变更对调用者透明
- 代码复用:创建代码抽离到独立的工厂类之后可以复用
- 隔离复杂性:封装复杂的创建逻辑,调用者无需了解如何创建对象
- 控制复杂度:将创建代码抽离出来,让原本的函数或类职责更单一,代码更简洁
原本丑陋的代码:
const TEACHER_TYPE = {CODING: 'coding',MUSIC: 'music'};class Teacher {constructor(type, name, instrument, programmingLanguage) {this.name = name;if (type === TEACHER_TYPE.CODING) {this.programmingLanguage = programmingLanguage;} else if (type === TEACHER_TYPE.MUSIC){this.instrument = instrument;}}}
在工厂模式中,将创建对象的过程单独封装
最大的好处应该是让你的代码具有更强的可扩展性吧。。。
class Teacher {constructor(properties) {this.name = properties.name;}}class CodingTeacher extends Teacher {constructor(properties) {super(properties);this.programmingLanguage = properties.programmingLanguage;}}class MusicTeacher extends Teacher {constructor(properties) {super(properties);this.instrument = properties.instrument;}}class TeacherFactory {static getTeacher(type, properties) {switch (type) {case TEACHER_TYPE.CODING:return new CodingTeacher(properties);case TEACHER_TYPE.MUSIC:return new MusicTeacher(properties);default:throw new Error('Wrong teacher type chosen');}}}const codingTeacher = TeacherFactory.getTeacher(TEACHER_TYPE.CODING, {programmingLanguage: 'JavaScript',name: 'John'});const musicTeacher = TeacherFactory.getTeacher(TEACHER_TYPE.MUSIC, {instrument: 'Guitar',name: 'Andy'});
工厂方法

// 创建者类声明的工厂方法必须返回一个产品类的对象。创建者的子类通常会提供// 该方法的实现。class Dialog is// 创建者还可提供一些工厂方法的默认实现。abstract method createButton():Button// 请注意,创建者的主要职责并非是创建产品。其中通常会包含一些核心业务// 逻辑,这些逻辑依赖于由工厂方法返回的产品对象。子类可通过重写工厂方// 法并使其返回不同类型的产品来间接修改业务逻辑。method render() is// 调用工厂方法创建一个产品对象。Button okButton = createButton()// 现在使用产品。okButton.onClick(closeDialog)okButton.render()// 具体创建者将重写工厂方法以改变其所返回的产品类型。class WindowsDialog extends Dialog ismethod createButton():Button isreturn new WindowsButton()class WebDialog extends Dialog ismethod createButton():Button isreturn new HTMLButton()// 产品接口中将声明所有具体产品都必须实现的操作。interface Button ismethod render()method onClick(f)// 具体产品需提供产品接口的各种实现。class WindowsButton implements Button ismethod render(a, b) is// 根据 Windows 样式渲染按钮。method onClick(f) is// 绑定本地操作系统点击事件。class HTMLButton implements Button ismethod render(a, b) is// 返回一个按钮的 HTML 表述。method onClick(f) is// 绑定网络浏览器的点击事件。class Application isfield dialog: Dialog// 程序根据当前配置或环境设定选择创建者的类型。method initialize() isconfig = readApplicationConfigFile()if (config.OS == "Windows") thendialog = new WindowsDialog()else if (config.OS == "Web") thendialog = new WebDialog()elsethrow new Exception("错误!未知的操作系统。")// 当前客户端代码会与具体创建者的实例进行交互,但是必须通过其基本接口// 进行。只要客户端通过基本接口与创建者进行交互,你就可将任何创建者子// 类传递给客户端。method main() isthis.initialize()dialog.render()
/*** The Creator class declares the factory method that is supposed to return an* object of a Product class. The Creator's subclasses usually provide the* implementation of this method.*/abstract class Creator {/*** Note that the Creator may also provide some default implementation of the* factory method.*/public abstract factoryMethod(): Product;/*** Also note that, despite its name, the Creator's primary responsibility is* not creating products. Usually, it contains some core business logic that* relies on Product objects, returned by the factory method. Subclasses can* indirectly change that business logic by overriding the factory method* and returning a different type of product from it.*/public someOperation(): string {// Call the factory method to create a Product object.const product = this.factoryMethod();// Now, use the product.return `Creator: The same creator's code has just worked with ${product.operation()}`;}}/*** Concrete Creators override the factory method in order to change the* resulting product's type.*/class ConcreteCreator1 extends Creator {/*** Note that the signature of the method still uses the abstract product* type, even though the concrete product is actually returned from the* method. This way the Creator can stay independent of concrete product* classes.*/public factoryMethod(): Product {return new ConcreteProduct1();}}class ConcreteCreator2 extends Creator {public factoryMethod(): Product {return new ConcreteProduct2();}}/*** The Product interface declares the operations that all concrete products must* implement.*/interface Product {operation(): string;}/*** Concrete Products provide various implementations of the Product interface.*/class ConcreteProduct1 implements Product {public operation(): string {return '{Result of the ConcreteProduct1}';}}class ConcreteProduct2 implements Product {public operation(): string {return '{Result of the ConcreteProduct2}';}}/*** The client code works with an instance of a concrete creator, albeit through* its base interface. As long as the client keeps working with the creator via* the base interface, you can pass it any creator's subclass.*/function clientCode(creator: Creator) {// ...console.log('Client: I\'m not aware of the creator\'s class, but it still works.');console.log(creator.someOperation());// ...}/*** The Application picks a creator's type depending on the configuration or* environment.*/console.log('App: Launched with the ConcreteCreator1.');clientCode(new ConcreteCreator1());console.log('');console.log('App: Launched with the ConcreteCreator2.');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()
if (config.OS == "Windows") thenfactory = new WinFactory()else if (config.OS == "Mac") thenfactory = new MacFactory()elsethrow new Exception("错误!未知的操作系统。")Application app = new Application(factory)
```typescript/*** The Abstract Factory interface declares a set of methods that return* different abstract products. These products are called a family and are* related by a high-level theme or concept. Products of one family are usually* able to collaborate among themselves. A family of products may have several* variants, but the products of one variant are incompatible with products of* another.*/interface AbstractFactory {createProductA(): AbstractProductA;createProductB(): AbstractProductB;}/*** Concrete Factories produce a family of products that belong to a single* variant. The factory guarantees that resulting products are compatible. Note* that signatures of the Concrete Factory's methods return an abstract product,* while inside the method a concrete product is instantiated.*/class ConcreteFactory1 implements AbstractFactory {public createProductA(): AbstractProductA {return new ConcreteProductA1();}public createProductB(): AbstractProductB {return new ConcreteProductB1();}}/*** Each Concrete Factory has a corresponding product variant.*/class ConcreteFactory2 implements AbstractFactory {public createProductA(): AbstractProductA {return new ConcreteProductA2();}public createProductB(): AbstractProductB {return new ConcreteProductB2();}}/*** Each distinct product of a product family should have a base interface. All* variants of the product must implement this interface.*/interface AbstractProductA {usefulFunctionA(): string;}/*** These Concrete Products are created by corresponding Concrete Factories.*/class ConcreteProductA1 implements AbstractProductA {public usefulFunctionA(): string {return 'The result of the product A1.';}}class ConcreteProductA2 implements AbstractProductA {public usefulFunctionA(): string {return 'The result of the product A2.';}}/*** Here's the the base interface of another product. All products can interact* with each other, but proper interaction is possible only between products of* the same concrete variant.*/interface AbstractProductB {/*** Product B is able to do its own thing...*/usefulFunctionB(): string;/*** ...but it also can collaborate with the ProductA.** The Abstract Factory makes sure that all products it creates are of the* same variant and thus, compatible.*/anotherUsefulFunctionB(collaborator: AbstractProductA): string;}/*** These Concrete Products are created by corresponding Concrete Factories.*/class ConcreteProductB1 implements AbstractProductB {public usefulFunctionB(): string {return 'The result of the product B1.';}/*** The variant, Product B1, is only able to work correctly with the variant,* Product A1. Nevertheless, it accepts any instance of AbstractProductA as* an argument.*/public anotherUsefulFunctionB(collaborator: AbstractProductA): string {const result = collaborator.usefulFunctionA();return `The result of the B1 collaborating with the (${result})`;}}class ConcreteProductB2 implements AbstractProductB {public usefulFunctionB(): string {return 'The result of the product B2.';}/*** The variant, Product B2, is only able to work correctly with the variant,* Product A2. Nevertheless, it accepts any instance of AbstractProductA as* an argument.*/public anotherUsefulFunctionB(collaborator: AbstractProductA): string {const result = collaborator.usefulFunctionA();return `The result of the B2 collaborating with the (${result})`;}}/*** The client code works with factories and products only through abstract* types: AbstractFactory and AbstractProduct. This lets you pass any factory or* product subclass to the client code without breaking it.*/function clientCode(factory: AbstractFactory) {const productA = factory.createProductA();const productB = factory.createProductB();console.log(productB.usefulFunctionB());console.log(productB.anotherUsefulFunctionB(productA));}/*** The client code can work with any concrete factory class.*/console.log('Client: Testing client code with the first factory type...');clientCode(new ConcreteFactory1());console.log('');console.log('Client: Testing the same client code with the second factory type...');clientCode(new ConcreteFactory2());
