一些设计模式及其案例原创
金蝶云社区-艾贺521
艾贺521
7人赞赏了该文章 1127次浏览 未经作者许可,禁止转载编辑于2019年04月29日 17:54:53
封面


常用的设计模式大家都有所了解,但有一些可能大家不是很留意的设计模式,这里我整理下


  • 原型模式

  • 桥接模式

  • 组合模式

  • 责任链模式

  • 命令模式

  • 解释器模式

  • 中介模式

  • 备忘录模式

  • 访问者模式


案例

原型模式

原型模式:指原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。

可以理解为有一个已经存在的模子,其它的对象都是通过这个模子创建出来的。比如说“海贼王”里面的暴君熊是PX-0,剩余的和平主义者,PX-x都是以第一代的熊为原型创建出来的。


在Spring中的scope可以指定为prototype,这样每次在获取对象的时候,都会根据beanDefinition创建一个新的对象出来。每次创建的对象都是相同的。beanDefinition就是原型。另外像对象继承了Cloneable接口实现了clone方法,clone出来的对象,不过要注意深克隆,浅克隆。

/**
* Scope identifier for the standard prototype scope: "prototype".
* Custom scopes can be added via {@code registerScope}.
* @see #registerScope
*/
String SCOPE_PROTOTYPE = "prototype";


桥接模式

和适配器模式有点像,但是有一些区别。

适配器模式:在业务不断的发展过程中,发现有两个接口无法对接,这个时候需要写一个适配器来适配两个接口。在现实生活中,适配器模式就是转换头,肯定要现有接口,充电头才能去买转换器是什么。

桥接模式:通过某种方式使两个类之间建立联系,继承也可以建立联系,但是桥接模式建议使用组合而不是继承。在桥接的时候,大致知道要桥接的对象,但是桥接对象可能会发生变化。


区别:

  • 适配器模式使用的时候,要适配的对象已经确定,基本不变。桥接模式要桥接的对象后期可以进行扩展

  • 适配器模式一般发生在业务发展的后期,桥接模式发生在业务开始设计的时候

  • 桥接模式是为了分离抽象与实现


源码中用到桥接模式的地方:

  • JDK中的数据库Driver,相同的Driver接口,实现不同。DriverManager与Driver之间实现了桥接


组合模式

将对象组合成树形结构,以表示“部分-整体”的层次结构,组合模式使客户端对单个对象和组合对象可以用一致的方法处理。


常见的地方,比如说安卓开发中的View,一个View内部可以有很多小的View,View可以是列表视图,也可以是原型的,文字的等等。

在JDK内部的添加集合,集合可能是一个元素,也可能是多个元素。

//ArrayList
public boolean addAll(Collection<? extends E> c) {
      Object[] a = c.toArray();
      int numNew = a.length;
      ensureCapacityInternal(size + numNew); // Increments modCount
      System.arraycopy(a, 0, elementData, size, numNew);
      size += numNew;
      return numNew != 0;
  }



责任链模式

定义:为请求创建一个接收此次请求的链,一个请求的处理需要一个或者多个对象中的几个协作处理。


优点:请求的发送者和接受者可以进行解耦,一般会在外部的配置文件中,可以灵活指定

缺点:责任链过多会影响性能


在JDK中处理Servlet的时候可以指定Filter,如果当前的Filter处理完毕,则使用filterChain.doFilter,继续下一步的Filter处理。


<filter>
      <filter-name>utfEncodingFilter</filter-name>
      <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
      <init-param>
          <param-name>encoding</param-name>
          <param-value>utf-8</param-value>
      </init-param>
  </filter>
  <filter-mapping>
      <filter-name>utfEncodingFilter</filter-name>
      <url-pattern>*.do</url-pattern>
  </filter-mapping>
   

  // 如果想让过滤链继续走,代码中
filterChain.doFilter(request, response);



命令模式

定义:将请求封装成对象,以便使用不同的请求。命令模式解决了应用程序中对象的职责以及它们之间的通信方式。

下命令的人只需要下命令就行了,而不需要具体是如何解决的。


