0%

工厂方法模式与原型模式

工厂方法模式

定义

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

模式结构

  • Product:抽象产品
  • ConcreteProduct:具体产品
  • Factory:抽象工厂
  • ConcreteFactory:具体工厂

工厂方法模式

优点

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

缺点

  • 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
  • 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。

实例

  • 背景:小成有一间塑料加工厂(仅生产A类产品);随着客户需求的变化,客户需要生产B类产品;

  • 冲突:改变原有塑料加工厂的配置和变化非常困难,假设下一次客户需要再发生变化,再次改变将增大非常大的成本;

  • 解决方案:小成决定置办塑料分厂B来生产B类产品;

创建抽象工厂类,定义具体工厂的公共接口

1
2
3
abstract class Factory{
public abstract Product Manufacture();
}

创建抽象产品类 ,定义具体产品的公共接口;

1
2
3
abstract class Product{
public abstract void Show();
}

创建具体产品类(继承抽象产品类), 定义生产的具体产品;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//具体产品A类
class ProductA extends Product{
@Override
public void Show() {
System.out.println("生产出了产品A");
}
}
//具体产品B类
class ProductB extends Product{
@Override
public void Show() {
System.out.println("生产出了产品B");
}
}

创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//工厂A类 - 生产A类产品
class FactoryA extends Factory{
@Override
public Product Manufacture() {
return new ProductA();
}
}

//工厂B类 - 生产B类产品
class FactoryB extends Factory{
@Override
public Product Manufacture() {
return new ProductB();
}
}

外界通过调用具体工厂类的方法,从而创建不同具体产品类的实例

1
2
3
4
5
6
7
8
9
10
11
//生产工作流程
public class FactoryPattern {
public static void main(String[] args){
//客户要产品A
FactoryA mFactoryA = new FactoryA();
mFactoryA.Manufacture().Show();
//客户要产品B
FactoryB mFactoryB = new FactoryB();
mFactoryB.Manufacture().Show();
}
}

简单工厂模式违背了开闭原则:当需要添加新产品时,需要在工厂类的静态方法中添加新产品的创建逻辑,这样就修改了原来的代码,复用性降低。 工厂方法模式:将简单工厂模式中的工厂类用继承的方式细分。具体操作是,当需要添加新产品时,需要添加具体产品类和具体工厂类,这样完美解决了简单工厂违背开闭原则的问题。主要优点是,客户端不需要知道具体产品类的类名,只需要知道对应的工厂类。缺点是,增加一个新产品,需要增加一个新产品类和一个具体工厂类,类种类成对增加。

原型模式

原型模式是一种创建型设计模式, 使你能够复制已有对象, 而又无需使代码依赖它们所属的类。

原型模式将克隆过程委派给被克隆的实际对象。 模式为所有支持克隆的对象声明了一个通用接口, 该接口让你能够克隆对象, 同时又无需将代码和对象所属类耦合。 通常情况下, 这样的接口中仅包含一个 克隆方法。

所有的类对克隆方法的实现都非常相似。 该方法会创建一个当前类的对象, 然后将原始对象所有的成员变量值复制到新建的类中。 你甚至可以复制私有成员变量, 因为绝大部分编程语言都允许对象访问其同类对象的私有成员变量。

模式结构

客户(Client)角色客户类提出创建对象的请求;

抽象原型(Prototype)角色:这是一个抽象角色,通常由一个Java接口或者Java抽象类实现。此角色定义了的具体原型类所需的实现的方法。

具体原型(Concrete Prototype)角色:此角色需要实现抽象原型角色要求的克隆相关接口

原型模式

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
/**
* 抽象原型角色
*/
public abstract class Prototype {
private String id;

public Prototype(String id) {
this.id = id;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

/**
* 克隆自身的方法
* @return 一个从自身克隆出来的对象。
*/
public abstract Prototype clone();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class ConcretePrototype1 extends Prototype {
public ConcretePrototype1(String id) {
super(id);
}

public Prototype clone() {
Prototype prototype = new ConcretePrototype1(this.getId());
return prototype;
}
}

public class ConcretePrototype2 extends Prototype {
public ConcretePrototype2(String id) {
super(id);
}

public Prototype clone() {
Prototype prototype = new ConcretePrototype2(this.getId());
return prototype;
}
}

优点

1、将产品的创建过程封装起来,客户端不需要了解产品的具体创建流程。
2、利用Java的clone方法来创建对象肯定要比使用new来创建对象快很多,尤其是那些很复杂的对象的时候。
3、可以在不修改其他代码的情况下添加新的产品,符合“开-闭”原则。

缺点

原型模式的最大缺点就是每一个类必须都有一个clone方法,如果这个类的组成不太复杂的话还比较好,如果类的组成很复杂的话,如果想实现深度复制就非常困难了。