设计模式学习10----建造者模式

简介: 这些天在阅读MyBatis的源码,发现MyBatis源码中运用了很多设计模式,例如:模板模式,建造者模式,装饰器模式。其中最常用的就是建造者模式。下面我们就来学习下建造者模式。

前言

这些天在阅读MyBatis的源码,发现MyBatis源码中运用了很多设计模式,例如:模板模式,建造者模式,装饰器模式。其中最常用的就是建造者模式。下面我们就来学习下建造者模式。

建造者模式

建造者模式结构图及角色

建造者模式中的主要角色如下:

建造者(Builder)接口:Builder接口用于定义构建产品对象的各部分的行为。

具体建造者(ConcreteBuilder)角色:直接创建产品对象的具体建造者。具体建造者类必须实现建造者接口所要求的两类方法:一类是建造方法,如上图中的 buildPart1()等方法。另外一类是获取构造好的产品对象的方法,如上图中的getProduct()方法。

指挥者(Director)角色: 该角色会通过调用具体建造者,创建需要的产品对象

产品(Product)角色:一个具体的产品对象

建造者模式的优缺点

优点

将复杂产品的创建步骤分解在不同的方法中。使得创建过程更加清晰,使得我们能更加精确的控制复杂对象的产生过程。

将产品的创建过程与产品本身分离开来,可以使用相同的创建过程来得到不同的产品。也就是说细节依赖抽象

每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或者增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。

缺点

建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间差异很大,则不适合使用建造者模式。

如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

MyBatis中应用建造者模式

在MyBatis中应用到建造者模式的地方有很多,我这里举一个用的最多的点。在解析映射文件中的cache时,创建缓存构造器运用到了建造者模式。代码如下:

public class CacheBuilder {
  private String id;
  private Class<? extends Cache> implementation;
  private List<Class<? extends Cache>> decorators;
  private Integer size;
  private Long clearInterval;
  private boolean readWrite;
  private Properties properties;
  private boolean blocking;
  public CacheBuilder(String id) {
    this.id = id;
    this.decorators = new ArrayList<Class<? extends Cache>>();
  }
  public CacheBuilder implementation(Class<? extends Cache> implementation) {
    this.implementation = implementation;
    return this;
  }
  public CacheBuilder addDecorator(Class<? extends Cache> decorator) {
    if (decorator != null) {
      this.decorators.add(decorator);
    }
    return this;
  }
  public CacheBuilder size(Integer size) {
    this.size = size;
    return this;
  }
  public CacheBuilder clearInterval(Long clearInterval) {
    this.clearInterval = clearInterval;
    return this;
  }
  public CacheBuilder readWrite(boolean readWrite) {
    this.readWrite = readWrite;
    return this;
  }
  public CacheBuilder blocking(boolean blocking) {
    this.blocking = blocking;
    return this;
  }
  public CacheBuilder properties(Properties properties) {
    this.properties = properties;
    return this;
  }
  /**
   * @return
   */
  public Cache build() {
//   1. 设置默认的缓存类型(PerpetualCache)和缓存装饰器(LruCache)
    setDefaultImplementations();
    //通过反射创建缓存
    Cache cache = newBaseCacheInstance(implementation, id);
    //设额外属性,初始化Cache对象
    setCacheProperties(cache);
    // issue #352, do not apply decorators to custom caches
//  2.  仅对内置缓存PerpetualCache应用装饰器
    if (PerpetualCache.class.equals(cache.getClass())) {
      for (Class<? extends Cache> decorator : decorators) {
          //装饰者模式一个个包装cache
        cache = newCacheDecoratorInstance(decorator, cache);
        //又要来一遍设额外属性
        setCacheProperties(cache);
      }
      //3. 应用标准的装饰者,比如LoggingCache,SynchronizedCache
      cache = setStandardDecorators(cache);
    } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
        //4.如果是custom缓存,且不是日志,要加日志
      cache = new LoggingCache(cache);
    }
    return cache;
  }
  private void setDefaultImplementations() {
      //又是一重保险,如果为null则设默认值,
    // 虽然和XMLMapperBuilder.cacheElement
    // 以及MapperBuilderAssistant.useNewCache逻辑重复了,但是还是有必要
//如果用户忘记设置implementation或者人为的将implementation设为空,会导致
//build方法在构建实例时触发空指针异常。
    if (implementation == null) {
//      设置默认的缓存实现类
      implementation = PerpetualCache.class;
      if (decorators.isEmpty()) {
//        添加LruCache装饰器
        decorators.add(LruCache.class);
      }
    }
  }
  //最后附加上标准的装饰者
  private Cache setStandardDecorators(Cache cache) {
    try {
//      创建"元信息"对象
      MetaObject metaCache = SystemMetaObject.forObject(cache);
      if (size != null && metaCache.hasSetter("size")) {
        metaCache.setValue("size", size);
      }
      if (clearInterval != null) {
        //刷新缓存间隔,怎么刷新呢,用ScheduledCache来刷,还是装饰者模式,漂亮!
        cache = new ScheduledCache(cache);
        ((ScheduledCache) cache).setClearInterval(clearInterval);
      }
      if (readWrite) {
          //如果readOnly=false,可读写的缓存 会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false。
        cache = new SerializedCache(cache);
      }
      //日志缓存
      cache = new LoggingCache(cache);
      //同步缓存, 3.2.6以后这个类已经没用了,考虑到Hazelcast, EhCache已经有锁机制了,所以这个锁就画蛇添足了。
      cache = new SynchronizedCache(cache);
      if (blocking) {
        cache = new BlockingCache(cache);
      }
      return cache;
    } catch (Exception e) {
      throw new CacheException("Error building standard cache decorators.  Cause: " + e, e);
    }
  }
  //省略部分代码
  }