在平时我们的代码中,如继承Runnable接口,需要实现其中的run方法,而去执行Runnable对象的人不需要知道其内部到底做了什么。


// 当然在我们的代码中,会有很多实现Runnable的类
public class CommandDemo {

  public static void main(String[] args) {
      ExecutorService threadPool = Executors.newFixedThreadPool(4);
      threadPool.execute(new DoCommand());
      threadPool.shutdown();
  }
   
  static class DoCommand implements Runnable{

      @Override
      public void run() {
          System.out.println("做某些事情");
      }
  }
}


解释器模式

定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器用来解释语言中的句子。

就像我们平时用的解释型语言,如Python,JS,bash,这种语言一般是一句一句的执行。像使用Spring的时候,spring的核心类库有个spel,就是spring的语法表达式:SPEL还有很多更复杂的表达语法,这就是其自己实现的解释器

publicclassInterpreterDemo{
   publicstaticvoidmain(String[] args) {
       SpelExpressionParserexpressionParser=newSpelExpressionParser();
       Expressionexpression=expressionParser.parseExpression("1+1");
       Objectvalue=expression.getValue();
       System.out.println(value);
  }
}


优点:语法有很多类表示,容易改变及扩展该语言

缺点:当语法规则太多的时候,增加了系统的复杂度


中介模式

定义:定义了一个封装一组,对象如何交互的对象。

适用场景:在系统中对象存在复杂的引用关系,产生相互依赖关系,结构混乱且难以理解,通过增加新的中介者类来达到扩展的目的。


像我们平时进行聊天的聊天室,系统中使用的中间件,都可以进行解耦的作用。但是中介者过多,会增加系统的复杂度,这一点在使用的时候要有所注意。


像平时我们使用的线程池,指定任务队列: 在执行任务的时候,发现核心线程都在工作,则把任务添加进入任务队列之中,后续再从队列之中取数据,任务队列可以看做 线程池 与 任务交互的中介。

new ThreadPoolExecutor(nThreads, nThreads,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>())



备忘录模式

定义:保存一个对象的状态,以便在适当的时候恢复对象。

这个主意就是保存与恢复,类似数据的备份与还原,也有时候在玩游戏的时候保存某个档案,后期再重新玩。能恢复的档案,必须要首先保存过。

备忘录模式与状态模式有点像,只不过状态模式用类表示状态,并且状态是用来控制程序的执行逻辑的。


缺点:会占用一些资源


代码中的应用:

  • 在Hbase中数据都会根据时间戳保存多个版本的数据

  • 程序代码中的回滚操作,如果某个执行发现出现了不可预期的东西,会退到原本的状态


访问者模式

定义:封装作用于某种数据结构(List,Set,Map)等中各元素的操作,可以再不改变这些元素的前提下,定义作用于这些元素的操作。


当有的时候,我们需要将数据与对数据的操作进行分离。增加一个操作,只需要增加新的访问者,但是改变具体的数据结构会比较麻烦一些。


在Spring中:可以遍历类的各种属性,这个类主要是分析Bean的各种数据。

// BeanDefinitionVisitor.java
public void visitBeanDefinition(BeanDefinition beanDefinition) {
visitParentName(beanDefinition);
visitBeanClassName(beanDefinition);
visitFactoryBeanName(beanDefinition);
visitFactoryMethodName(beanDefinition);
visitScope(beanDefinition);
visitPropertyValues(beanDefinition.getPropertyValues());
ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
visitIndexedArgumentValues(cas.getIndexedArgumentValues());
visitGenericArgumentValues(cas.getGenericArgumentValues());
}


访问者模式一般在实际中用的会少一些。


最后

设计模式相对比较抽象,想要完全的理解它不是一朝一夕的事情,这里主要提供了一种思路,在平时写代码的时候,对设计模式做一些尽可能的关注,代码写的多了,见得多了,也就明白各种设计模式了,后期在自己写的时候,可能就信手拈来了。

大家可以参考:https://github.com/iluwatar/java-design-patterns

希望能帮到大家


注:

本文独家发布自金蝶云社区



赞 7