④隐藏new操作符-工厂模式

new 有什么不对

实例化对象的过程不应该总是公开的进行,这里面会有一些耦合的问题。

对 new 本身来讲并没有什么不对,需要通过 new 操作符实例化对象。但是对于设计模式来讲,new 操作符让我们针对业务编写代码,初始化的逻辑可能在一段条件语句中,如果添加了新的类,必须修改原来的代码。也就是说我们代码没有对修改关闭。

当有一些相关类实例化的时候,可能会写出下面的代码,每当加入新的类这段代码就会被修改,也就违反对修改关闭的设计原则。

1
2
3
if (picnic) {duck = new MallardDuck();}
else if (hunting) (duck = new DecoyDuck();)
else if (inBathTub) {duck = new RubberDuck()}

简单工厂

一个最简单的工厂就是把实例化对象的过程提取出来,单独放到一个工厂类中,并暴露方法,允许第三方类通过这个方法实例化对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
abstract class FruitFactory {
// 抽象工厂类的公用方法
common() {}
// 需要子类重新实现
abstract createTea(type: string): AppleTea;
}

interface AppleTea {
name(): void;
}

class Type1AppleTea implements AppleTea {
name() {
console.log("苹果茶类型1");
}
}
class Type2AppleTea implements AppleTea {
name() {
console.log("苹果茶类型2");
}
}
class Apple extends FruitFactory {
taste() {
console.log("苹果很甜");
}
public createTea(type: string): AppleTea {
if (type == "appleTeaType1") {
return new Type1AppleTea();
} else if (type == "appleTeaType1") {
return new Type2AppleTea();
}
return new Type1AppleTea();
}
}

class FruitStore {
factory: FruitFactory;
constructor(factory: FruitFactory) {
this.factory = factory;
}
makeFruitTea(type: string) {
return this.factory.createTea("type1");
}
orderTea() {
const tea = this.makeFruitTea("type1");
tea.name();
}

// 公用的茶的制作方法
// ...
}

const store = new FruitStore(new Apple());
store.orderTea();

工厂方法模式: 定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个的,工厂方法让类把实例化推迟到子类。

上面的代码中可以看到工厂是外部传入,可以创建不同类型的茶,但是茶的制作行为更像是 Store 的一部分,所以做一点改变。

  • 定义一个 Store 的抽象类,所有的 Store 都要实现这个抽象类。
  • 在 Store 抽象类中定义 createTea 的抽象方法,这个方法的内容就是之前外部传入的工厂。
  • 换句话说, 工厂方法转移到子类中实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
interface AppleTea {
name(): void;
}

class Type1AppleTea implements AppleTea {
name() {
console.log("苹果茶类型1");
}
}
class Type2AppleTea implements AppleTea {
name() {
console.log("苹果茶类型2");
}
}

abstract class FruitStore {
abstract createTea(type: string): any;
}

class AppleFruitStore extends FruitStore {
constructor() {
super();
}
public createTea(type: string): AppleTea {
if (type == "type1") {
return new Type1AppleTea();
} else if (type == "type2") {
return new Type2AppleTea();
}
return new Type1AppleTea();
}
orderTea() {
const tea = this.createTea("type1");
tea.name();
}
}

const store = new AppleFruitStore();
store.orderTea();

所有的工厂模式都是用来封装对象创建的。创建者类 需要有一个抽象创建者类,定义了一个抽象工厂方法,所有实现了抽象创建者类的子类,都可以用自己实现的工厂方法生产产品。产品类 是在创建者实例的工厂方法中创建出来的。

依赖倒置与抽象工厂

依赖倒置原则:要依赖抽象,不要依赖具体类,这句话很像是要面向接口编程,而不是具体的类。没错,但是这句话更强调,高层的组件不应该依赖低层的组件,而是应该两者都依赖于 抽象。抽象可以是接口,也可以是抽象类。

抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类

思考

  • 当只有一个创建者类的时候是否应该使用工厂模式,答案是肯定的,工厂模式让你便于扩展,并将产品类的创建与使用解耦。
  • 所有的工厂都是用来封装对象的创建
  • 简单工厂,虽然不是真正的设计模式,但仍不失为一个简单的方法,可以将客户程序从具体类解耦。
  • 工厂方法使用继承:把对象的创建委托给子类,子类实现工厂方法来创建对象。
  • 抽象工厂使用对象组合:对象的创建被实现在工厂接口所暴露出来的方法中
  • 所有工厂模式都通过减少应用程序和具体类之间的依赖促进松耦合。
  • 工厂方法允许类将实例化延迟到子类进行。
  • 抽象工厂创建相关的对象家族,而不需要依赖它们的具体类。
  • 依赖倒置原则,指导我们避免依赖具体类型,而要尽量依赖抽象。
  • 工厂是很有威力的技巧,帮助我们针对抽象编程,而不要针对具体类编程。
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2015-2025 SunZhiqi

此时无声胜有声!

支付宝
微信