常用的设计模式大家都有所了解,但有一些可能大家不是很留意的设计模式,这里我整理下
原型模式
桥接模式
组合模式
责任链模式
命令模式
解释器模式
中介模式
备忘录模式
访问者模式
案例
原型模式
原型模式:指原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
可以理解为有一个已经存在的模子,其它的对象都是通过这个模子创建出来的。比如说“海贼王”里面的暴君熊是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
希望能帮到大家
注:
本文独家发布自金蝶云社区
推荐阅读