专栏首页后端技术学习设计模式之装饰者模式

设计模式之装饰者模式

在前面我们看到了单例模式的使用,在dubbo中,同样有单例模式的使用,找到dubbo中的ExtensionLoader类可以看到这样的代码:

/**
 * Find the extension with the given name. If the specified name is not found, then {@link IllegalStateException}
 * will be thrown.
 */
@SuppressWarnings("unchecked")
public T getExtension(String name) {
    return getExtension(name, true);
}

获取扩展:

public T getExtension(String name, boolean wrap) {
    if (StringUtils.isEmpty(name)) {
        throw new IllegalArgumentException("Extension name == null");
    }
    if ("true".equals(name)) {
        return getDefaultExtension();
    }
    final Holder<Object> holder = getOrCreateHolder(name);
    Object instance = holder.get();
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                instance = createExtension(name, wrap);
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}

同时在createExtension中看到装饰者模式的使用,是否需要进行装饰wrap,如果需要,则对其进行装饰:

private T createExtension(String name, boolean wrap) {
    Class<?> clazz = getExtensionClasses().get(name);
    if (clazz == null) {
        throw findException(name);
    }
    try {
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        injectExtension(instance);


        if (wrap) {

            List<Class<?>> wrapperClassesList = new ArrayList<>();
            if (cachedWrapperClasses != null) {
                wrapperClassesList.addAll(cachedWrapperClasses);
                wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                Collections.reverse(wrapperClassesList);
            }

            if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                for (Class<?> wrapperClass : wrapperClassesList) {
                    Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                    if (wrapper == null
                            || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
                        instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                    }
                }
            }
        }

        initExtension(instance);
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                type + ") couldn't be instantiated: " + t.getMessage(), t);
    }
}

上面是使用wrapper的场景进行装饰,也可以说成是包装,本质就是装饰者模式的体现。除了wrapper,在spring中也有装饰者模式的体现。

在前面的注册beanDefintion的时候,进行默认标签解析时,会经过四步:

/**
 * Process the given bean element, parsing the bean definition
 * and registering it with the registry.
 */
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   //解析beanDefintion元素 
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
   if (bdHolder != null) {
      //进行装饰
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
         // Register the final decorated instance.
          //注册最终被装饰的实例
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
      }
      catch (BeanDefinitionStoreException ex) {
         getReaderContext().error("Failed to register bean definition with name '" +
               bdHolder.getBeanName() + "'", ele, ex);
      }
      // Send registration event.
      //发送注册事件 
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
   }
}

除了这里使用装饰者模式之外,对于wrapper的对象也是采用了装饰者模式。如果我们需要实现一个装饰者模式,那么怎样实现呢?

/**
 * Decorate the given bean definition through a namespace handler, if applicable.
  如果使用,通过名称空间处理程序装饰给定的beanDefintion
 * @param ele the current element  当前元素ele
 * @param originalDef the current bean definition 当前的beanDefintion =>originalDef
 * @param containingBd the containing bean definition (if any)
 * @return the decorated bean definition 返回被装饰好的beanDefintion
 */
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
      Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {

   BeanDefinitionHolder finalDefinition = originalDef;

   // Decorate based on custom attributes first.
   NamedNodeMap attributes = ele.getAttributes();
   for (int i = 0; i < attributes.getLength(); i++) {
      Node node = attributes.item(i);
      finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
   }

   // Decorate based on custom nested elements.
   NodeList children = ele.getChildNodes();
   for (int i = 0; i < children.getLength(); i++) {
      Node node = children.item(i);
      if (node.getNodeType() == Node.ELEMENT_NODE) {
         finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
      }
   }
   return finalDefinition;
}

进行装饰如果需要:

/**
 * Decorate the given bean definition through a namespace handler,
 * if applicable.
 * @param node the current child node
 * @param originalDef the current bean definition
 * @param containingBd the containing bean definition (if any)
 * @return the decorated bean definition
 */
public BeanDefinitionHolder decorateIfRequired(
      Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {

   String namespaceUri = getNamespaceURI(node);
   if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
      NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
      if (handler != null) {
         BeanDefinitionHolder decorated =
               handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
         if (decorated != null) {
            return decorated;
         }
      }
      else if (namespaceUri.startsWith("http://www.springframework.org/")) {
         error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
      }
      else {
         // A custom namespace, not to be handled by Spring - maybe "xml:...".
         if (logger.isDebugEnabled()) {
            logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
         }
      }
   }
   return originalDef;
}

如果debug的时候,可以看到程序中在默认标签中,如果存在自定义的标签时,此时就需要对自定义标签进行解析,此时会寻找命名空间getNamespaceURI,使用命名空间处理器进行标签的解析:this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri)。这个就是自定义标签解析的套路。

要实现装饰者模式,需要完成:

1.继承处理接口
2.提供构造函数或者set方法
3.覆盖原有接口方法

装饰者的使用场景:

如果不想要改变原有接口的实现,而又想增加新的功能或者特性时,此时就可以考虑使用装饰者模式对原有的功能进行只增强

下面我们来看一个装饰者模式的例子:

原始的例子:

在小商店还很小的时候,只提供做煎饼的业务,这个时候的样式比较单一,这个时候师傅只做煎饼,随着客户的反馈,之后在煎饼的基础之上又加上了鸡蛋,但随后有加上了香肠,形成了煎饼+鸡蛋+香肠的方式,代码来源于github…

原始接口:

public class BatterCake {

    //商品信息
    public String getMsg() {
        return "煎饼";
    }

    //获取价格
    public int getPrice() {
        return 5;
    }
}

原始的实现方式:

煎饼+鸡蛋

public class BatterCakeWithEgg extends BatterCake {
    @Override
    public String getMsg() {
        return super.getMsg() +" + 1个鸡蛋";
    }

