最近在学习Spring的源码,虽然对源码认识不深刻,却勾起了对设计模式的热爱,越发能理解,这些理论的东西为什么值得被称赞。设计模式本身系统的学习过三遍了,但依旧停留在记不住的情况下,这次的总结源于Spring源码的学习,自认为有了一定的认知,如果存在理解偏颇之处,恳请各位大神斧正,小妹不胜感激。
三工厂模式最重要的认知,属于创建型。这意味着简单工厂,工厂模式,抽象工厂存在的意义是为了创建对象。在Java中称为Object,如果放到Spring中则称为创建bean。其实object等同于bean的概念,只不过Spring中有对bean的管理,即BeanFacotry。由此可见Spring的核心还是利用了工厂模式。学会三工厂更利于对Spring源码的学习。
下面举个例子,详细介绍下三个模式的不同之处
编写具有加减乘除运算方式的计算器,最简单的业务代码
public abstract class Operation {
private double numberA=0;
private double numberB=0;
public double getNumberA() {
return numberA;
}
public void setNumberA(double numberA) {
this.numberA = numberA;
}
public double getNumberB() {
return numberB;
}
public void setNumberB(double numberB) {
this.numberB = numberB;
}
public abstract double getResult();
}
/**
* 加法
*/
public class OperationAdd extends Operation {
@Override
public double getResult() {
double result = 0;
result = getNumberA()+getNumberB();
return result;
}
}
/**
* 减法
*/
public class OperationSub extends Operation {
@Override
public double getResult() {
double result = 0;
result = getNumberA()-getNumberB();
return result;
}
}
/**
* 乘法
*/
public class OperationMul extends Operation {
@Override
public double getResult() {
double result = 0;
result = getNumberA()*getNumberB();
return result;
}
}
/**
* 除法
*/
public class OperationDiv extends Operation {
@Override
public double getResult() {
double result = 0;
result = getNumberA()/getNumberB();
return result;
}
}
简单工厂——用一个单独的类来做实例对象的创建
/**
* 运算工厂
*/
public class OperationFactory {
public static Operation createOperate(String operate) {
Operation operation = null;
switch (operate) {
case "+":
operation = new OperationAdd();
break;
case "-":
operation = new OperationSub();
break;
case "*":
operation = new OperationMul();
break;
case "/":
operation = new OperationDiv();
break;
}
return operation;
}
}
客户端执行逻辑
public class Client {
public static void main(String[] args) {
/**
* 简单工厂的核心,对象的创建本质放到服务端的,客户端的作用只是决定使用具体逻辑处理方式
*/
Operation operate = OperationFactory.createOperate("+");
operate.setNumberA(1);
operate.setNumberB(2);
double result = operate.getResult();
System.out.println(result);
}
}
简单工厂模式最大的优点在于工厂类中包含了必要的逻辑判断,根据客户端的条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。
但是简单工厂并不能成为一个完整的设计模式,是因为它并不符合开闭原则,例如 再加一个开根号,运算工厂就需要被修改。因此在产生了工厂模式,遵循开闭原则,将程序设计更安心。
大家要在简单工厂理解一个点,运算类本质上是由运算工厂创建的(服务端,非客户行为创建),客户端只是决定具体的使用逻辑,也就造成无法遵循开闭原则
依旧使用上面的例子,假设此增加一个求A的B次方,用简单工厂方式,就是增加一个Operation的子类,并且修改OperationFactory类即可。但是使用工厂模式该怎么实现呢?
/**
* A的B次方
*/
public class OperationPower extends Operation {
@Override
public double getResult() {
double result = 1;
int B = Double.valueOf(getNumberB()).intValue();
for (int i = 0; i < B; i++) {
result= result*getNumberA();
}
return result;
}
}
工厂模式 ,定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类中。
/**
* 定义一个创建对象的接口,让子类实现接口,让类的实例化延迟早子类实现中
*/
public interface IFacotry {
/**
* 定义创建类的方法
* @return
*/
Operation createOperation();
}
延迟到子类中的创建
/**
* 创建A的B次方的工厂
*/
public class OparetionPowerFactory implements IFacotry {
@Override
public Operation createOperation() {
return new OperationPower();
}
}
客户端的使用方式
public static void main(String[] args) {
/**
* 客户决定逻辑,自行使用对应的工厂
*/
OparetionPowerFactory factory = new OparetionPowerFactory();
/**
* 通过工厂,将类的实现延迟到子类工厂中,新增其他运算逻辑,则不需要额外修改服务端代码,新增即可
*/
Operation operation = factory.createOperation();
operation.setNumberA(2);
operation.setNumberB(3);
double result = operation.getResult();
System.out.println(result);
}
再解释一遍:工厂方法实现是,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,也就是说工厂方法把简单工厂的内部逻辑判断移到了客户端代码来进行,如果你想要加功能,本来是改工厂类,而现在是修改客户端。
工厂模式解决了一条生产线的问题,但是如果同时有多个生产线生产同样的内容,那么又该如何处理呢?
通常常见的案例就是换数据库问题,使用mysql需要有于表对应的user类,school类,那么换成oracle同样有这诉求。此时我们不可能每换一种数据库就新增一组逻辑上的接口吧。而是希望所有复杂的创建流程,全部由程序创建处理,这样广大同胞再也不用加班了!
被动创建对象的方式,最常见的就是反射,反射通过全类名加载,可以实现自动创建类。
抽象工厂模式,提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
先使用工厂方式来实现下,上述案例 关于User的两种数据库改造
public class User {
}
/**
* user 接口
*/
public interface IUser {
void insert(User user);
User getUser(int id);
}
/**
* 定义一个创建对象的接口,让子类实现接口,让类的实例化延迟早子类实现中
*/
public interface IFacotry {
/**
* 定义创建类的方法
* @return
*/
IUser createUser();
}
public class MysqlUser implements IUser {
@Override
public void insert(User user) {
System.out.println("mysql create user");
}
@Override
public User getUser(int id) {
System.out.println("mysql query user");
return null;
}
}
public class OracleUser implements IUser {
@Override
public void insert(User user) {
System.out.println("oracle create user");
}
@Override
public User getUser(int id) {
System.out.println("oracle query user");
return null;
}
}
public static void main(String[] args) {
/**
* 客户决定逻辑,自行使用对应的工厂
*/
MysqlFacotry factory = new MysqlFacotry();
IUser user = factory.createUser();
user.insert(new User());
user.getUser(1);
}
如果此时在新增一个school类呢?那么在新增一种数据库方式呢?简直是噩梦。
编程是门艺术,这样大批量的改动,不是很明智的选择。
用反射+配置文件的方式改造它。
public class DataAccess {
private final static String path = "com.mandy.factory.service";
private String db;
public DataAccess(String db) {
this.db = db;
}
public IUser createUser() {
// com.mandy.factory.service.MysqlUser
String classPath = path +"."+ db + "User";
Class<?> clazz = null;
try {
clazz = Class.forName(classPath);
return (IUser) clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
public class Client {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String db = scanner.nextLine();
DataAccess dataAccess = new DataAccess(db);
IUser user = dataAccess.createUser();
user.getUser(1);
}
}
到此为止一个object的创建可以通过读取配置+反射的方式创建,其实从抽象工厂模式就已经可以看到Spring最初的设计了,解决不断去new的方式,将手动的new改为由Spring去创建,创建的方式本质上同样是使用的反射。
个人理解 如果说在Java开发过程真正的巨人的是什么,相比各种各样伟大的框架,我坚信最伟大的基石就是这些设计模式。
不想做架构师的技术负责人不是好的开发。未来要选择的道路都是联通,所以要学的还有很多!