前言
这两天复习OKHttp+Retrofit源码时,看到了一些设计模式,平时写业务逻辑或多或少会使用的,简单记录一下
区别:工厂模式和建造者模式同属于创建类模式,但关注的维度不一样,工厂模式关于重点是创建出一个产品,即对象,而建造者模式关注重点是如何创建,如何Builder、按何种顺序Builder出该产品对象,即产品组装细节。
工厂模式(定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类)优缺点
- 优点 : 封装性、扩展性、对象创建和使用分离,降低了代码耦合、减少了重复代码、统一管理创建对象的不同实现逻辑、围绕着特定的抽象产品(一般是接口)来封装对象的创建过程
- 缺点:抽象接口新增方法会增加开发成本、具体实现工厂不统一会增加代码的理解难度
建造者模式(定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示)优缺点
- 优点:封装性、建造者容易独立和扩展、便于控制细节风险(建造过程细化,不会对其他模块产生影响)
- 缺点:使用范围有限(创建的不同对象间如果差异性很大就不适用)、容易引起超大的类、代码行数增加
目录
一、工厂模式
平时用工厂模式是解决什么类型的问题?出于什么初衷引入了工厂模式?引入工厂模式后又引入了什么问题?
PS : 不要过度用设计模式,不要因为设计模式而设计模式
1、解决的问题
在数据库相关开发中,如果使用JDBC 连接数据库,假如这样写
ADriver aDriver = new ADriver();
aDriver......//数据库操作
那下次有个需求,需要把数据库从MySQL切换到Oracle,那是不是又得改一下代码
BDriver dDriver = new BDriver();
bDriver.....
这样只要新增一个不同的数据库连接,就需要显示new 一个出来,而且管理维护起来很不方便,如果是协同开发的话,B负责实现的Driver创建比较复杂,而你要使用Driver类,你说你的心会不会加速跳动起来,所以这种问题是不是很值得优化?
2、如何解决
我们期望使用B同事实现的Driver相关类时,只需要改动的地方就是切换一下驱动名称,其它都不需要修改,这样最好了,解决方法例如下面的简单工厂类
public class DriverFactory {
public static <T extends Driver> T createDriver(Class<T> c){
Driver driver = null;
try{
driver = (Driver) Class.forName(c.getName()).newInstance();
}catch (Exception e){
}
return (T)driver;
}
}
调用的时候只需要传入Driver的子类,是不是简单了,假如新增一个Driver类型,例如MongoDBDriver extend Driver,你可以很简单的通过工厂创建和使用它。
对此你应该对简单工厂有了一个简单的了解,接下来看另一个简单工厂类
public class OperationFactory {
private OperationFactory(){}
public static IOperation createrOperation(String name){
if(name == null){
return null;
}
if(name.equals("+")){
return new OperationAdd();
}else if(name.equals("-")){
return new OperationSub();
}else{
return null;
}
}
}
如果我们要加新的运算方法类,直接在工厂类中增加一个 if else 判断即可,如果要修改已有类的某个运算规则,直接修改该类即可,也就是会修改OperationFactory工厂类和已有运算类的代码,即扩展和修改都开放了,违背了开闭原则,但它仍然是一个非常实用的设计模式。还有一个问题就是每次修改工厂类的 if else ,最后将会造成工厂类庞大无比,即维护问题。
既然简单工厂模式有问题,那就必须对该问题进行解决,进而引入了工厂模式的升级版,多个工厂类,写法如下
public class OperationAddFactory implements OperationFactory {
@Override
public IOperation getOperation() {
return new OperationAdd();
}
}
public class OperationSubFactory implements OperationFactory {
@Override
public IOperation getOperation() {
return new OperationSub();
}
}
public interface OperationFactory {
IOperation getOperation();
}
每次使用如下
OperationFactory factory = new OperationAddFarcory();
IOperation operation = factory.getOperation();
这是不是解决了简单工厂模式中对修改开放的问题了(新增一个算法需求,就增加一个运算类和运算工厂) ,但是这又增加了复杂性,B同事写的每个工厂类我都得仔细了解么?有没有更好的方法让其他人使用?一般做法会新增一个协调类,用来管理工厂类,这种一般会在复杂项目里见到。
BUT,用这种多工厂方法模式,每次都新建一个工厂和对应的类,岂不是很累。确实很累,反正适合项目的就是最好的。
为了解决上面说到的这个缺点,再引入一下抽象工厂模式,看看是否能解决问题
既然是抽象工厂模式,那么理解上可以再抽象点,可以把上面提到的运算类看作不同的产品,每个工厂类对应不同的产品线,假如对运算类A有不同的运算需求,那么就产生对应的A1 A2 A3需求运算类,就比如上述的OperationAdd的相加方法,A1是直接相加,A2是直接相加后和别的数字再相加。
假如一个产品分为等级1和2,按产品等级区分,一个工厂生产等级1的产品,一个工厂生产等级2的产品,写法如下
public abstract class AbstractProductA{
}
public ProductA1 extend AbstractProductA{
}
public ProductA2 extend AbstractProductA{
}
//抽象工厂类 职责 定义每个工厂要实现的功能
public abstract class AbstractCreator{
public abstract AbstractProductA createProductA();
public abstract AbstractProductB createProductB();
}
public class Creator1 extend AbstractCreator{
public AbstractProductA createProductA(){
return new ProductA1();
}
public AbstractProductB createProductB(){
return new ProductB1();
}
}
public class Creator2 extend Creator{
public AbstractProductA createProductA(){
return new ProductA2();
}
public AbstractProductB createProductB(){
return new ProductB2();
}
}
在抽象工厂类中,如果有N个产品族,A B C .... 那么就需要创建N个创建产品方法 new ProductA1() 等等,其中Creator1 Creator2是实现共产类,如果有M个产品等级,就有M个Creator类。
可能看完上面写的还没明白为什么这样写?可以看一下如何调用的
AbstractCreator creator1 = new Creator1();
AbstractCreator creator2 = new Creator2();
AbstractProductA a1 = creator1.createProductA();
AbstractProcuctA a2 = creator2.createProductA();
可以看到在具体的业务中,不会有任何一个方法与实现类有关系,对于一个产品来说,我们只需要知道它的工厂方法,就可以直接产生一个产品对象。
回到之前对多工厂方法的缺点,新增一个算法需求,就增加一个运算类和运算工厂,这种写法很累。对比一下抽象工厂方法,可以发现新增产品C,Creator1和Creator2类里都需要增加一个createProductC()的方法,如果用多工厂类方法,就需要新增一个产品类,你可能会说违反了开闭原则,确实是,这个可以叫做有毒代码,扩展很难。
3、使用场景
当一个产品族(或一组没有任何关系的对象),都有相同的约束,例如文本编辑器和一个图片编辑器,虽然都是编辑器,但是linux和window下的文本编辑器代码实现肯定是不同的,图片编辑器类似,这里相同约束就是操作系统类型,我们可以利用工厂模式,产生不同操作系统下的编辑器和图片处理。
也可以简单理解,为了不必在创建时显示指定要创建的类型,就考虑使用工厂模式,这其实可以说是工厂模式的本质了。
二、 建造者模式
1、建造者模式
也称生成器模式,模板如下
产品类
public class Product{
public void doSomething(){
//独立业务处理
}
}
抽象建造者
public abstract class Builder{
//设置产品的不同部分,以获得不同的产品
public abstract void setPart();
//建造产品
public abstract Product buildProduct();
}
具体建造者
public class ConcreteProduct extends Builder{
//设置产品
public void setPart(){
//产品内部逻辑处理
}
public Product buildProduct(){
return product;
}
}
构建产品
public class ProductBuilder{
private Builder builder = new ConcretProduct();
public Product buildAProduct(){
builder.setPart();
return builder.buildProduct();
}
}
书上一般都这样介绍,但是实际上可以按照下面这种实现
public class Product {
private String name;
private int maxYear;
private int produceYear;
private Product() {
}
private Product(String name, int maxYear, int produceYear) {
this.name = name;
this.maxYear = maxYear;
this.produceYear = produceYear;
}
public void doSomething() {
//独立业务处理
}
public static class Builder {
private String name;
private int maxYear;
private int produceYear;
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setMaxYear(int maxYear) {
this.maxYear = maxYear;
return this;
}
public Builder setProduceYear(int produceYear) {
this.produceYear = produceYear;
return this;
}
public Product build() {
//对参数进行判空或者其他处理
return new Product(name, maxYear, produceYear);
}
}
}
调用如下
Product product = new Product.Builder().setName("产品").setMaxYear(2020).setProduceYear(2020).build();
2、优点和使用场景
建造者模式的优点在于它的独立性、封装性,客户端不需要知道产品内部组成的细节,不需要关心每一个模型背后的实现逻辑,对于第一种写法的,其Builder类的扩展性也很好的体现出来了。
它与工厂模式都是创建类模式,但是建造者模式关注的更多是相同的方法,不同的方法的执行, 也就是如何组装成一个Product类,而工厂模式关注的重点是创建,组装Product类,组装顺序不是它关注的点。