我们再来看看调用者。

// * MapperBuilderAssistant
    //调用CacheBuilder构建cache,id=currentNamespace(使用建造者模式构建缓存实例)
    Cache cache = new CacheBuilder(currentNamespace)
        .implementation(typeClass)
        .addDecorator(evictionClass)
        .clearInterval(flushInterval)
        .size(size)
        .readWrite(readWrite)
        .blocking(blocking)
        .properties(props)
        .build();

如上,是一个对建造者模式的最佳应用。其中

1.CacheBuilder 类充当了具体建造者的角色

2.Cache 类充当了具体产品的角色

3.MapperBuilderAssistant 充当了指挥者的角色。

跟标准建造者模式不同的是,Cache 对象是由反射生成的。不是直接实例化得到的。

下面,我们画个类图更好的理解下。

df95f40d214b7ea3012a43fb3981c4b5_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ1MzQ4MDg=,size_16,color_FFFFFF,t_70.png


总结

本文,首先对建造者模式的基本结构和角色进行了阐述,是读者对建造者有个大致的理解。然后,简要的介绍了建造者模式的优缺点。最后,通过一个例子简单介绍了建造者模式在MyBatis中的实际运用。希望本文对读者朋友们有所帮助。


相关文章
|
21天前
|
设计模式 存储 算法
设计模式学习心得之五种创建者模式(2)
设计模式学习心得之五种创建者模式(2)
17 2
|
21天前
|
设计模式 uml
设计模式学习心得之前置知识 UML图看法与六大原则(下)
设计模式学习心得之前置知识 UML图看法与六大原则(下)
13 2
|
21天前
|
设计模式 算法
建造者模式-大话设计模式
建造者模式-大话设计模式
11 1
|
15天前
|
设计模式 JavaScript
js设计模式【详解】—— 建造者模式
js设计模式【详解】—— 建造者模式
15 0
|
19天前
|
设计模式
设计模式-05建造者模式(Builder Pattern)
设计模式-05建造者模式(Builder Pattern)
|
20天前
|
设计模式 Java
Java设计模式:建造者模式之经典与流式的三种实现(四)
Java设计模式:建造者模式之经典与流式的三种实现(四)
|
21天前
|
设计模式 Java
Java设计模式之建造者模式详解
Java设计模式之建造者模式详解
|
21天前
|
设计模式 Java
设计模式之建造者模式
设计模式之建造者模式
|
21天前
|
设计模式 安全 Java
设计模式学习心得之五种创建者模式(1)
设计模式学习心得之五种创建者模式(1)
10 0
|
21天前
|
设计模式 数据可视化 程序员
设计模式学习心得之前置知识 UML图看法与六大原则(上)
设计模式学习心得之前置知识 UML图看法与六大原则(上)
12 0