- 工厂模式
- 1. 简单工厂
- 1.1 角色及其职责
- 1.2 UML类图
- 1.3 产品工厂实例
- 1.4 工厂使用
- 1.5 简单工厂在框架中的应用
- 1.6 优缺点
- 2. 工厂方法(鸡肋)
- 3. 抽象工厂模式
- 单例模式
- 建造者模式
- 概念
- 示例
- 框架中的使用
- 原型模式
- 原型模式适用场景
- 深克隆
- 框架中的使用
简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式。
1.1 角色及其职责| 角色 | 职责 |
|---|---|
| 工厂(Creator)角色 | 简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。 |
| 抽象产品(Product)角色 | 简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。 |
| 具体产品(Concrete Product)角色 | 是简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。 |
以笔者所在金融公司金融产品为例,不同的金融产品有这不同的‘授信’,‘借款’风控流程方法。
- CLProductService接口是金融产品顶层接口,该接口约束不同产品的授信、借款等方法
- AbstractCLProductService 是针对CLProductService抽象事件,针对一些通用的方法进行实现比如注册、绑卡等。
- DefaultCLProductService、DifferentCLProductService、StandardCLProductService具体的金融产品实现,主要针对不同产品的授信、借款流程进行具体的实现。
- ProductFactory 具体的工厂对象,针对不同的产品类型获取对应的产品实现进行相关业务流程处理。
public class ProductFacotry implements ApplicationContextAware {
//产品bean实例名称后缀
private static String serviceNameSubffix = "CLProductService";
// spring上下文环境,用于从spring中获取产品对象
private ApplicationContext applicationContext;
public CLProductService getSpringInstance(ProductTypeEnum productType){
if(productType == null || StringUtils.isBlank(productType.getCode())){
return new DefaultCLProductService();
}
StringBuffer buffer = new StringBuffer();
//Standard + CLProductService
buffer.append(productType.getCode()).append(serviceNameSubffix);
//从spring中获取bean实例
return applicationContext.getBean(buffer.toString(),CLProductService.class);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
1.4 工厂使用
@Test
public void simpleProductFactory(){
//获取标准化理财产品
CLProductService instance = productFacotry.getSpringInstance(ProductTypeEnum.STANDARD);
instance.loan(null);
//获取差异化产品
CLProductService cashInstance = productFacotry.getSpringInstance(ProductTypeEnum.DIFFERENT);
cashInstance.loan(null);
//获取默认产品产品
CLProductService springInstance = productFacotry.getSpringInstance(ProductTypeEnum.DEFAULT);
springInstance.loan(null);
}
输出结果
1.5 简单工厂在框架中的应用BeanFactory获取bean实例
最常用的spring框架中获取bean实例的BeanFactory就可以看做简单工厂的使用+单例模型组合,我们只需要编写对应的Class对象使用xml配置或者注解的形式,通过该工厂的doGetBean()获取到对应的实例。
Calendar日期对象
jdk中的Calendar的createCalendar方法根据不同时区和地理位置获取日期对象。
NumberFormat
NumberFormat类通过getInstance()方法根据特定的区域设置格式化数字。
1.6 优缺点优点
隐藏对象创建细节。
客户端不关注对象的创建过程。
通过配置文件或者spring容器管理可以通过不需要代码的情况下新加新的实现。
缺点
简单工厂模式也有他的缺点:工厂类的职责相对过重,不易于扩展过于复杂的产品结构。
2. 工厂方法(鸡肋) 简单工厂如果不使用spring管理或者配置文件,如果产品很多那么工厂类根据不同类型创建产品的if分支或者case分支会比较复杂
public static CLProductService getInstance(ProductTypeEnum productType){
if(productType == null || StringUtils.isBlank(productType.getCode())){
return new DefaultCLProductService();
}
if(productType.getCode().equals(ProductTypeEnum.STANDARD.getCode())){
return new StandardCLProductService();
}else if(productType.getCode().equals(ProductTypeEnum.DIFFERENT.getCode())){
return new DifferentCLProductService();
//...... 省略不同产品类型创建实例
}else{
return new DefaultCLProductService();
}
}
如何去掉if分支可以使用多态,针对工厂类我们可以抽象出一个工厂接口其有一个接口方法用来创建产品,不同类型的产品通过不同类型的产品工厂实现去创建。
public interface IProductFactory {
CLProductService createProductService();
}
这样当我们新增一种 理财产品的,只需要新增一个实现了 IRuleConfigParserFactory 接口的 Factory 类即可。,工厂方法模式比起简单工厂模式更加符合开闭原则,但是其使用的时候还是会如上if分支的问题,而且类结构也复杂了。
3. 抽象工厂模式工厂方法的工厂接口以产品为维度需要创建,按照工厂方法需要再额外添加三个工厂类,如果抽象工厂可以提供多个创建产品的方法则为抽象工厂。
public interface IProductFactory {
CLProductService createProductService();
CLProductService createChannelService();
}
单例模式
单例比较简单,具体的实现可以参考 单例模式那点事,这里主要讲解什么场景使用单例模式
使用场景
- 处理资源访问冲突: 比如IO资源多线程访问可以进行单例处理(加锁也成)
- 全局唯一:比如配置信息类,id生成器,连接池等
1. 单例存在哪些问题?
-
单例对 OOP 特性的支持不友好,单例式违背了基于接口而非实现的设计原则,虽然可以使用继承但是会使可读性变差。
-
单例会隐藏类之间的依赖关系。通过构造函数、参数传递等方式可以清晰了解依赖关系,但是单例不需要显示创建,不依赖参数传递。
-
单例对代码的扩展性不友好
-
单例对代码的可测试性不友好
-
单例不支持有参数的构造函数
2. 单例有什么替代解决方案?
-
为了保证全局唯一,除了使用单例,我们还可以用静态方法来实现。
-
通过工厂模式、IOC 容器(比如 Spring IOC 容器)来保证,
Builder 模式,中文翻译为建造者模式或者构建者模式,也有人叫它生成器模式。主要是为了如下场景
-
解决构造器参数过长:new创建影响可读性和易用性
-
参数需要进行校验:参数过长通过空构造+set也能解决,但是如果参数有必填校验,参数依赖
-
对象从创建好后就不允许修改
-
避免对象出现无效状态,比如长方形只设置长或者宽一个属性不能称之为长方形。
public class ResourcePoolConfig {
private String name;
private Integer minIdle;
private Integer maxIdle;
private Integer maxTotal;
//省略setter getter方法
public static Builder builder(){
return new ResourcePoolConfig.Builder();
}
private ResourcePoolConfig(Builder builder){
this.name = builder.name;
this.maxTotal = builder.maxTotal;
this.maxIdle = builder.maxIdle;
this.minIdle = builder.minIdle;
}
//使用内部类
public static class Builder {
private static final int DEFAULT_MAX_TOTAL = 8;
private static final int DEFAULT_MAX_IDLE = 8;
private static final int DEFAULT_MIN_IDLE = 0;
private String name;
private int maxTotal = DEFAULT_MAX_TOTAL;
private int maxIdle = DEFAULT_MAX_IDLE;
private int minIdle = DEFAULT_MIN_IDLE;
public ResourcePoolConfig build(){
// 校验逻辑放到这里来做,包括必填项校验、依赖关系校验、约束条件校验等
if (StringUtils.isBlank(name)) {
throw new IllegalArgumentException("name属性为空");
}
if (maxIdle > maxTotal) {
throw new IllegalArgumentException("参数不符合需求");
}if (minIdle > maxTotal || minIdle > maxIdle) {
throw new IllegalArgumentException("参数不符合需求");
}
return new ResourcePoolConfig(this);
}
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setMaxTotal(int maxTotal) {
this.maxTotal = maxTotal;
return this;
}
public Builder setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
return this;
}
public Builder setMinIdle(int minIdle) {
this.minIdle = minIdle;
return this;
}
}
}
框架中的使用
在mybatis框架中创建SqlSessionFactory对象的时候需要很多配置信息,其调用SqlSessionFactoryBuilder的build方法通过后建造者模式将众多参数传入从而构建出DefaultSqlSessionFactory。 示例如下:
org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration 创建对应的Configuration
原型模式原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。(创建性模式)
原型模式适用场景1、类初始化消耗的资源相对较多。
2、new 产生的一个对象需要非常繁琐的过程(数据准备,访问权限等)。
3、构造函数比较复杂
4、循环体中生产大量的对象时。
示例
public class BigCostObject implements Cloneable {
private String name;
private Config config;
private List urlList;
//省略setter、getter方法
public BigCostObject(String name,List urlList){
this.name = name;
this.urlList = urlList;
try {
//模拟构建对象的耗时
Thread.sleep(6*1000);
System.out.println("创建大成本对象");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public BigCostObject clone() throws CloneNotSupportedException {
//此处使用浅克隆
return (BigCostObject) super.clone();
}
}
使用
@Test
public void cloneMethod() throws CloneNotSupportedException {
String name = "资源配置";
List urlList = new ArrayList<>();
urlList.add("http://www.baidu.com");
urlList.add("http://www.jd.com");
//第一次创建对象有大约6秒的耗时
BigCostObject bigCost = new BigCostObject(name,urlList);
//再次获取对象 通过原型模式 则立马获取到对象 没有耗时
BigCostObject clone = bigCost.clone();
System.out.println(clone);
}
问题
上述方法使用的clone是浅克隆,只能针对基本数据类型进行克隆,对于引用类型 List则只会拷贝其list对象的地址,则克隆后克隆对象和源对象的list为一个内存引用。这里可以考虑使用深克隆。
深克隆- 对于对象中有引用类型属性的需要递归的调用其对象的clone方法
@NotNull
private BigCostObject deepCloneRecursion() throws CloneNotSupportedException {
BigCostObject clone = (BigCostObject) super.clone();
Config config = clone.config.clone();
clone.setConfig(config);
List newUrlList = new ArrayList<>();
System.arraycopy(urlList,0,newUrlList,0,urlList.size());
clone.setUrlList(newUrlList);
return clone;
}
- 使用ObjectInputStream进行对象的序列化和反序列化。
public BigCostObject deepClone() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos);
out.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream in = new ObjectInputStream(bis);
BigCostObject bigCost = (BigCostObject)in.readObject();
return bigCost;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
框架中的使用
bean的scope:有一种prototype,容器在接受到该类型对象请求的时候,都会重新生成一个新的对象实例给请求方,该标志内部实现就是实用来了原型模式,完成对象的生成。



