设计模式 - 装饰器模式(Decorator Pattern)

问题

在实际生产中,新需求在软件的整个生命过程中是不断出现的。当有新需求出现时,就需要为某个组件添加新的功能来满足这些需求。添加新功能的方式有很多。

1、我们可以直接修改已有的组件的代码添加相应的新功能,这显然破坏了已有组件的稳定性。修改完之后,整个组件需要重新进行测试,才能上线使用。违反了“开放-封闭”原则。

2、使用继承方式,子类实现新功能实现扩展。这种方式是静态的,用户不能控制增加行为的方式和时机。而且有些情况下继承是不可行的,例如final修饰的类。另外,若新功能存在多种组合,使用继承方式,会导致大量子类产生。

装饰器能帮助解决上述问题。


装饰器模式(Decorator Pattern)

概念

装饰器模式 允许向一个现有的对象添加新的功能,同时又不改变其结构。装饰者可以在所委托被装饰者的行为之前或之后加上自己的行为,以达到特定的目的。

装饰器可以动态地为对象添加功能,他是基于组合的方式实现功能的。在实践中,我们应该尽量使用组合的方式来扩展系统功能,而非使用继承方式。

组成


装饰器模式由组件和装饰者组成。

抽象组件(Component):需要装饰的抽象对象。 
具体组件(ConcreteComponent):是我们需要装饰的对象 
抽象装饰类(Decorator):内含指向抽象组件的引用及装饰者共有的方法。 
具体装饰类(ConcreteDecorator):被装饰的对象。

适用场景:

  • 扩展一个类的功能。
  • 动态增加功能,动态撤销。

优缺点:

优点:

  • 装饰类和被装饰类可以独立发展,不会相互耦合
  • 动态的将责任附加到对象身上。

缺点:

  • 需求越来越多时,可能会创建多层装饰,增加了系统的复杂性。

代码实战:

参看mybatis-cache源码包

package org.apache.ibatis.cache;

import java.util.concurrent.locks.ReadWriteLock;

public interface Cache {

  String getId();

  int getSize();

  void putObject(Object key, Object value);

  Object getObject(Object key);

  Object removeObject(Object key);

  void clear();

  ReadWriteLock getReadWriteLock();

}
抽象组件(Component):需要装饰的抽象对象。


其下有多个实现,多数为其装饰器。而具体的实现是PerpetualCache,类似具体组件(ConcreteComponent):是我们需要装饰的对象。

装饰器只列举其中一个实现类做说明,例如FifoCache,存储了被装饰的目标对象。

public class FifoCache implements Cache {

  private final Cache delegate;   //被装饰的cache对象
  private final LinkedList<Object> keyList;    //用于记录key进入缓存的先后顺序
  private int size;    //缓存上限

  public FifoCache(Cache delegate) {
    this.delegate = delegate;
    this.keyList = new LinkedList<Object>();
    this.size = 1024;
  }

  public String getId() {
    return delegate.getId();
  }

  public int getSize() {
    return delegate.getSize();
  }

  public void setSize(int size) {
    this.size = size;
  }

  public void putObject(Object key, Object value) {
    cycleKeyList(key);
    delegate.putObject(key, value);
  }

  public Object getObject(Object key) {
    return delegate.getObject(key);
  }

  public Object removeObject(Object key) {
    return delegate.removeObject(key);
  }

  public void clear() {
    delegate.clear();
    keyList.clear();
  }

  public ReadWriteLock getReadWriteLock() {
    return delegate.getReadWriteLock();
  }

  private void cycleKeyList(Object key) {
    keyList.addLast(key);
    if (keyList.size() > size) {
      Object oldestKey = keyList.removeFirst();
      delegate.removeObject(oldestKey);
    }
  }

}

其他实现装饰类自行了解。LRU、软引用、弱引用等常用cache方式。可结合guava缓存原理、实现独立学习一下。


参考:

Head First 设计模式

mybatis源码

猜你喜欢

转载自blog.csdn.net/u013956074/article/details/80707511