记录自己理解的一些设计模式

记录一下自己理解的一些 设计模式 ,并尽量使用表达清楚的例子进行讲解。

策略模式

策略模式应该是最基础的一个设计模式,它是对行为的一个抽象。jdk中的Comparator比较器就是一个使用策略设计模式的策略。

比如有一个Student学生类,有name和age两个属性。如果有个需求需要打印学生名单,并按照字母顺序排序,可以使用Comparator接口并在内部使用name进行比较即可。
如果哪一天需要按照年龄进行排序,那么只需要修改Comparator即可,也就是使用一个新的策略,其它完全不变。

工厂模式

工厂模式的意义在于对象的创建、管理可以使用工厂去管理,而不是创建者自身。最典型的工厂模式使用者就是Spring,Spring内部的容器就是一个工厂,所有的bean都由这个容器管理,包括它们的创建、销毁、注入都被这个容器管理。

工厂模式分简单工厂和抽象工厂。它们的区别在于抽象工厂抽象程度更高,把工厂也抽象成了一个接口,这样可以再每添加一个新的对象的时候而不需要修改工厂的代码。

比如有个Repository接口,用于存储数据,有DatabaseRepository,CacheRepository,FileRepository分别在数据库,缓存,文件中存储数据,定义如下:


  1. public interface Repository {  
  2. void save(Object obj);  
  3. }  
  4. class DatabaseRepository implements Repository {  
  5. @Override  
  6. public void save(Object obj) {  
  7. System.out.println("save in database");  
  8. }  
  9. }  
  10. class CacheRepository implements Repository {  
  11. @Override  
  12. public void save(Object obj) {  
  13. System.out.println("save in cache");  
  14. }  
  15. }  
  16. class FileRepository implements Repository {  
  17. @Override  
  18. public void save(Object obj) {  
  19. System.out.println("save in file");  
  20. }  

简单工厂的使用


  1. public class RepositoryFactory {  
  2. public Repository create(String type) {  
  3. Repository repository = null;  
  4. switch (type) {  
  5. case "db":  
  6. repository = new DatabaseRepository();  
  7. break;  
  8. case "cache":  
  9. repository = new CacheRepository();  
  10. break;  
  11. case "file":  
  12. repository = new FileRepository();  
  13. break;  
  14. }  
  15. return repository;  
  16. }  
  17. public static void main(String[] args) {  
  18. RepositoryFactory factory = new RepositoryFactory();  
  19. factory.create("db").save(new Object());  
  20. factory.create("cache").save(new Object());  
  21. factory.create("file").save(new Object());  
  22. }  

简单工厂的弊端在于每添加一个新的Repository,都必须修改RepositoryFactory中的代码

抽象工厂的使用


  1. public interface RepositoryFactoryProvider {  
  2. Repository create();
  3. }  
  4. class DatabaseRepositoryFactory implements RepositoryFactoryProvider {  
  5. @Override  
  6. public Repository create() {  
  7. return new DatabaseRepository();  
  8. }  
  9. }  
  10. class CacheRepositoryFactory implements RepositoryFactoryProvider {  
  11. @Override  
  12. public Repository create() {  
  13. return new CacheRepository();  
  14. }  
  15. }  
  16. class FileRepositoryFactory implements RepositoryFactoryProvider {  
  17. @Override  
  18. public Repository create() {  
  19. return new FileRepository();  
  20. }  

抽象工厂的测试:


  1. RepositoryFactoryProvider dbProvider = new DatabaseRepositoryFactory(); 
  2. dbProvider.create().save(new Object()); 
  3. RepositoryFactoryProvider cacheProvider = new CacheRepositoryFactory(); 
  4. cacheProvider.create().save(new Object()); 
  5. RepositoryFactoryProvider fileProvider = new FileRepositoryFactory(); 
  6. fileProvider.create().save(new Object()); 

抽象工厂把工厂也进行了抽象话,所以添加一个新的Repository的话,只需要新增一个RepositoryFactory即可,原有代码不需要修改。

装饰者模式

装饰者模式的作用就在于它可以在不改变原有类的基础上动态地给类添加新的功能。之前写过一篇 通过源码分析MyBatis的缓存 文章,mybatis中的query就是使用了装饰者设计模式。

用一段简单的代码来模拟一下mybatis中query的实现原理:


  1. @Data  
  2. @AllArgsConstructor  
  3. @ToString  
  4. class Result { // 查询结果类,相当于一个domain  
  5. private Object obj; 
  6.  private String sql;  
  7. }  
  8. public interface Query { // 查询接口,有简单查询和缓存查询  
  9. Result query(String sql);  
  10. }  
  11. public class SimpleQuery implements Query { // 简单查询,相当于直接查询数据库,这里直接返回Result,相当于是数据库查询的结果  
  12. @Override  
  13. public Result query(String sql) {  
  14. return new Result(new Object(), sql);  
  15. }  
  16. }  
  17. public class CacheQuery implements Query { // 缓存查询,如果查询相同的sql,不直接查询数据库,而是返回map中存在的Result  
  18. private Query query;  
  19. private Map cache = new HashMap<>();  
  20. public CacheQuery(Query query) {  
  21. this.query = query;  
  22. }  
  23. @Override  
  24. public Result query(String sql) {  
  25. if(cache.containsKey(sql)) {  
  26. return cache.get(sql);  
  27. }  
  28. Result result = query.query(sql);  
  29. cache.put(sql, result);  
  30. return result;  
  31. }  

测试:


  1. Query simpleQuery = new SimpleQuery();  
  2. System.out.println(simpleQuery.query("select * from t_student") == simpleQuery.query("select * from t_student")); // false  
  3. Query cacheQuery = new CacheQuery(simpleQuery);  
  4. System.out.println(cacheQuery.query("select * from t_student") == cacheQuery.query("select * from t_student")); // true 

这里CacheQuery就是一个装饰类,SimpleQuery是一个被装饰者。我们通过装饰者设计模式动态地给SimpleQuery添加了缓存功能,而不需要修改SimpleQuery的代码。

当然,装饰者模式也有缺点,就是会存在太多的类。

如果我们需要添加一个过滤的查询(sql中有敏感字的就直接返回null,而不查询数据库),只需要可以添加一个FilterQuery装饰者即可:


  1. public class FilterQuery implements Query {  
  2. private Query query;  
  3. private List words = new ArrayList<>();  
  4. public FilterQuery(Query query) {  
  5. this.query = query;  
  6. words.add("fuck");  
  7. words.add("sex");  
  8. }  
  9. @Override  
  10. public Result query(String sql) {
  11. for(String word : words) {  
  12. if(sql.contains(word)) return null;  
  13. }  
  14. return query.query(sql);  
  15. }  
  16. }  
  17. Query filterQuery = new FilterQuery(simpleQuery);  
  18. System.out.println(filterQuery.query("select * from t_student where name = 'fuck'")); // null  
  19. System.out.println(filterQuery.query("select * from t_student where name = 'format'")); // Result(obj=java.lang.Object@1b4fb997, sql=select * from t_student where name = 'format') 

代理模式

代理模式的作用是使用一个代理类来代替原先类进行操作。比较常见的就是aop中就是使用代理模式完成事务的处理。

代理模式分静态代理和动态代理,静态代理的原理就是对目标对象进行封装,最后调用目标对象的方法即可。

动态代理跟静态代理的区别就是动态代理中的代理类是程序运行的时候生成的。Spring中对于接口的代理使用jdk内置的Proxy和InvocationHandler实现,对于类的代理使用cglib完成。

以1个UserService为例,使用jdk自带的代理模式完成计算方法调用时间的需求:


  1. // UserService接口  
  2. public interface IUserService {  
  3. void printAll();  
  4. }  
  5. // UserService实现类  
  6. class UserService implements IUserService {  
  7. @Override  
  8. public void printAll() {  
  9. System.out.println("print all users");  
  10. }  
  11. }  
  12. // InvocationHandler策略,这里打印了方法调用前后的时间  
  13. @AllArgsConstructor  
  14. class UserInvocationHandler implements InvocationHandler {  
  15. private IUserService userService;  
  16. @Override  
  17. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  18. System.out.println("start : " + System.currentTimeMillis());  
  19. Object result = method.invoke(userService, args);  
  20. System.out.println("end : " + System.currentTimeMillis());  
  21. return result;  
  22. }  

测试:


  1. IUserService userService = new UserService();  
  2. UserInvocationHandler uih = new UserInvocationHandler(userService);  
  3. IUserService proxy = (IUserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), new Class[] {IUserService.class}, uih); 
  4. proxy.printAll(); // 打印出start : 1489665566456 print all users end : 1489665566457 

组合模式

组合模式经常跟策略模式配合使用,用来组合所有的策略,并遍历这些策略找出满足条件的策略。之前写过一篇
SpringMVC关于json、xml自动转换的原理研究
文章,里面springmvc把返回的返回值映射给用户的response做了一层抽象,封装到了HandlerMethodReturnValueHandler策略接口中。

在HandlerMethodReturnValueHandlerComposite类中,使用存在的HandlerMethodReturnValueHandler对返回值进行处理,在HandlerMethodReturnValueHandlerComposite内部的代码如下:


  1. // 策略集合  
  2. private final List returnValueHandlers = new ArrayList();  
  3. @Override  
  4. public void handleReturnValue(Object returnValue, MethodParameter returnType,  
  5. ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { 
  6.  // 调用selectHandler方法  
  7. HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);  
  8. if (handler == null) {  
  9. throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());  
  10. }  
  11. handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); // 使用找到的handler进行处理  
  12. }  
  13. private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) {  
  14. boolean isAsyncValue = isAsyncReturnValue(value, returnType);  
  15. // 遍历存在的HandlerMethodReturnValueHandler  
  16. for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {  
  17. if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {  
  18. continue;  
  19. }  
  20. if (handler.supportsReturnType(returnType)) { // 找到匹配的handler  
  21. return handler;  
  22. }  
  23. }  
  24. return null;  

模板模式

跟策略模式类似,模板模式会先定义好实现的逻辑步骤,但是具体的实现方式由子类完成,跟策略模式的区别就是模板模式是有逻辑步骤的。比如要给院系里的学生排序,并取出排名第一的学生。这里就有2个步骤,分别是排序和取出第一名学生。

一段伪代码:


  1. public abstract class AbstractStudentGetter {  
  2. public final Student getStudent(List students) {  
  3. sort(students); // 第一步  
  4. if(!CollectionUtils.isEmpty(students)) {  
  5. return students.get(0); // 第二步  
  6. }  
  7. return null;  
  8. }  
  9. abstract public void sort(List students);  
  10. }  
  11. class AgeStudentGetter extends AbstractStudentGetter { // 取出年纪最大的学生  
  12. @Override  
  13. public void sort(List students) {  
  14. students.sort(new Comparator() { 
  15.  @Override  
  16. public int compare(Student s1, Student s2) {  
  17. return s2.getAge() - s1.getAge();  
  18. }  
  19. });  
  20. }  
  21. class NameStudentGetter extends AbstractStudentGetter { // 按照名字字母排序取出第一个学生  
  22. @Override  
  23. public void sort(List students) {  
  24. students.sort(new Comparator() {  
  25. @Override  
  26. public int compare(Student s1, Student s2) {  
  27. return s2.getName().compareTo(s1.getName());  
  28. }  
  29. });  
  30. }  

测试:


  1. MetricsObserable metricsObserable = new MetricsObserable(); 
  2. metricsObserable.addObserver(new AdminA()); 
  3. metricsObserable.addObserver(new AdminB()); 
  4. metricsObserable.updateCounter("request-count", 100l); 

观察者设计模式

观察者设计模式主要的使用场景在于一个对象变化之后,依赖该对象的对象会收到通知。典型的例子就是rss的订阅,当订阅了博客的rss之后,当博客更新之后,订阅者就会收到新的订阅信息。

jdk内置提供了Observable和Observer,用来实现观察者模式:


  1. // 定义一个Observable  
  2. public class MetricsObserable extends Observable {  
  3. private Map counterMap = new HashMap<>();  
  4. public void updateCounter(String key, Long value) {  
  5. counterMap.put(key, value);  
  6. setChanged();  
  7. notifyObservers(counterMap);  
  8. }  
  9. }  
  10. // Observer  
  11. public class AdminA implements Observer {  
  12. @Override  
  13. public void update(Observable o, Object arg) {  
  14. System.out.println("adminA: " + arg);  
  15. }  
  16. }  
  17. public class AdminB implements Observer {  
  18. @Override  
  19. public void update(Observable o, Object arg) {  
  20. System.out.println("adminB: " + arg);  
  21. }  

测试:


  1. MetricsObserable metricsObserable = new MetricsObserable();  
  2. metricsObserable.addObserver(new AdminA());  
  3. metricsObserable.addObserver(new AdminB());  
  4. metricsObserable.updateCounter("request-count", 100l); 

打印出:


  1. adminB: {request-count=100}  
  2. adminA: {request-count=100} 

享元模式

线程池中会构造几个核心线程用于处理,这些线程会去取阻塞队列里的任务然后进行执行。这些线程就是会被共享、且被重复使用的。因为线程的创建、销毁、调度都是需要消耗资源的,没有必要每次创建新的线程,而是共用一些线程。这就是享元模式的使用。类似的还有jdbc连接池,对象池等。

之前有一次面试被问到:


  1. Integer.valueOf("1") == Integer.valueOf("1") // true还是false 

当时回答的是false,后来翻了下Integer的源码发现Integer里面有个内部类IntegerCache,用于缓存一些共用的Integer。这个缓存的范围可以在jvm启动的时候进行设置。

其实后来想想也应该这么做,我们没有必要每次使用对象的时候都返回新的对象,可以共享这些对象,因为新对象的创建都是需要消耗内存的。

适配器模式

适配器模式比较好理解。像生活中插线口的插头有2个口的,也有3个口的。如果电脑的电源插口只有3个口的,但是我们需要一个2个口的插口的话,这个时候就需要使用插座来外接这个3个口的插头,插座上有2个口的插头。

这个例子跟我们编程一样,当用户系统的接口跟我们系统内部的接口不一致时,我们可以使用适配器来完成接口的转换。

使用继承的方式实现类的适配:


  1. public class Source {  
  2. public void method() {  
  3. System.out.println("source method");  
  4. }  
  5. }  
  6. interface Targetable {  
  7. void method();  
  8. void newMethod();  
  9. }  
  10. class Adapter extends Source implements Targetable {  
  11. @Override  
  12. public void newMethod() {  
  13. System.out.println("new method");  
  14. }  

测试:


  1. Targetable targetable = new Adapter();  
  2. targetable.method(); // source method  
  3. targetable.newMethod(); // new method 

上述方式是用接口和继承的方式实现适配器模式。当然我们也可以使用组合的方式实现(把Source当成属性放到Adapter中)。

单例模式

单例模式比较好理解,Spring就是典型的例子。被Spring中的容器管理的对象都有对应的scope,配置成singleton说明这个对象就是单例,也就是在Spring容器的生命周期中,这个类只有1个实例。

java中单例模式的写法也有好多种。比如懒汉式、饿汉式、内部类方式、枚举方式等。

需要注意的如果使用dcl的话需要初始化过程,这篇 Java内存模型之从JMM角度分析DCL 文章中说明了dcl的正确用法。

Effectice java中推荐的单例方式写法是使用枚举类型的方式。

外观模式

外观模式用来包装一组接口用于方便使用。 比如系统中分10个模块,有个功能需要组合使用所有的模块,这个时候就需要一个包装类包装这10个接口,然后进行业务逻辑的调用。

作者:佚名

来源:51CTO

时间: 2024-09-20 06:28:23

记录自己理解的一些设计模式的相关文章

《设计模式沉思录》目录—导读

版权声明设计模式沉思录Authorized translation from the English language edition, entitled Pattern Hatching: Design Patterns Applied, 9780201432930 by John Vlissides, published by Pearson Education, Inc., publishing as Addison-Wesley Professional. Copyright 1998

《研磨设计模式》试读样章

第1章  设计模式基础 1.1  设计模式是什么 1.1.1  什么是模式      从字面上来看,模,就是模型.模板的意思:式,就是方式.方法的意思.综合起来,所谓模式就是:可以作为模型或模板的方式或方法.再简单点说就是可以作为样板的方式或方法,类似于大家所熟悉的范例. 1.1.2  设计模式的概念 按照上面的理解,设计模式指的就是设计方面的模板,也即设计方面的方式或方法. 1.设计模式是解决方案 根据上面对设计模式的定义可以看出,归根结底,设计模式就是一些解决方案. 所谓解决方案,就是解决办

简析J2EE应用程序数据库类设计模式

本文主要介绍一种数据库相关类的设计模式.并介绍在J2EE框架中的具体实现,以及在事务处理方面的一些考虑. 一.设计模式简介 在开发J2EE应用程序时,通常是要找出应用程序中涉及到的各种信息,比如一个公司的产品目录,或一个网站的用户信息,我们会将这些信息放在数据库里. 在通常的设计中,我们要分析这些数据的属性和关系,然后进行数据库的逻辑设计,把各种信息用不同的表来存储.比如,要开发一个图书信息查询系统.可以创建下面两个表来分别表示书和出版社. table Book (ID, Name, ISBN,

工厂设计模式的探讨——iOS类簇的应用分析

工厂设计模式的探讨--iOS类簇的应用分析 一.何为设计模式 什么是设计模式,先来看段度娘的话:       设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的:设计模式使代码编制真正工程化:设计模式是软件工程的基石脉络,如同大厦的结构一样. 其实我们不需要这么专业,在我的理解,设计模式就是一种规范化的编程习惯,养成了这样的

《设计模式解析(第2版•修订版)》目录—导读

作者简介 设计模式解析(第2版•修订版) Alan Shalloway 美国Net Objectives咨询/培训公司的创始人和CEO.他是麻省理工学院的计算机科学硕士,具有30多年面向对象咨询.培训和软件开发的经验,并经常受邀在重要的软件开发会议(包括SD Expro.Java One.OOP和OOPSLA)上演讲. James R. Trott 美国一家大型金融机构的资深顾问.他是应用数学科学硕士.MBA和跨文化研究硕士.在其20年的职业生涯中,他一直将面向对象和基于模式的分析技术运用在知识

JavaScript设计模式初探_javascript技巧

目的:设计模式众多,尝试记录下学到的不同设计模式的优劣,方便以后查阅. 前言:半年前看高程的时候看到设计模式这章,云里雾里,不是看不明白,而是不明白为啥要如此麻烦只为创建一个对象.直到最近完成了自己第一个小项目,才体会到当代码量多起来时没有适当的规范与限制是多么大的灾难.于是重新翻开高程,总结下几种我学到的简单设计模式的优劣. 正文:本文一共介绍7种设计模式以及他们的应用场景.优劣. 1.工厂模式 直接用函数来封装对象,将对象作为返回值. function person (name,age) {

设计模式:开篇

  最近在整理设计模式这个系列,这里做一下全局的概括.本系列的文章对于初识设计模式的朋友也许不太适应,对于那些了解过或者使用过设计模式的人比较适应,本系列的文章对设计模式的关键点做了一个终结性的陈述,也列举了相关例子方便理解和记忆,但是并没有循序渐进的讲解.譬如,在适配器模式中,博主阐述了适配器的定义.类图.案例等,但是并没有阐述类似"比如你买了一个港版的iphone6s,但是大陆的插座不并适合,需要一个转接口,这个转接口就是适配器"这样的语句来更形象的讲解相关内容.   虽然关于设计

Java设计模式之代理模式(Proxy模式)介绍_java

理解并使用设计模式,能够培养我们良好的面向对象编程习惯,同时在实际应用中,可以如鱼得水,享受游刃有余的乐趣. Proxy是比较有用途的一种模式,而且变种较多,应用场合覆盖从小结构到整个系统的大结构,Proxy是代理的意思,我们也许有代理服务器等概念,代理概念可以解释为:在出发点到目的地之间有一道中间层,意为代理. 设计模式中定义:为其他对象提供一种代理以控制对这个对象的访问. 为什么要使用代理模式 1.授权机制 不同级别的用户对同一对象拥有不同的访问权利,如Jive论坛系统中,就使用Proxy进

请大神推荐java设计模式视频教程

问题描述 目前急需学习java设计模式,有没有讲得好容易理解的java设计模式视频?请各位推荐推荐 解决方案 解决方案二:专门讲设计模式的视频还真不太多,推荐看书吧,headfirst设计模式,比较生动,容易理解解决方案三:尚学堂马士兵讲的解决方案四:恩,同意一楼的,HeadFirst设计模式,我也正在看,挺好的.如果实在想看视频的话,好像慕课网上面也有一些设计模式的视频,不过好像只有几种解决方案五:搜索尚学堂,他们有免费的设计模式的视频,同时给你推荐一本书,<大话设计模式>,讲得很幽默,很清