设计模式

工厂模式

一、工厂模式的概念

为什么要使用工厂模式?
对于一个典型的Java应用而言,应用之间存在着复杂的调用关系(Spring中把这种调用关系称之为依赖关系)。当A对象需要调用B对象时,许多人想到先new
一个B类的实例,然后再A类实例中调用B实例的方法。从语法角度讲这种做法没有任何问题(这种方式称之为硬编码耦合),但是一旦系统重构:需要使用C类对象替换B类对象且A类的代
码在多种地方以这种硬编码的方式耦合了B类,则需要每一处都要改动。。这是一件很可怕的事请。
现在换一种姿势尝试一下:对于A类对象,它只需要调用B类对象的方法,而不关心B对象是如何实现的,如何创建的。于是可以考虑让Blei实现一个IB的
接口,而A类只需要与IB接口耦合--A类并不直接使用new关键字来创建B,而是重新定义一个B的工厂类IBFactory,专门生成IB的实例;而A类通过调用
IBFactory的方法得到IB对象实例。如果系统需要重构:需要用C代替B,只需让C也实现IB接口,并改变IBFactory中生产IB实例的方法代码,
让其生产C(实现IB接口)实例即可,由于所有依赖IB实例的对象都是通过工厂来获取IB实例的,所有他们都将改为获得C的实例,从而完成了重构。

这种将多个类对象交给工厂列来生成的设计方式称为简单工厂模式

二、简单工厂的实现

有这样一个需求:假设程序中有个个Computer对象需要一个输出设备,现在有两个选择,一是直接让Computer对象依赖一个Printer对象,二是Computer
依赖一个Output(接口)属性。在这种场景下,使用简单工厂模式是代码具有更好的维护性、可扩展性。根据工厂模式,程序用过让Computer拥有
一个Output属性,将Computer类与Printer类分离开来。Computer只需要面向Output接口编程即可。具体实现如下:
1、首先定义一个Output接口,表示任何输出设备都应当拥有的功能即方法
1
2
3
4
public interface Output {
public void out();
public void getData(String data);
}
2、定义一个Printer类,表示一个具体厂家的的输出设备--打印机,例如:SONY的,其一定是实现了Output接口的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Printer implements Output {

private List<String> buffer = new ArrayList<>();
@Override
public void out() {
System.out.println("这是sony的打印机在执行out方法"+buffer.get(0));
}

@Override
public void getData(String data) {
buffer.add(data);
System.out.println("这是sony的打印机在执行getData方法"+data);
}
}
3、现在实现了输出设备的标准(Output接口)的具体产品(Printer类)打印机的设计有了,再定义一个生产输出设备的工厂吧:

1
2
3
4
5
public class OutputFactory {
public Output getOutput() {
return new Printer();
}
}
OutputFactory工厂的getOutput()方法就是负责生产打印机的方法。 4、在Computer中调用输出设备Output的方法,Computer与接口Output是耦合的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Computer {

private Output output;

public Computer(Output output) {
this.output = output;
}

public void keyIn(String msg) {
output.getData(msg);
}

public void print(){
output.out();
}
}
5、现在可以进行打印了:
1
2
3
4
5
6
7
8
9
10
11
12
13
    public class main {
public static void main(String[] args) {
//创建一个工厂
OutputFactory of = new OutputFactory();
//从工厂中生产一件Output标准的产品
Computer computer = new Computer(of.getOutput());
//调用
computer.keyIn("sssss");
computer.print();
}


}
打印结果为:
1
2
这是一个性能一般的打印机在执行getData方法sssss
这是一个性能一般的打印机在执行out方法sssss
6、现在sony的打印机已经不能满足我的需求的,需要换一个更好的打印机,于是定义一个BetterPrinter类,实现了Output接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
public class BetterPrinter implements Output {

private List<String> buffer = new ArrayList<>();
@Override
public void out() {
System.out.println("这是一个性能更好的sony的打印机在执行out方法"+buffer.get(0));
}

@Override
public void getData(String data) {
System.out.println("这是一个性能更好的sony的打印机在执行out方法"+buffer.add(data));
}
}
修改工厂的生产方法,让其生产BetterFactory产品:
1
2
3
4
5
public class OutputFactory {
public Output getOutput() {
return new BetterPrinter();
}
}
打印接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
 public class main {
public static void main(String[] args) {
//创建一个工厂
OutputFactory of = new OutputFactory();
//从工厂中生产一件Output标准的产品
Computer computer = new Computer(of.getOutput());
//调用
computer.keyIn("sssss");
computer.print();
}


}
简单工厂模式的优势:让对象的调用者和对象的创建过程分离,当调用者需要对象时,直接向工厂请求即可,从而避免了对象的调用者与对象的实现类以硬编码的方式耦合,以提高系统的可维护性、可扩展性。
缺陷:当产品修改时,工厂类也需要做相应的修改。