    @Override
    public int getPrice() {
        return super.getPrice()+1;
    }
}

煎饼+鸡蛋+香肠:

//在原有煎饼+鸡蛋的基础之上,加1个香肠
public class BatterCakeWithEggAndHotdog extends BatterCakeWithEgg {
    @Override
    public String getMsg() {
        return super.getMsg() +" + 1个香肠";
    }

    @Override
    public int getPrice() {
        return super.getPrice() +2;
    }
}

进行商品、价格获取:

public class BatterCakeTest {
    public static void main(String[] args) {
        //创建一个对象煎饼
        BatterCake batterCake = new BatterCake();
        System.out.println(batterCake.getMsg() +" 的价格是:"+batterCake.getPrice());

        //创建一个煎饼+鸡蛋对象
        batterCake = new BatterCakeWithEgg();
        System.out.println(batterCake.getMsg() +" 的价格是:"+batterCake.getPrice());

        //创建一个煎饼+鸡蛋+香肠
        batterCake = new BatterCakeWithEggAndHotdog();
        System.out.println(batterCake.getMsg() +" 的价格是:"+batterCake.getPrice());
    }
}

结果:

使用装饰者模式进行重构:

原始接口:

public abstract class BatterCake {

    //商品信息
    protected abstract String getMsg();
    //价格
    protected abstract int getPrice();
}

原始接口实现:

public class BaseBatterCake extends BatterCake {
    protected String getMsg() {
        return "煎饼";
    }

    protected int getPrice() {
        return 5;
    }
}

待装饰的类:

// 装饰类继承原始接口
public class BatterCakeDecorator extends BatterCake {
   //原始接口对象
    private BatterCake batterCake;

    //提供构造函数
    public BatterCakeDecorator(BatterCake batterCake) {
        this.batterCake = batterCake;
    }

    //重写原始接口的方法
    @Override
    protected String getMsg() {
        return this.batterCake.getMsg();
    }

    @Override
    protected int getPrice() {
        return this.batterCake.getPrice();
    }
}

进行装饰:

//进行装饰:在原有的基础上进行装饰
public class BatterCakeEggDecorator extends BatterCakeDecorator {

    //原始的数据构造函数
    public BatterCakeEggDecorator(BatterCake batterCake) {
        super(batterCake);
    }

    //重写原始方法
    @Override
    protected String getMsg() {
        return super.getMsg() +" 1 个鸡蛋";
    }

    //重写原始方法
    @Override
    protected int getPrice() {
        return super.getPrice() +1;
    }
}

进行装饰:

//进行进一步装饰
public class BatterCakeWihHotdogDecorator extends BatterCakeDecorator {
    //需要装饰的对象
    public BatterCakeWihHotdogDecorator(BatterCake batterCake) {
        super(batterCake);
    }

    //重写被装饰方法
    @Override
    protected String getMsg() {
        return super.getMsg() +" 1 个香肠";
    }

    @Override
    protected int getPrice() {
        return super.getPrice() + 2;
    }
}

进行测试:

public class BatterCakeDecoratorTest {
    public static void main(String[] args) {
       //原始被修饰对象
        BaseBatterCake baseBatterCake = new BaseBatterCake();
        //装饰对象
        BatterCakeDecorator batterCakeDecorator = new BatterCakeDecorator(baseBatterCake);
        //创建装饰后对象
        batterCakeDecorator = new BatterCakeEggDecorator(batterCakeDecorator);
        //创建最终装饰的对象
        batterCakeDecorator = new BatterCakeWihHotdogDecorator(batterCakeDecorator);

        System.out.println(batterCakeDecorator.getMsg() +" 的总价是: "+batterCakeDecorator.getPrice());
    }
}

最终被增强的对象的信息:

本文分享自微信公众号 - 后端技术学习(gh_9f5627e6cc61),作者:路行的亚洲

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-11-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • LinkedBlockingQueue源码学习

    采用线程池和阻塞队列实现生产/消费者模型。其中LinkedBlockingQueue是阻塞队列,同时线程安全,其特点:

    路行的亚洲
  • Mybatis中sql拦截增强-AOP+interceptor实现分页和排序

    mybatis的执行的大概过程:首先需要有sqlSessionFactroy,然后通过sqlSessionFactory拿到sqlSession,然后通过sql...

    路行的亚洲
  • CompletableFuture学习

    前面我们已经知道CompletionService是可以解决Future带来的阻塞问题的,同时我们除了前面我们看到的take方法之外,还可以使用poll方法,这...

    路行的亚洲
  • Struts2(接受表单参数)请求数据自动封装和数据类型转换

    Struts2请求数据自动封装:   (1)实现原理:参数拦截器   (2)方式1:jsp表单数据填充到action中的属性;        普通的成员变量,...

    别先生
  • 聊聊nacos的RaftPeerSet

    nacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent...

    codecraft
  • 聊聊nacos的RaftPeerSet

    nacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent...

    codecraft
  • Unity 离线建造系统

    很多游戏,特别是养成类手游,都会有自己独特的建造系统,一个建造装置的状态循环或者说生命周期一般是这样的:

    汐夜koshio
  • Android开发中总结的Adapter工具类【附完整源码下载】

    本文实例讲述了Android开发中总结的Adapter工具类。分享给大家供大家参考,具体如下:

    砸漏
  • 关于Elasticsearch里面聚合group的坑

    我是攻城师
  • 生动化你的表达——DuerOS中的SSML应用

    在对话式AI系统中,语音交互是主要的输入输出方式。对语音输出而言,有两种主要的方法,一种是事先制作好音频,然后根据用户的请求,播放音频;另一种是通过语音合成中的...

    半吊子全栈工匠

扫码关注云+社区

领取腾讯云代金券