电话
0291-273489802
点击上方 "法式员小乐"关注, 星标或置顶一起发展 第一时间与你相约 逐日英文 One of the most important things in the world is to know oneself. Therefore occasional isolation and meditation are necessary for him. 人生最重要的事之一是发现自己,所以有须要偶然与孤苦、沉思为伍。逐日掏心话 人生最恐怖的不是眼睛看不见了,而是心失去了偏向。幸福就是只要牵对了手,就算失去了偏向,你也不会再畏惧。泉源:fangjian0423 | 责编:乐乐 链接:fangjian0423.github.io/2017/03/26/design-pattern/ 法式员小乐(ID:study_tech)第 716 次推文 图片来自网络 往日回首:你的电脑被黑客黑过吗? 正文 记载一下自己明白的一些设计模式,并只管使用表达清楚的例子举行解说。
计谋模式 计谋模式应该是最基础的一个设计模式,它是对行为的一个抽象。jdk中的Comparator比力器就是一个使用计谋设计模式的计谋。好比有一个Student学生类,有name和age两个属性。
如果有个需求需要打印学生名单,并根据字母顺序排序,可以使用Comparator接口并在内部使用name举行比力即可。如果哪一天需要根据年事举行排序,那么只需要修改Comparator即可,也就是使用一个新的计谋,其它完全稳定。
工厂模式 工厂模式的意义在于工具的建立、治理可以使用工厂去治理,而不是建立者自身。最典型的工厂模式使用者就是Spring,Spring内部的容器就是一个工厂,所有的bean都由这个容器治理,包罗它们的建立、销毁、注入都被这个容器治理。
工厂模式分简朴工厂和抽象工厂。它们的区别在于抽象工厂抽象水平更高,把工厂也抽象成了一个接口,这样可以再每添加一个新的工具的时候而不需要修改工厂的代码。好比有个Repository接口,用于存储数据,有DatabaseRepository,CacheRepository,FileRepository划分在数据库,缓存,文件中存储数据,界说如下: public interface Repository { void save(Object obj); } class DatabaseRepository implements Repository { @Override public void save(Object obj) { System.out.println("save in database"); } } class CacheRepository implements Repository { @Override public void save(Object obj) { System.out.println("save in cache"); } } class FileRepository implements Repository { @Override public void save(Object obj) { System.out.println("save in file"); } } 简朴工厂的使用 public class RepositoryFactory { public Repository create(String type) { Repository repository = null; switch (type) { case "db": repository = new DatabaseRepository(); break; case "cache": repository = new CacheRepository(); break; case "file": repository = new FileRepository(); break; } return repository; } public static void main(String[] args) { RepositoryFactory factory = new RepositoryFactory(); factory.create("db").save(new Object()); factory.create("cache").save(new Object()); factory.create("file").save(new Object()); } } 简朴工厂的毛病在于每添加一个新的Repository,都必须修改RepositoryFactory中的代码。
抽象工厂的使用 public interface RepositoryFactoryProvider { Repository create(); } class DatabaseRepositoryFactory implements RepositoryFactoryProvider { @Override public Repository create() { return new DatabaseRepository(); } } class CacheRepositoryFactory implements RepositoryFactoryProvider { @Override public Repository create() { return new CacheRepository(); } } class FileRepositoryFactory implements RepositoryFactoryProvider { @Override public Repository create() { return new FileRepository(); } } 抽象工厂的测试: RepositoryFactoryProvider dbProvider = new DatabaseRepositoryFactory(); dbProvider.create().save(new Object()); RepositoryFactoryProvider cacheProvider = new CacheRepositoryFactory(); cacheProvider.create().save(new Object()); RepositoryFactoryProvider fileProvider = new FileRepositoryFactory(); fileProvider.create().save(new Object()); 抽象工厂把工厂也举行了抽象话,所以添加一个新的Repository的话,只需要新增一个RepositoryFactory即可,原有代码不需要修改。装饰者模式 装饰者模式的作用就在于它可以在不改变原有类的基础上动态地给类添加新的功效。之前写过一篇通过源码分析MyBatis的缓存文章,mybatis中的query就是使用了装饰者设计模式。
用一段简朴的代码来模拟一下mybatis中query的实现原理: @Data @AllArgsConstructor @ToString class Result { // 查询效果类,相当于一个domain private Object obj; private String sql; } public interface Query { // 查询接口,有简朴查询缓和存查询 Result query(String sql); } public class SimpleQuery implements Query { // 简朴查询,相当于直接查询数据库,这里直接返回Result,相当于是数据库查询的效果 @Override public Result query(String sql) { return new Result(new Object(), sql); } } public class CacheQuery implements Query { // 缓存查询,如果查询相同的sql,不直接查询数据库,而是返回map中存在的Result private Query query; private Map<String, Result> cache = new HashMap<>(); public CacheQuery(Query query) { this.query = query; } @Override public Result query(String sql) { if(cache.containsKey(sql)) { return cache.get(sql); } Result result = query.query(sql); cache.put(sql, result); return result; } } 测试: Query simpleQuery = new SimpleQuery(); System.out.println(simpleQuery.query("select * from t_student") == simpleQuery.query("select * from t_student")); // false Query cacheQuery = new CacheQuery(simpleQuery); System.out.println(cacheQuery.query("select * from t_student") == cacheQuery.query("select * from t_student")); // true 这里CacheQuery就是一个装饰类,SimpleQuery是一个被装饰者。我们通过装饰者设计模式动态地给SimpleQuery添加了缓存功效,而不需要修改SimpleQuery的代码。
固然,装饰者模式也有缺点,就是会存在太多的类。如果我们需要添加一个过滤的查询(sql中有敏感字的就直接返回null,而不查询数据库),只需要可以添加一个FilterQuery装饰者即可: public class FilterQuery implements Query { private Query query; private List<String> words = new ArrayList<>(); public FilterQuery(Query query) { this.query = query; words.add("fuck"); words.add("sex"); } @Override public Result query(String sql) { for(String word : words) { if(sql.contains(word)) return null; } return query.query(sql); } } Query filterQuery = new FilterQuery(simpleQuery); System.out.println(filterQuery.query("select * from t_student where name = 'fuck'")); // null 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自带的署理模式完成盘算方法挪用时间的需求: // UserService接口 public interface IUserService { void printAll(); } // UserService实现类 class UserService implements IUserService { @Override public void printAll() { System.out.println("print all users"); } } // InvocationHandler计谋,这里打印了方法挪用前后的时间 @AllArgsConstructor class UserInvocationHandler implements InvocationHandler { private IUserService userService; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("start : " + System.currentTimeMillis()); Object result = method.invoke(userService, args); System.out.println("end : " + System.currentTimeMillis()); return result; } } 测试: IUserService userService = new UserService(); UserInvocationHandler uih = new UserInvocationHandler(userService); IUserService proxy = (IUserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), new Class[] {IUserService.class}, uih); proxy.printAll(); // 打印出start : 1489665566456 print all users end : 1489665566457 组合模式 组合模式经常跟计谋模式配合使用,用来组合所有的计谋,并遍历这些计谋找出满足条件的计谋。
之前写过一篇SpringMVC关于json、xml自动转换的原理研究文章,内里springmvc把返回的返回值映射给用户的response做了一层抽象,封装到了HandlerMethodReturnValueHandler计谋接口中。在HandlerMethodReturnValueHandlerComposite类中,使用存在的HandlerMethodReturnValueHandler对返回值举行处置惩罚,在HandlerMethodReturnValueHandlerComposite内部的代码如下: // 计谋荟萃 private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<HandlerMethodReturnValueHandler>(); @Override public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { // 挪用selectHandler方法 HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); if (handler == null) { throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName()); } handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); // 使用找到的handler举行处置惩罚 } private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) { boolean isAsyncValue = isAsyncReturnValue(value, returnType); // 遍历存在的HandlerMethodReturnValueHandler for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) { if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) { continue; } if (handler.supportsReturnType(returnType)) { // 找到匹配的handler return handler; } } return null; } 模板模式 跟计谋模式类似,模板模式会先界说好实现的逻辑步骤,可是详细的实现方式由子类完成,跟计谋模式的区别就是模板模式是有逻辑步骤的。好比要给院系里的学生排序,并取出排名第一的学生。
这里就有2个步骤,划分是排序和取出第一名学生。一段伪代码: public abstract class AbstractStudentGetter { public final Student getStudent(List<Student> students) { sort(students); // 第一步 if(!CollectionUtils.isEmpty(students)) { return students.get(0); // 第二步 } return null; } abstract public void sort(List<Student> students); } class AgeStudentGetter extends AbstractStudentGetter { // 取出年龄最大的学生 @Override public void sort(List<Student> students) { students.sort(new Comparator<Student>() { @Override public int compare(Student s1, Student s2) { return s2.getAge() - s1.getAge(); } }); } } class NameStudentGetter extends AbstractStudentGetter { // 根据名字字母排序取出第一个学生 @Override public void sort(List<Student> students) { students.sort(new Comparator<Student>() { @Override public int compare(Student s1, Student s2) { return s2.getName().compareTo(s1.getName()); } }); } } 测试: AbstractStudentGetter ageGetter = new AgeStudentGetter(); AbstractStudentGetter nameGetter = new NameStudentGetter(); List<Student> students = new ArrayList<>(); students.add(new Student("jim", 22)); students.add(new Student("format", 25)); System.out.println(ageGetter.getStudent(students)); // Student(name=format, age=25) System.out.println(nameGetter.getStudent(students)); // Student(name=jim, age=22) 视察者设计模式 视察者设计模式主要的使用场景在于一个工具变化之后,依赖该工具的工具会收到通知。
典型的例子就是rss的订阅,当订阅了博客的rss之后,当博客更新之后,订阅者就会收到新的订阅信息。jdk内置提供了Observable和Observer,用来实现视察者模式: // 界说一个Observable public class MetricsObserable extends Observable { private Map<String, Long> counterMap = new HashMap<>(); public void updateCounter(String key, Long value) { counterMap.put(key, value); setChanged(); notifyObservers(counterMap); } } // Observer public class AdminA implements Observer { @Override public void update(Observable o, Object arg) { System.out.println("adminA: " + arg); } } public class AdminB implements Observer { @Override public void update(Observable o, Object arg) { System.out.println("adminB: " + arg); } } 测试: MetricsObserable metricsObserable = new MetricsObserable(); metricsObserable.addObserver(new AdminA()); metricsObserable.addObserver(new AdminB()); metricsObserable.updateCounter("request-count", 100l); 打印出: adminB: {request-count=100} adminA: {request-count=100} 享元模式 线程池中会结构几个焦点线程用于处置惩罚,这些线程会去取阻塞行列里的任务然后举行执行。这些线程就是会被共享、且被重复使用的。
因为线程的建立、销毁、调理都是需要消耗资源的,没有须要每次建立新的线程,而是共用一些线程。这就是享元模式的使用。类似的另有jdbc毗连池,工具池等。之前有一次面试被问到: Integer.valueOf("1") == Integer.valueOf("1") // true还是false 其时回覆的是false,厥后翻了下Integer的源码发现Integer内里有个内部类IntegerCache,用于缓存一些共用的Integer。
这个缓存的规模可以在jvm启动的时候举行设置。其实厥后想想也应该这么做,我们没有须要每次使用工具的时候都返回新的工具,可以共享这些工具,因为新工具的建立都是需要消耗内存的。适配器模式 适配器模式比力好明白。
像生活中插线口的插头有2个口的,也有3个口的。如果电脑的电源插口只有3个口的,可是我们需要一个2个口的插口的话,这个时候就需要使用插座来外接这个3个口的插头,插座上有2个口的插头。这个例子跟我们编程一样,当用户系统的接口跟我们系统内部的接口纷歧致时,我们可以使用适配器来完成接口的转换。
使用继续的方式实现类的适配: public class Source { public void method() { System.out.println("source method"); } } interface Targetable { void method(); void newMethod(); } class Adapter extends Source implements Targetable { @Override public void newMethod() { System.out.println("new method"); } } 测试: Targetable targetable = new Adapter(); targetable.method(); // source method targetable.newMethod(); // new method 上述方式是用接口和继续的方式实现适配器模式。固然我们也可以使用组合的方式实现(把Source当成属性放到Adapter中)。
单例模式 单例模式比力好明白,Spring就是典型的例子。被Spring中的容器治理的工具都有对应的scope,设置成singleton说明这个工具就是单例,也就是在Spring容器的生命周期中,这个类只有1个实例。
java中单例模式的写法也有很多多少种。好比懒汉式、饿汉式、内部类方式、枚举方式等。
需要注意的如果使用dcl的话需要初始化历程,这篇Java内存模型之从JMM角度分析DCL文章中说明晰dcl的正确用法。Effectice java中推荐的单例方式写法是使用枚举类型的方式。
外观模式 外观模式用来包装一组接口用于利便使用。好比系统中分10个模块,有个功效需要组合使用所有的模块,这个时候就需要一个包装类包装这10个接口,然后举行业务逻辑的挪用。接待在留言区留下你的看法,一起讨论提高。
如果今天的文章让你有新的启发,学习能力的提升上有新的认识,接待转发分享给更多人。猜你还想看 阿里、、百度、华为、京东最新面试题搜集 Linux 系统 CPU 100% 异常排查实践与总结 动画演绎Java常用数据结构(建议收藏) MyBatis 的9种设计模式,我猜你纷歧定知道! 关注「法式员小乐」,收看更多精彩内容嘿,你在看吗?。
本文来源:开运体育官网入口-www.meinaugenlicht.com