学点设计模式,盘点Spring等源码中与设计模式的那些事之创建型模型
首先,在创建型模式中,包括了以下几种设计模式:
- 单例模式
- 原型模式
- 工厂方法模式
- 抽象工厂模式
- 建造者模式
为什么会有创建型模式,它有什么用呢?
- 创建型模式主要关注怎样创建出对象,将对象的创建和使用分离。
- 它能降低系统的耦合度,使用者无需关注对象的创建细节。
1、单例模式
- 定义:一个单一的类,负责创建自己的对象,同时确保系统中只有单个对象被创建。
- 特点 —— 做法:
- 这个类只能有一个实例 —— 构造器私有
- 必须自行创建这个实例 —— 自己编写实例化逻辑
- 想整个系统提供这个实例 —— 对外提供实例方法
关于单例模式的实现,本专栏专门有一篇文章讲解单例模式常见的七种写法,这里就不再附代码了,点击查看
- 使用场景
- 1、多线程中的线程池、数据库连接池等池化操作
- 2、系统环境信息,如Java中的System类获取当前系统属性、环境变量等
2、原型模式
- 定义:用于创建重复的对象,同时又能保证性能。
- 举个栗子:比如我们现在要写一个查询数据库的框架MeBatis,数据库中的数据基本不会改变,每次查询到之后,把查到的所有数据封装成一个对象返回。试想一下,如果现在有10000个线程在查询相同的一个记录,那么,就会创建出10000个这样的对象,造成极大的浪费。
- 解决例子的问题:对于上边的问题,我们可以想到,将第一次查询到的数据缓存到一个Map中,以后每次查询先去Map中看有没有,有的话直接返回,没有再查询数据库。
- 存在问题:这样的方案仔细想想还是有问题的,如果其中的一个线程获取对象之后,将这个对象修改了,那么,后边所有的线程都将拿到一个错误的数据,造成脏缓存问题。
- 怎么办呢:这时候就可以考虑原型模式,当线程拿到数据对象之后,我们不直接返回,而是返回一个克隆对象,那么无论这个线程如何修改,都不会影响缓存数据,后边的线程就会获取到正确的数据。
- 代码演示:10000个线程查询数据库中一个User数据(模拟,部分主要代码)
//User:实现Cloneable接口,重写clone()方法
public class User implements Cloneable {
private String username;
private Integer age;
/**
* 再创建一个人,赋予我的所有属性
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Object clone() throws CloneNotSupportedException {
User user = new User();
user.setUsername(username);
user.setAge(age);
return user;
}
}
public class Mebatis {
//缓存user
private Map<String,User> userCache = new HashMap<>();
//模拟从数据库查数据
public User getUser(String username) throws Exception {
User user = null;
//缓存中没有
if(!userCache.containsKey(username)){
//查询数据库
user = getUserFromDb(username);
}else {
//从缓存中直接拿,脏缓存问题
//原型已经拿到,但是不能直接给。
user = userCache.get(username);
System.out.println("从缓存中拿到的是:"+user);
//从这个对象快速得到一个克隆体(克隆人)==原型模式
user = (User) user.clone();
}
return user;
}
private User getUserFromDb(String username) throws Exception{
System.out.println("从数据库查到:"+username);
User user = new User();
user.setUsername(username);
user.setAge(18);
//给缓存中放一个clone
userCache.put(username, (User) user.clone());
return user;
}
}
- 总结:本体给外部提供一个克隆体使用
3、工厂模式
- 定义:提供一种创建对象的最佳方式,使我们不必关系创建细节,只需根据不同情况获得不同产品。
1.简单工厂
- 在只有几个产品的情况下,适合使用简单工厂模式,比如现在有一个汽车工厂,只生产两种车
- 代码示例:
/**
* 简单工厂
* 1、产品数量极少
*/
public class WuLinSimpleFactory {
public AbstractCar newCar(String type){
//核心方法:一切从简
if("van".equals(type)){
// 钣金、喷漆、放发动机、申请环保
return new VanCar();
}else if("mini".equals(type)){
return new MiniCar();
}
return null;//没有产品
}
}
/**
* 抽象的产品
*/
public abstract class AbstractCar {
String engine;
public abstract void run();
}
//具体产品
public class MiniCar extends AbstractCar{
public MiniCar(){
this.engine = "单引擎";
}
@Override
public void run() {
System.out.println(engine+"--> 发车...");
}
}
/**
* 具体产品
*/
public class VanCar extends AbstractCar{
public VanCar(){
this.engine = "双引擎";
}
@Override
public void run() {
System.out.println(engine+"-->发车");
}
}
- 缺点:扩产性差,违反开闭原则
2.工厂方法
- 改进:将工厂提升一个等级,创建一个抽象的工厂,然后利用多个具体的工厂,每个工厂生产一种商品,想要扩展产品时,只需要添加具体的工厂,提高程序的可扩展性。
- 主要代码:
/**
* 抽象工厂
*/
public abstract class AbstractCarFactory {
public abstract AbstractCar newCar();
//我能造什么.....
}
/**
* minicar分厂,具体的产品工厂,可以扩展添加
*/
public class WulinMinCarFactory extends AbstractCarFactory{
@Override
public AbstractCar newCar() {
return new MiniCar();
}
}
//具体的产品,有某一具体的工厂生产
public class MiniCar extends AbstractCar {
public MiniCar(){
this.engine = "迷你发动机";
}
@Override
public void run() {
System.out.println(engine+"--> 发车...");
}
}
- 缺点:品类单一,现在只能造车,我要是想造口罩怎么办
3.抽象工厂
- 改进:为了满足多个产品,继续向上抽象
- 主要代码
/**
* 总集团规范,最上层抽象工厂,定义可以造啥
*/
public abstract class WulinFactory {
abstract AbstractCar newCar();
abstract AbstractMask newMask();
}
/**
* wulin 汽车子集团,只负责造汽车,口罩直接返回null
*/
public abstract class WulinCarFactory extends WulinFactory{
@Override
abstract AbstractCar newCar();
@Override
AbstractMask newMask() {
return null;
}
}
/**
* wulin口罩子集团,只负责造口罩,汽车直接返回null
*/
public abstract class WulinMaskFactory extends WulinFactory{
@Override
AbstractCar newCar() {
return null;
}
abstract AbstractMask newMask();
}
/**
* 抽象产品
*/
public abstract class AbstractCar {
String engine;
public abstract void run();
}
/**
* 抽象产品
*/
public abstract class AbstractMask {
Integer price;
public abstract void protectedMe();
}
//具体产品省略,代码太多了......
/**
* 口罩子集团建分厂:只造口罩,且武汉的厂只造N95口罩
*/
public class WulinWuHanMaskFactory extends WulinMaskFactory{
@Override
AbstractMask newMask() {
return new N95Mask();
}
}
/**
* 口罩子集团建分厂:只造口罩,且杭州的厂造普通外科口罩
*/
public class WulinHangZhouMaskFactory extends WulinMaskFactory {
@Override
AbstractMask newMask() {
return new CommonMask();
}
}
//汽车子集团建汽车各地分厂类似......
4、建造者模式
- 定义:创建的东西细节复杂,还必须暴露给使用者。屏蔽过程而不屏蔽细节。
- 举例(比如我们现在要定制一台手机)
//我们想要定制的手机
@Getter
@ToString
public class Phone {
protected String cpu;
protected String mem;
protected String disk;
protected String cam;
}
/**
* 抽象建造者,定义我们定制化的参数,返回最终的手机
*/
public abstract class AbstractBuilder {
Phone phone;
abstract AbstractBuilder customCpu(String cpu);
abstract AbstractBuilder customMem(String mem);
abstract AbstractBuilder customDisk(String disk);
abstract AbstractBuilder customCam(String cam);
Phone getProduct(){
return phone;
}
}
//赋值后返回自己,方便链式调用
public class XiaomiBuilder extends AbstractBuilder{
public XiaomiBuilder(){
phone = new Phone();
}
@Override
AbstractBuilder customCpu(String cpu) {
phone.cpu = cpu;
return this;
}
@Override
AbstractBuilder customMem(String mem) {
phone.mem = mem;
return this;
}
@Override
AbstractBuilder customDisk(String disk) {
phone.disk = disk;
return this;
}
@Override
AbstractBuilder customCam(String cam) {
phone.cam = cam;
return this;
}
}
到这里,创建型的几种设计模式就学习完了,下一篇结构型设计模式…