Factory 工厂模式
当使用new构建对象时的逻辑不够用时,需要添加一些额外的抽象时,工厂模式或许是实现该抽象最简单的模式
一般情况下,工厂模式分为三种更加细分的类型:
- 简单工厂
- 工厂方法
- 抽象工厂
简单工厂、工厂方法原理比较简单,在实际的项目中也比较常用。而抽象工厂的原理稍微复杂点,在实际的项目中相对也不常用
也有些地方将工厂模式之际分为两类:工厂方法和抽象工厂
1.简单工厂模式
基本原理
使用工厂模式写代码时常见的术语
- Concrete Creator:调用Creator(工厂方法)的客户端应用程序、类或方法
- Product Interface:描述工厂创建最终对象所需的 属性和方法的接口
- Creator:工厂类,声明工厂方法返回要创建的对象
- Concrete Product:从工厂类中返回的对象,该对象实现了相应的Product Interface
interface IProduct { //Product Interface
name: string
}
class ConcreteProduct implements IProduct {
name = ''
}
class ConcreteProductA extends ConcreteProduct { //从工厂类创建、返回的对象
constructor() {
super()
this.name = 'ConcreteProductA'
}
}
class ConcreteProductB extends ConcreteProduct {//从工厂类创建、返回的对象
constructor() {
super()
this.name = 'ConcreteProductB'
}
}
class ConcreteProductC extends ConcreteProduct {//从工厂类创建、返回的对象
constructor() {
super()
this.name = 'ConcreteProductC'
}
}
class Creator { //工厂类
static createObject(someProperty: string): IProduct { //工厂方法
if (someProperty === 'a') {
return new ConcreteProductA()
} else if (someProperty === 'b') {
return new ConcreteProductB()
} else {
return new ConcreteProductC()
}
}
}
// The Client
const PRODUCT = Creator.createObject('b') //Concrete Creator调用工厂方法
console.log(PRODUCT.name)
- 工厂类中创建对象的方法一般是以create开头
上面的方式在一些资料为称为简单工厂,简单工厂模式的代码实现中,有多处 if 分支判断逻辑,违背开闭原则,但权衡扩展性和可读性,这样的代码实现在大多数情况下(比如,不需要频繁地添加 parser,也没有太多的 parser)是没有问题的
使用情况举例:
这里先举一个使用工厂模式的例子,例如一个汽车(保时捷)配件选购应用,需要根据客户对各种配置(的选择最终根据该参数使用工厂模式例化出相应的对象。在应用程序实际运行并且用户开始使用之前,不知道用户会选择什么。
使用工厂模式的化便可以,根据选定的参数来确认具体使用那个子类来进行实例化以返回相应的对象
2.工厂方法模式
工厂方法模式使用多态等方式解决了简单简单工厂模式中if-else逻辑以让代码基本上符合开闭原则
当对象的创建逻辑比较复杂,不只是简单的 new 一下就可以,而是要组合其他类对象,做各种初始化操作的时候,就推荐使用工厂方法模式,将复杂的创建逻辑拆分到多个工厂类中,让每个工厂类都不至于过于复杂。而使用简单工厂模式,将所有的创建逻辑都放到一个工厂类中,会导致这个工厂类变得很复杂
除此之外,在某些场景下,如果对象不可复用,那工厂类每次都要返回不同的对象。如果我们使用简单工厂模式来实现,就只能选择第一种包含 if-elsl分支逻辑的实现方式。
3.抽象工厂模式
抽象工厂模式的应用场景比较特殊,没有前两种常用
抽象工厂模式在其他其创建型设计模式中添加一个抽象层,可简单的看作可以返回工厂的工厂(尽管会返回创建型设计模式中的其他实现)
基本原理
- Client:调用抽象工厂模式的应用程序
- Abstract Factory:所有子工厂通用的接口
- Concrete Factory: 抽象工厂的子工厂,包含创建一些具体对象(Concrete Product)的方法
- Abstract Product:子工厂返回的对象的抽象
- Concrete Product:最终返回的对象
interface IProduct extends IProductA, IProductB {}
//抽象工厂类
class AbstractFactory {
static createObject(factory: string): IProduct | undefined {
try {
if (["aa", "ab", "ac"].indexOf(factory) > -1) {
return FactoryA.getObject(factory[1]);
}
if (["ba", "bb", "bc"].indexOf(factory) > -1) {
return FactoryB.getObject(factory[1]);
}
throw new Error("No Factory Found");
} catch (e) {
console.log(e);
}
}
}
// The Client
let PRODUCT = AbstractFactory.createObject("ab");
console.log(PRODUCT);
PRODUCT = AbstractFactory.createObject("bc");
console.log(PRODUCT);
//factoryA
interface IProductA {
name: string
}
class ConcreteProduct implements IProductA {
name = ''
}
class ConcreteProductA extends ConcreteProduct {
constructor() {
super()
this.name = 'FactoryA:ConcreteProductA'
}
}
class ConcreteProductB extends ConcreteProduct {
constructor() {
super()
this.name = 'FactoryA:ConcreteProductB'
}
}
class ConcreteProductC extends ConcreteProduct {
constructor() {
super()
this.name = 'FactoryA:ConcreteProductC'
}
}
class FactoryA {
static getObject(some_property: string): IProductA {
try {
if (some_property === 'a') {
return new ConcreteProductA()
} else if (some_property === 'b') {
return new ConcreteProductB()
} else if (some_property === 'c') {
return new ConcreteProductC()
} else {
throw new Error('Class Not Found')
}
} catch (e) {
console.log(e)
}
return new ConcreteProduct()
}
}
//factoryB
interface IProductB {
name: string
}
class ConcreteProduct implements IProductB {
name = ''
}
class ConcreteProductA extends ConcreteProduct {
constructor() {
super()
this.name = 'FactoryB:ConcreteProductA'
}
}
class ConcreteProductB extends ConcreteProduct {
constructor() {
super()
this.name = 'FactoryB:ConcreteProductB'
}
}
class ConcreteProductC extends ConcreteProduct {
constructor() {
super()
this.name = 'FactoryB:ConcreteProductC'
}
}
class FactoryB {
static getObject(some_property: string): IProductB {
try {
if (some_property === 'a') {
return new ConcreteProductA()
} else if (some_property === 'b') {
return new ConcreteProductB()
} else if (some_property === 'c') {
return new ConcreteProductC()
} else {
throw new Error('Class Not Found')
}
} catch (e) {
console.log(e)
}
return new ConcreteProduct()
}
}
在实际使用过程中通常将其分模块到不同文件中
应用举例
假如你有一家具店,出售许多类型的家具,如椅子、桌子,它们都购买自不同的加工厂
您可以创建一个名为 FurnitureFactory 的抽象类来处理椅子和桌子工厂,从而在客户端中移除实现细节
文件模块划分模块:
furniture-factory.ts
:抽象工厂类chair-factory.ts
子工厂chairchair.ts
chair基类及chair类接口samall-chair.ts
继承chair基类并实现其接口的子类medium-chair.ts
big-chair.ts
table-factory.ts
子工厂tabletable.ts
samall-table.ts
继承table基类并实现其接口的子类medium-table.ts
big-table.ts
- client.ts:调用抽象工厂类的应用程序
Builder 建造者模式
在直接使用给构造函数传递一些必须或可选参数和使用set方法设置可选参数、使用构造函数接受必选参数,在或者在new一个新对象后就不可以使用set修改其属性值时就可以考虑建造者模式了
建造者模式试图解决的问题
- 类如何创建复杂对象的不同表示
- 如何简化创建复杂对象的类
建造者模式与工厂模式都是在运行时实例化对象
1.基本原理
- Product: 最后构造的对象/产品
- Builder Interface: 构造者需要具体实现的接口
- Builder: 构造者类,提供相应参数给构造者类和校验参数的具体方法,同时实现相应的构造者接口
- Director: 有一个
construct()
静态方法 ,当被程序调用时创建要使用Builder类的方法创建一个定制的产品/对象
class Product {
parts: string[] = []
}
interface IBuilder { //构造者类接口
buildPartA(): this
buildPartB(): this
buildPartC(): this
getResult(): Product
}
class Builder implements IBuilder {
// The Concrete Builder
product: Product
constructor() {
this.product = new Product()
}
buildPartA() {
this.product.parts.push('a')
return this
}
buildPartB() {
this.product.parts.push('b')
return this
}
buildPartC() {
this.product.parts.push('c')
return this
}
getResult() {
return this.product
}
}
//可以创建多个Director来自定义创建多种不同的对象
class Director {
static construct() {
'Constructs and returns the final product'
return new Builder()
.buildPartA()
.buildPartB()
.buildPartC()
.getResult()
}
}
// The Client
const PRODUCT1 = Director.construct()
console.log(PRODUCT1.parts)
[ 'a', 'b', 'c' ]
2.应用举例
假设现在要实现一个程序:资源池配置类 ResourcePoolConfig
,其成员变量和方法如下,而且不能在实例化出具体对象后使用set方法修改设定其成员属性值。
client.ts
import ResourcePoolConfigDirectors from "./ResourcePoolConfig-director";
const test = ResourcePoolConfigDirectors.structure();
console.log(test);
ResourcePoolConfig-director
import ResourcePoolConfigBuilder from "./ResourcePoolConfig-builder";
import ResourcePoolConfig from "./ResourcePoolConfig";
export default class ResourcePoolConfigDirectors {
static structure(): ResourcePoolConfig {
return new ResourcePoolConfigBuilder()
.setName("hahah")
.setMaxIdle(8)
.setMaxTotal(10)
.setMinIdle(2)
.build();
}
}
ResourcePoolConfig-builder.ts
import ResourcePoolConfig from "./ResourcePoolConfig";
interface IResourcePoolConfigBuilder {
resourcePoolConfig: ResourcePoolConfig;
setName(name: string): this;
setMaxTotal(maxToal: number): this;
setMaxIdle(maxIdle: number): this;
setMinIdle(maxIdle: number): this;
build(): ResourcePoolConfig; //校验方法
}
export default class ResourcePoolConfigBuilder
implements IResourcePoolConfigBuilder
{
resourcePoolConfig: ResourcePoolConfig;
constructor() {
this.resourcePoolConfig = new ResourcePoolConfig();
}
setName(name: string): this {
if ((name = "")) {
//错误处理或者提示
}
this.resourcePoolConfig.name = name;
return this;
}
setMaxTotal(maxToal: number): this {
if (maxToal <= 0) {
//错误处理
}
this.resourcePoolConfig.maxToal = maxToal;
return this;
}
setMaxIdle(maxIdle: number): this {
if (maxIdle <= 0) {
//错误处理
}
this.resourcePoolConfig.maxIdle = maxIdle;
return this;
}
setMinIdle(minIdle: number): this {
if (minIdle <= 1) {
//错误处理
}
this.resourcePoolConfig.minIdle = minIdle;
return this;
}
build(): ResourcePoolConfig {
if ((this.resourcePoolConfig.name = "")) {
// 错误处理
}
if (this.resourcePoolConfig.maxIdle > this.resourcePoolConfig.maxToal) {
//错误处理
}
if (
this.resourcePoolConfig.minIdle > this.resourcePoolConfig.maxToal ||
this.resourcePoolConfig.minIdle > this.resourcePoolConfig.maxIdle
) {
//错误处理
}
return this.resourcePoolConfig;
}
}
ResourcePoolConfig.ts
export default class ResourcePoolConfig {
name: string = "";
maxToal: number = 8;
maxIdle: number = 8;
minIdle: number = 0;
construction(): string {
return `资源名称为 ${this.name},最大资源数量为 ${this.maxToal},最大空闲资源数量为${this.maxIdle},
最小资源数量为${this.minIdle}`;
}
}
3.与工厂模式的区别
示例理解就行不用死记概念:顾客走进一家餐馆点餐,我们利用工厂模式,根据用户不同的选择,来制作不同的食物,比 如披萨、汉堡、沙拉。对于披萨来说,用户又有各种配料可以定制,比如奶酪、西红柿、起司,我们通过建造者模式根据用户选择的不同配料来制作披萨