三、工厂方法

简单工厂模式里,系统使用工厂类生产所有产品实例,且该工厂决定生产哪个类的实例,即该工厂负责所有的逻辑判断、实例创建等工作。
如果不想在工厂里进行逻辑判断,程序可以为不同的产品提供不同的工厂,不同的工厂类生产不同的产品。例如上面的Printer、BetterPrinter
分别提供PrinterFactory和BetterFactory工厂类,这就无需在工厂类中进行复杂的逻辑判断。

下面展示将一个Outputfactory改为一个接口,并为该接口提供两个实现类:PrinterFactory和BetterPrinterFactory。

1、Outputfactory接口:
1
2
3
public interface OutputFactory {
Output getOutput();
}
2、实现一个PrinterFactory和一个BetterPrinterfactory:
1
2
3
4
5
6
public class PrinterFactory implements OutputFactory {
@Override
public Output getOutput() {
return new Printer();
}
}
1
2
3
4
5
6
public class BetterPrinterFactory implements OutputFactory {
@Override
public Output getOutput() {
return new BetterPrinter();
}
}
其中Printer与BetterPrinter实现与上面一样。
3、Computer实现与上面一样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Computer {

private Output output;

public Computer(Output output) {
this.output = output;
}

public void keyIn(String msg) {
output.getData(msg);
}

public void print(){
output.out();
}

public static void main(String[] args) {
OutputFactory of = new PrinterFactory();
Computer computer = new Computer(of.getOutput());
computer.keyIn("this is Bob");
computer.print();
}
}
运行程序后:
1
2
3

这是一个性能一般的sony打印机在输入数据this is Bob
这是一个性能一般的sony打印机在打印。。this is Bob
从上面的代码可以看出,对于工厂方法的设计架构,客户端代码成功的与被调用对象的实现类分离,但是带来了另一种耦合:客户端代码与不同工厂的耦合。这依然是一个问题。

四、抽象工厂模式

为了解决客户端代码与不同工厂类耦合的问题,接着再考虑增加一个工厂类,该工厂类不是生产Output对象,而是生产OutputFactory实例--简而言之,这个工厂不制造具体的被调用对象,而是生产不同工厂对象。这个特殊的工厂类称为抽象工厂类,这种设计方式也被称为抽象工厂模式。

于是,新增一个OutputFactoryFactory工厂类,该工厂类通过了一个getOutputFactory(String type)方法,该方法返回一个OutputFactory工厂实例。下面是该抽象工厂类的实例:
1
2
3
4
5
6
7
8
9
public class OutputFactoryFactory {
public static OutputFactory getOutputFactory(String type) {
if (type.equalsIgnoreCase("better")) {
return new BetterPrinterFactory();
}else {
return new PrinterFactory();
}
}
}
而客户端的调用方式为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Computer {

private Output output;

public Computer(Output output) {
this.output = output;
}

public void keyIn(String msg) {
output.getData(msg);
}

public void print(){
output.out();
}

public static void main(String[] args) {
// OutputFactory of = new PrinterFactory();
OutputFactory of = OutputFactoryFactory.getOutputFactory("better");
Computer computer = new Computer(of.getOutput());
computer.keyIn("this is Bob");
computer.print();
}
}
通过抽象工厂的设计模式,系统可以让客户端与被调用对象的实现类、具体的工厂类分离。
文章目录
  1. 1. 一、工厂模式的概念
  2. 2. 二、简单工厂的实现
  3. 3. 三、工厂方法
  4. 4. 四、抽象工厂模式