一天一个设计模式:简单工厂模式
存在的目的:
解耦,在a类中如何想使用b类的某种功能,必须new 一个b对象出来,假如有一个c类,跟b实现了同一个接口,后续想要将b替换成c就需要更改在a中的代码,以此类推,代码的耦合度会随着项目复杂度越来越高,维护成本也会越来越高。
使用简单工厂,使a类与工厂做耦合,然后工厂来提供接口的实现类即可,这样只需要对工厂进行维护就可以实现替换了成功解耦。
核心思想:
不在乎工厂是如何生产产品的,而是将产品的生产与产品的使用分开。
注:这里的产品指的是功能类的对象
实际的应用:
jdk中的线程池:ThreadPoolExecutor,根据自己的需求传入corePoolSize、maximumPoolSize、keepAliveTimem、keepAliveTimem、unit、threadFactory、handler这几个参数,new一个指定的ThreadPoolExecutor出来。
jdk提供了Executors这个类,让开发者对线程池的使用与生产分离开,开发者只需要调用不同的方法就可以获取到不同的线程池,开发者不用关心线程池的实现细节,只需要调用api即可获取不同的线程池。
如:Executors.newSingleThreadExecutor() 获取单线程的线程池
Executors.newCachedThreadPool() 获取无界线程池
优点:
简单优化了软件体系结构,明确了各自功能模块的职责和权力,
通过工厂类,外界不需要直接创建具体产品对象,只需要负责消费,不需要关心内部如何创建对象。
缺点:
如果只是使用简单的if else这样来做生产,随着产品的增多,势必要对工厂类进行不断的维护,
使用反射的工厂效率会低一些。
实现案例
以登录系统为例,支持多种登录体系:口令登录、域登录。那么需要建立一个各种登录方式都适合的接口,uml图如下:
登录接口:
public interface Login {
//登录验证
public boolean verify(String name , String password);
}
域登录:
public class DomainLogin implements Login {
@Override
public boolean verify(String name, String password) {
// TODO Auto-generated method stub
/**
* 业务逻辑
*/
return true;
}
}
口令登录:
public class PasswordLogin implements Login {
@Override
public boolean verify(String name, String password) {
// TODO Auto-generated method stub
/**
* 业务逻辑
*/
return true;
}
}
工厂类LoginManager,根据需求创建对象,如果不合法就跑出异常,会返回一个Runtime异常
public class LoginManager {
public static Login factory(String type){
if(type.equals("password")){
return new PasswordLogin();
}else if(type.equals("passcode")){
return new DomainLogin();
}else{
/**
* 这里抛出一个自定义异常会更恰当
*/
throw new RuntimeException("没有找到登录类型");
}
}
}
测试类:
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
String loginType = "password";
String name = "name";
String password = "password";
Login login = LoginManager.factory(loginType);
boolean bool = login.verify(name, password);
if (bool) {
/**
* 业务逻辑
*/
} else {
/**
* 业务逻辑
*/
}
}
}
实际上的结构图:
基于上面的,使用反射来实现
public class LoginManager { public static Login factory(String path) { Class<?> c = Class.forName(fruitPath); return (Fruit)c.newInstance(); } }
这样仅需要控制入参就可以获取对应的实例了,不需要那么多if else了结构上更加清爽。
参考链接:
https://www.cnblogs.com/java-my-life/archive/2012/03/22/2412308.html
https://www.cnblogs.com/xrq730/p/4902597.html