0%

设计模式之Factory Method模式

用Template Method模式来构建生成实例的工厂,就是Factory Method模式.
在Factory Method模式中,父类决定实例的生成方式,但并不决定所要生成的具体的类,具体的处理全部交给子类负责.这样就可以将生成实例的框架和实际负责生成实例的类解耦

介绍

  • 意图: 定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
  • 何时使用:我们明确地计划不同条件下创建不同实例时。
  • 应用实例:
    1. 您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。
    2. Hibernate 换数据库只需换方言和驱动就可以。
  • 优点:
    • 一个调用者想创建一个对象,只要知道其名称就可以了。
    • 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
    • 屏蔽产品的具体实现,调用者只关心产品的接口。
  • 缺点:
    • 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
  • 使用场景:
    • 日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
    • 数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
    • 设计一个连接服务器的框架,需要三个协议,”POP3”、”IMAP”、”HTTP”,可以把这三个作为产品类,共同实现一个接口。
  • 注意事项:
    作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

    示例代码

Factory.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.edu.tju.GOF.Factory_Method.framework;

public abstract class Factory {
public final Product create(String owner) {
Product p = createProduct(owner);
registerProduct(p);
return p;
}

protected abstract Product createProduct(String owner);

protected abstract void registerProduct(Product product);

public abstract void getAllProductOwner();
}

Product.java

1
2
3
4
5
package com.edu.tju.GOF.Factory_Method.framework;

public abstract class Product {
public abstract void use();
}

IDCard.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.edu.tju.GOF.Factory_Method.idcard;

import com.edu.tju.GOF.Factory_Method.framework.*;

public class IDCard extends Product {
private String owner;

IDCard(String owner) {
System.out.println("制作" + owner + "的ID卡。");
this.owner = owner;
}

public void use() {
System.out.println("使用" + owner + "的ID卡。");
}

public String getOwner() {
return owner;
}
}

IDCardFactory.java

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
package com.edu.tju.GOF.Factory_Method.idcard;

import java.util.ArrayList;
import java.util.List;

import com.edu.tju.GOF.Factory_Method.framework.*;

public class IDCardFactory extends Factory {
List owners = new ArrayList();

public Product createProduct(String owner) {
System.out.println("为 " + owner + " 创造产品成功...");
return new IDCard(owner);
}

public void registerProduct(Product p) {
String owner = ((IDCard) p).getOwner();
owners.add(owner);
System.out.println("注册 " + owner + " 的产品成功...");
}

public void getAllProductOwner() {
for (int i = 0; i < owners.size(); i++) {
System.out.println("产品用户:" + owners.get(i));
}
}
}

Main.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.edu.tju.GOF.Factory_Method;

import com.edu.tju.GOF.Factory_Method.idcard.*;
import com.edu.tju.GOF.Factory_Method.framework.*;
public class Main {
public static void main(String[] args) {
Factory factory = new IDCardFactory();
Product card1 = factory.create("rlj");
Product card2 = factory.create("jason");
Product card3 = factory.create("zjr");
card1.use();
card2.use();
card3.use();
factory.getAllProductOwner();
}
}

登场角色

  • Product (产品)
    Product角色属于框架的一方,是一个抽象类.它定义了在Factory Method模式中生成的那些实例所持有的接口(API),但具体的处理则由子类ConcreteProduct角色决定.在示例中由Product类扮演此角色.
  • Creator (创建者)
    Creator角色属于框架的一方,它是负责生成Product角色的抽象类,但具体的处理则由子类ConcreteCreator角色决定, 在示例中由 Factory类 扮演此角色.
    Creator角色对实际负责生成的实例ConcreteCreator角色一无所知,它唯一知道的就是,只要调用Product角色和生成实例的方法,就可以生成Product的实例. createProduct就是生成实例的方法.不用new关键字来生成实例,而是调用生成实例的专用方法来生成实例,这样就可以防止父类与其他具体类耦合.
  • ConcreteProduct (具体的产品)
    ConcreteProduct角色属于具体加工的以放,它决定了具体的产品.在示例程序中,由IDCard扮演此角色.
  • ConcreteCreatror (具体的创建者)
    ConcreteCreatror角色属于具体加工的一方,它负责生成具体的产品.在示例程序中,由IDFactory类来扮演此角色.
  • Factory Method模式的类图

工厂方法模式适用场景

在以下情况下可以使用工厂方法模式:

  • 一个类不知道它所需要的对象的类
    在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。

  • 一个类通过其子类来指定创建哪个对象
    在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。

  • 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。

    总结

    联系迭代器模式,迭代器间接地使用了工厂方法来创造迭代器对象。在这里我们思考可不可以将工厂类和产品类这两个抽象类改成接口,答案是不能,因为我们使用了模板,模板的定义就要求我们能在父类中定义实际的概括性的操作(模板方法),而接口只能定义协议,不能定义实现;第二个问题,在工厂类里面我们可不可以直接创造产品,答案是不能,因为如果在工厂类的创造产品方法中我们使用new Product();来做的话,Product就不能为抽象类了,因为抽象类不能new出对象。对于产品类的实现的构造方法里面我们可以看到使用了默认的定义域,这样只能在本包中使用该方法,保证了使用权限安全。
    迭代器使用了工厂方法,工厂方法使用了模板方法,都是用了继承、接口、抽象等机制因此我们可以看到设计模式不是独立的,而是相互之间有着关系和区别的,在学习的时候我们要善于总结设计模式之间的共同之处和不同之处,活学活用.

生成示例,方法的三种实现方式

createProduct方法是抽象方法,也就是说需要在子类中实现该方法
createProduct方法的实现方式一般有以下3种.

  • 指定其为抽象方法
    指定为抽象方法后,子类就必须实现该方法.如果子类不实现该方法,编译器将会报告编译错误.

    1
    public abstract Product createProduct(String name);
  • 为其实现默认处理
    实现默认处理后,如果子类没有实现该方法,将进行默认处理.

    1
    2
    3
    public Product createProduct(String name){
    return new Product(name);
    }

    不过,这时是使用new关键字创建出实例的.因此不能将Product类定义为抽象类

  • 在其中抛出异常
    默认处理若为抛出异常,这样一来,如果未在子类种实现该方法,程序就会再运行的时候出错(报错,告知开发人员没有createProduct方法)

    1
    2
    3
    public Product createProduct(String name){
    throw new FactoryMethodRuntimeException();
    }

    不过,需要另外编写FactoryMethodRuntimeException 异常类

Thank you for your support