设计模式之工厂方法模式
设计模式总结
什么是设计模式?
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。我们使用设计模式可以让代码更容易被重用,更容易让人们理解的,让代码实现高内聚低耦合。应用成熟的设计模式能够增强代码的可复用性、可扩展性与可维护性。
设计模式的分类
GoF(《设计模式:可复用面向对象软件的基础》的作者)总结的设计模式虽然只有23种,但是现在的设计模式已经超过了23种,我们目前先把23种设计模式学好就可以了。
设计模式我们能不能用上呢?
- 如果上街买菜,但是不会数学计算,能买到菜,但是有可能会被黑心的商家坑了
- 当买房子装修时,高中学的立体几何就有用了
- ... ...
设计模式也是一样的,如果你的代码多了却没用用设计模式,那么代码将一团糟,很难维护,别人想修改代码却无从下手,那么代码将牵一发动全身,这时候,设计模式就很重要了。
-
创建型:除了直接new来实例化对象外,提供了多种隐藏创建逻辑的生成对象的方法
-
结构型:通过对象和类的组合,得到新的结构和功能
-
行为型:解决对象之间的通行和功能职责分配
常用设计模式
我们今天讲一讲关于工厂的设计模式...
几个原则
在此开始之前,先了解几个原则:
- 单一职责原则:对于一个类而言,应该仅有一个引起它变化的原因
- 开闭原则:软件实体应该(类、模块、函数)应该可以扩展,但是不可以修改(可扩展,不可修改)
- 依赖倒转原则:
- 高层模块不应该依赖低层模块,两个都应该依赖抽象
- 抽象不应该依赖细节,细节应该依赖抽象
- 里氏代换原则:子类型必须能够替换掉他们的父类型(子类可以以父类的身份出现,用父类的引用指向子类对象)
- 迪米特法则:如果两个类不必彼此直接通信,那这两个类就不应当发生直接的相互作用。如果其中的一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用
简单工厂模式
现在我们有一个需求,要我们做一个计算器出来,可能会这么写:
package top.linzeliang.designpattern
import java.util.Scanner;
public class Program {
static void main(String[] args) {
Scanner sc = new Scanner(System.in);
//输入数据
System.out.println("请输入数字A: ");
double a = sc.nextDouble();
System.out.println("请选择运算符(+ - * /): ");
String operator = sc.next();
System.out.println("请输入数字B: ");
double b = sc.nextIntDouble();
double result = 0;
//Java7之后switch中支持使用String
//选择运算符
switch (operator) {
case "+":
result = a + b;
break;
case "-":
result = a - b;
break;
case "*":
result = a * b;
break;
case "/":
if (b != 0) {
result = a / b;
} else {
System.out.println("除数不能为0");
}
break;
}
//输出
System.out.println("计算结果是:" + result);
}
}
这样子写时没错,我们之前学C的时候就是这么写的,但是这个程序只能满足当前的需求,不容易维护,也不容易拓展,如果我要增加求余功能呢,那这个代码又得重新改了(违背了开闭原则)。
在三国时期,发明了刻版印刷,为印刷书方便了很多,不用再手抄字了,只要一个刻版印刷,就可以制作出很多的书,但是,如果在刻版印刷做完了之后,发现文章需要改动,或者文章写错了写字,那就得需要重头开始再刻一遍了,于是后来出现了活字印刷,只要把需要替换的字拿出来即可,效率大大提升
于是我们可以考虑使用封装继承多态把程序的耦合度降低,要可维护、可复用、可扩展
public class OperationFactory {
public static void main(String[] args) {
//通过工厂创建对象
Operation oper = OperationFactory.createOperate("+");
oper.setA(1);
oper.setB(2);
System.out.println(oper.getResult());
//输出3
}
}
class Operation {
private double a;
private double b;
public double getA() {
return a;
}
public void setA(double a) {
this.a = a;
}
public double getB() {
return b;
}
public void setB(double b) {
this.b = b;
}
public double getReuslt() {
double result = 0;
return result;
}
}
class OperationAdd extends Operation {
//重写getResult方法
@Override
public double getReuslt() {
double result = 0;
//进行计算
result = a + b;
return result;
}
}
class OperationSub extends Operation {
//重写getResult方法
@Override
public double getReuslt() {
double result = 0;
//进行计算
result = a - b;
return result;
}
}
...
/**
* 工厂运算类
*/
public class OperationFactory {
public static Operation createOperation(String operate) {
Operation oper = null;
switch (operate) {
case "+":
oper = new OperationAdd();
break;
case "-":
oper = new OperationSub();
break;
case "*":
oper = new OperationMul();
break;
case "/":
oper = new OperationDiv();
break;
}
return oper;
}
}
这次改造之后,每个运算方法通过继承Operation类,用OperationFactory工厂来操控这些具体的实现类,避免了与具体产品的依赖
简单工厂模式优点:工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖
简单工厂模式缺点:违背了开闭原则
简单工厂模式的UML类图
工厂方法模式
对于上面的简单工厂模式来说,如果要增加一个就余的类,那么就先需要创建一个继承Operation的子类,然后修改工厂中的方法,再case中加入判断,然后还要再去更改客户端,这样子不但没有简化难度,而且害增加了许多类和方法,把问题复杂化了,而且违背了开闭原则,所以,可以考虑一下工厂方法模式了:
工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类
工厂方法模式的UML类图:
即:

代码实现一下:
//新增一个OperationFactory接口
interface Operation {
Operation createOperation();
}
class AddFactory implements OperationFactory {
@Override
public Operation createOperation() {
return new OperationAdd();
}
}
...
public class Test {
public static void main(String[] args) {
//创建加法工厂对象
OperationFactory factory = new AddFactory();
Operation operarion = factory.createOperation();
operation.setA(1);
operation.setB(2);
System.out.println(operation.getResult());
//结果3
}
}
工厂方法模式克服了简单工厂模式的开闭原则,又保持了封装对象创建对象过程的优点,在要更换对象时,不需要做大的改动就可以实现。但是由于每增加一个产品,就需要增加一个产品工厂的类,增加了额外的开发量(利用反射技术可以解决这个问题)

浙公网安备 33010602011771号