设计模式是不少程序员总结出来的最佳实践。曾经在刚开始写项目的时候学习过设计模式,在开发过程当中,也主动或者被动的使用过。如今写代码虽然说不会特地明确在用哪一种设计模式,但潜移默化的写出来公认的最佳实践代码,毕竟看的比较清爽。为何再看一遍设计模式,主要有几个缘由:第一,不少优秀的源码基本都使用了设计模式,明确设计模式可以更好的看源码。第二,不少中间件设计理念也是基于设计模式的,还有其余的语言,都有本身的设计最佳实践。对于我来讲,设计模式始于java,不止于java。第三,有了这种规范,能够更好的和他人沟通,言简意赅。java
不少优秀的文章和书籍都讲的很明白了,我说下本身的体会。
1.单一职责原则,就是一个类只负责作一件事情。这样就能够作到解耦合的效果,让代码看起来比较清爽,也体现了java的封装性。还有个原则叫迪米特法则,就是一个对象对另外一个对象有尽可能少的了解,说的也是解耦合的事情。
2.里氏替换原则和依赖致使原则,说的是继承的事情。父类能够作的事情,子类均可以去作,子类能够尽可能去依赖父类去作事情;可是反过来,父类不能依赖子类去作一些事情。体现了java的继承特性。
3.接口隔离原则,接口也应该尽量的隔离开来。其实类写多了,的确耦合性低,为了让他们交流起来,用的最多的就是接口,毕竟只须要知道作什么,怎么作,去访问那个具体的类吧。
4.开闭原则,对修改关闭,对拓展开放。就是代码须要有很好的延展性,对原有代码结构不能破坏。node
建立者模式就是为了用优雅的方式建立咱们使用的类。mysql
这个用的比较少,就是有个工厂,告诉你我要什么东西,你造好了给我就行。好比说:程序员
public interface Ball { public String create(); }
public class Soccer implements Ball { @Override public String create() { return "give you a soccer"; } }
public class BasketBall implements Ball { @Override public String create() { return "give you a basketBall"; } }
public class EasyBallFactory { public static Ball createBall(String name){ if (name.equals("basketball")){ return new BasketBall(); }else if(name.equals("soccer")){ return new Soccer(); }else { return null; } } public static void main(String[] args) { Ball soccer = EasyBallFactory.createBall("soccer"); Ball basketBall = EasyBallFactory.createBall("basketball"); System.out.println(soccer.create()); //give you a soccer System.out.println(basketBall.create()); //give you a basketBall } }
这个其实和简单工厂模式差不太多,就是将工厂继续拆分,好比说刚刚EasyBallFactory是一个总工厂,咱们如今拆分红SoccerFactory和BasketBallFactory分别生产足球和篮球。某个工厂内部能够根据需求生产不一样的产品,好比说soccer能够生产不一样大小的出来。算法
public interface BallFactory { public Ball create(); }
public class SoccerFactory implements BallFactory { @Override public Ball create() { //do something return null; } } public class BasketBallFactory implements BallFactory { @Override public Ball create() { //do something return null; } }
抽象工厂模式主要设计产品组的概念,就是某一个工厂生产出配套的一系列产品。例如,在生产足球的同时,SoccerFactory还能够生产与之配套的足球杂志。sql
public interface Journal { public String create(); } public class SoccerJournal implements Journal{ @Override public String create() { return "give you a Soccer Journal..."; } } public class SoccerFactory implements BallFactory { @Override public Ball create() { return new Soccer(); } public Journal createJournal(){ return new SoccerJournal(); } }
单例模式有不少种形式,最佳实践应该是两重判断,保证只new出来一个。单例能够说是很是广泛的设计模式了。单例就是指在服务容器的生命周期中只能有这么一个。好比说Servlet、Spring中注入的Bean等等都是单例的。数据库
public class ShiroUtils { public static Session session; public static Session getSession() { if (session == null){ synchronized(ShiroUtils.class){ if (session == null){ session = SecurityUtils.getSubject().getSession(); } } } return session; } }
将一个复杂对象分布建立。若是一个超大的类的属性特别多,咱们能够把属性分门别类,不一样属性组成一个稍微小一点的类,再把好几个稍微小点的类窜起来。比方说一个电脑,能够分红不一样的稍微小点的部分CPU、主板、显示器。CPU、主板、显示器分别有更多的组件,再也不细分。编程
@Data public class Computer{ private CPU cpu;//cpu 是个接口,有不一样实现如InterCPU AMDCPU 等等 private MainBoard mainBoard;//mainBoard 是个接口,有不一样的实现 private DisPlayer disPlayer;//disPlayer 是个接口,有不一样的实现 }
public abstract class Builder { abstract void buildCPU(); abstract void buildMainBoard(); abstract void buildDisPlayer(); abstract Computer createComputer(); }
public class XiaoMiBuilder extends Builder{ private Computer computer = new Computer(); @Override void buildCPU() { computer.setCpu(new InterCPU()); } @Override void buildMainBoard() { computer.setMainBoard(new AMainBoard()); } @Override void buildDisPlayer() { computer.setDisPlayer(new ADisPlayer()); } @Override Computer createComputer() { return computer; } }
SpringBoot实现了0配置,几乎全部的配置都写到了java代码中,大量的配置不得不让配置类采用建造者模式,这样层次比较清晰。设计模式
原型模式用的比较少,用于建立重复对象。须要实现Cloneable 能够选择重写clone()方法。clone分为浅克隆和深克隆。浅克隆只是克隆引用,对象仍是一个。深克隆是对象也新建立了一个,以下:缓存
@Data @Builder public class User implements Cloneable{ private String name; private int age; @Override protected User clone() throws CloneNotSupportedException { return new User(this.name,this.age); } public static void main(String[] args) throws CloneNotSupportedException { User user1 = new User("pjjlt",25); User user2 = user1.clone(); user1.setAge(18); System.out.println(user2.getAge()); //25 } }
上面的设计模式能够帮助咱们很是优雅的建立出来对象,下面看几个对象关系之间的模型。
Spring的AOP用的是动态代理,何为动态不看了,用过Spring的小伙伴都知道吧。单纯看一下最基础代理模式是什么样的。代理就是,一个对象辅助另外一个对象去作某件事,同时还能够增长一点辅助功能。例如,你买车,的确是你花钱把车买到了,可是你不可能直接去和厂家谈吧,你应该经过4S店购买,同时4S店帮助你入保险扣税等操做,最终你才获得了你想要的车。
public interface Buy { public void buyCar(); }
public class People implements Buy { @Override public void buyCar() { System.out.println("you get a car"); } }
public class ProxyPeople implements Buy{ private People people; public ProxyPeople(People people){ this.people=people; } @Override public void buyCar() { System.out.println("4s店帮你纳税、上保险..."); people.buyCar(); } public static void main(String[] args) { Buy buy = new ProxyPeople(new People()); buy.buyCar(); } } //输出: 4s店帮你纳税、上保险... you get a car
适配器,顾名思义,是让两个不兼容的东西能够一块儿工做。例如插座的电源是220V,手机直接给他220V 50HZ的交流电我相信通常都会直接炸了(除了诺基亚...)手机充电器就进行了适配,将电压变小,交流电变成直流电。除了这种须要改变属性的操做(比较好说,不举例子了),适配器还用于在接口继承方面。假设一个顶级接口有一大堆方法须要实现类实现,我新写了个类只是想选择的实现一两个接口,那其余的方法我是否是都须要实现一下,即便是空实现(单纯实现,不进行任何逻辑操做),这是咱们就须要一个适配器类,空实现那些方法,个人新类只须要继承这个适配器类就行了,要是想实现某个方法,只须要重写掉配置类中对应的方法就好。这种模式基本都会用到,毕竟谁的代码还没个顶级接口啊。
public interface ATopIntf { public void one(); public void two(); public void three(); }
public class Adapter implements ATopIntf { @Override public void one() { } @Override public void two() { } @Override public void three() { } }
public class You extends Adapter { @Override public void one() { super.one(); System.out.println("one"); } }
就是用于抽象化和实现化的解耦。又是解耦,貌似设计模式就是教咱们如何优雅的解耦。提升了代码的拓展性,而且能够实现代码的动态切换。
最开始的Ball、Soccer、BasketBall接着用,增长新的类
public class BallCut { private Ball ball; public Ball getBall() { return ball; } public void setBall(Ball ball) { this.ball = ball; } public void create(){ System.out.println(ball.create()); } public static void main(String[] args) { BallCut ballCut = new BallCut(); ballCut.setBall(new Soccer()); ballCut.create(); //give you a soccer ballCut.setBall(new BasketBall()); ballCut.create(); //give you a basketBall } }
一个装饰类,在原来类的基础上增长一点功能。是否是和代理模式很像,我甚至能够将整个代码搬过来照样能够说的通的。这两个模式意思上有点差异,代理模式是原对象作不了那件事,必须让代理对象去作,主导侧重于代理对象,好比说买车。装饰模式是说,就是让原对象直接去作这件事,只是功能上加强一点,主导在于原对象。好比说炒菜的时候撒点盐。
又称门面模式,就是一个门面,一个操做无需让对象知道其内部实现的复杂度,尽可能让用户感知到是很是简单的。这就是为何咱们controller层尽可能(或者说必定)少些业务逻辑,让controller层只是起到一个传参和通用性参数校验的功能,剩下的全交给service去作吧。咱们还须要在代码中不断将“长得”特别长的代码封装成一个方法,“让到处都有好看的外观”。看一下咱们曾写过的代码,这里只起到了传参的做用,究竟这个足球是怎么建立出来的,客户端没必要担忧。
public static void main(String[] args) { Ball soccer = EasyBallFactory.createBall("soccer"); System.out.println(soccer.create()); //give you a soccer }
组合模式是将存在某种包含关系的数据组织在一块儿,典型的例子就是树状结构。例如菜单功能,一个菜单除了本身该有的属性,还可能包含子菜单,建立的时候可使用递归的方法。
@Data public class Menu{ private String name; private int type; private List<Menu> childMenus; }
享元模式尽量的让用户复用已经有的对象,从而避免形成反复建立对象的资源浪费。首先就会想到数据库链接池还有String常量池,延伸一下,几乎全部和缓存有关的代码,多少都会用到享元模式。享元模式要求大部分的对象能够外部化。这边要说两个概念,享元模式对象的属性能够分为两个部分,内部状态和外部状态,内部状态是指不会随环境而改变的值,好比说我的信息,外部状态是指随环境改变的值,不能进行共享的信息,如某大学生选修的课程。
public abstract class Flyweight { //内部状态 private String name; private String age; //外部状态 private final String subject; protected Flyweight(String subject) { this.subject = subject; } //行为 public abstract void exam(); public String getSubject() { return subject; } }
public class RealFlyweight extends Flyweight { @Override public void exam() { System.out.println(this.getSubject()+" is examing..."); } public RealFlyweight(String subject){ super(subject); } }
public class FlyweightFactory { //定义一个池子 private static HashMap<String,Flyweight> pool= new HashMap(); public static Flyweight getFlyweight(String subject){ Flyweight flyweight =null; if (pool.containsKey(subject)){ flyweight=pool.get(subject); }else { flyweight = new RealFlyweight(subject); pool.put(subject,flyweight); } return flyweight; } public static void main(String[] args) { System.out.println(pool.size());//0 getFlyweight("math"); System.out.println(pool.size());//1 getFlyweight("english"); System.out.println(pool.size());//2 getFlyweight("math"); System.out.println(pool.size());//2 } }
建立了对象,对象之间有告终构关系,就要看下怎么更加优雅的相互做用了。
定义一组算法, 将每一个算法都封装起来, 而且使它们之间能够互换。能够说是一组算法的封装,根据客户端给出的不一样要求,进行不一样的运算。好比下面这个简易计算器。
public interface Strategy { public int doOperation(int num1, int num2); } public class OperationAdd implements Strategy{ @Override public int doOperation(int num1, int num2) { return num1 + num2; } } public class OperationSubstract implements Strategy{ @Override public int doOperation(int num1, int num2) { return num1 - num2; } } public class Context { private Strategy strategy; public Context(Strategy strategy){ this.strategy = strategy; } public int executeStrategy(int num1, int num2){ return strategy.doOperation(num1, num2); } }
public class StrategyPatternDemo { public static void main(String[] args) { Context context = new Context(new OperationAdd()); System.out.println("10 + 5 = " + context.executeStrategy(10, 5)); context = new Context(new OperationSubstract()); System.out.println("10 - 5 = " + context.executeStrategy(10, 5)); } }
定义了一种一对多的依赖关系,当一个对象(被观察者)状态改变的时候,全部依赖于该对象的观察者都会被通知,从而进行相关操做。不少中间件都依赖于观察者模式,例如RabbitMQ,还有那些事件驱动模型(好像node就是)。下面举个例子,被观察者是监考老师,考试时间结束,通知全部观察者学生上交试卷。
@Data public class Student { private String name; public Student(String name){ this.name=name; } public void dosomething(){ System.out.println(getName()+"交卷了"); } }
public class Teacher { private Set<Student> students = new HashSet<>(); public void addStudent(Student student){ students.add(student); } public void removeStudent(Student student){ students.remove(student); } public void doNotify(){ for(Student student:students){ student.dosomething(); } } public static void main(String[] args) { Teacher teacher = new Teacher(); Student student = new Student("张三"); Student student1 = new Student("李四"); teacher.addStudent(student); teacher.addStudent(student1); teacher.doNotify(); } }
责任链模式为请求建立一个接收者对象的链,对发送者和接受者进行解耦合。filter链就是责任链模式。
public abstract class Handler { //下一个处理者 private Handler nextHandler; public final Response handleMessage(Request request){ Response response =null; if (this.getHandlerLevel().equals(request.getRequestLevel())){ response = this.echo(request); }else { if (this.nextHandler!=null){ //传递给下一个 response = this.nextHandler.handleMessage(request); }else { response =new Response() } } return response; } public Handler getNextHandler() { return nextHandler; } public void setNextHandler(Handler nextHandler) { this.nextHandler = nextHandler; } protected abstract Level getHandlerLevel(); protected abstract Response echo(Request request); }
一个抽象类公开定义了执行它的方法的方式/模板。它的子类能够按须要重写方法实现,但调用将以抽象类中定义的方式进行。SpringBoot为用户封装了不少继承代码,都用到了模板方式,例如那一堆XXXtemplate。
public abstract class DBTemplate { abstract void open(); abstract void select(); abstract void close(); //一个搜索模板 public final void selectTemplate(){ open(); select(); close(); } }
public class MysqlDB extends DBTemplate { @Override void open() { System.out.println("Mysql open..."); } @Override void select() { System.out.println("Mysql select..."); } @Override void close() { System.out.println("Mysql close..."); } public static void main(String[] args) { DBTemplate mysql = new MysqlDB(); mysql.selectTemplate(); } }
简单来讲,就是一个对象有不一样的状态,根据状态不一样,可能有不一样的行为。
public interface State { public void doAction(Context context); } public class StartState implements State { public void doAction(Context context) { System.out.println("Player is in start state"); context.setState(this); } public String toString(){ return "Start State"; } } public class StopState implements State { public void doAction(Context context) { System.out.println("Player is in stop state"); context.setState(this); } public String toString(){ return "Stop State"; } } public class Context { private State state; public Context(){ state = null; } public void setState(State state){ this.state = state; } public State getState(){ return state; } } public class StatePatternDemo { public static void main(String[] args) { Context context = new Context(); StartState startState = new StartState(); startState.doAction(context); System.out.println(context.getState().toString()); StopState stopState = new StopState(); stopState.doAction(context); System.out.println(context.getState().toString()); } }
提供一个方法,能够顺序访问一个对象内部的各个元素,不须要知道内部构造。如今基本不多本身实现迭代器了,基本成熟的框架或者强大的JDK都会给出访问的方法,好比说java中iterator。这样作主要是进一步封装对象内部的结构,让行为和结构想耦合。这个不举例子了,用过iterator这个的小伙伴应该都清楚,就是不停的next,去访问下一个元素。
命令模式是将请求以命令的形式包裹在对象中,并传递给对象,调用对象寻找处处理该命令的合适的对象,并将该命令传递给相应的对象,该对象执行。简单点说就是不一样请求都封装成一个对象,不一样的请求调用不一样的执行者。
//真正干活的对象 public class Receiver { public void doSomething(){ System.out.println("Receiver干活"); } }
//命令对象 public abstract class Command { public abstract void exectue(); }
//命令实现类 public class ConcreteComand extends Command{ //干活那个 private Receiver receiver; public ConcreteComand(Receiver receiver) { this.receiver = receiver; } @Override public void exectue() { this.receiver.doSomething(); } public static void main(String[] args) { Receiver receiver = new Receiver(); Command command =new ConcreteComand(receiver); command.exectue();//Receiver干活 } }
至关于作一个快照,在不破坏对象自己结构的状况下,记录对象的一个状态,合适的时候能够恢复到这种状态。数据库作事务回滚的时候就用了这种方式。这里须要注意的是,对象不与备忘录自己耦合,而是跟备忘录管理类耦合(就是List<备忘录>),这个好理解,毕竟快照不止一个嘛。
@Data//备忘录 public class Memento { private String state; } @Data //某对象 public class Originator { private String state; public Memento saveStateToMemento(){ return new Memento(state); } public void getStateFromMemento(Memento Memento){ state = Memento.getState(); } } //备忘录管理类 public class CareTaker { private List<Memento> mementoList = new ArrayList<Memento>(); public void add(Memento state){ mementoList.add(state); } public Memento get(int index){ return mementoList.get(index); } public static void main(String[] args) { Originator originator = new Originator(); CareTaker careTaker = new CareTaker(); originator.setState("State #1"); originator.setState("State #2"); careTaker.add(originator.saveStateToMemento()); originator.setState("State #3"); careTaker.add(originator.saveStateToMemento()); originator.setState("State #4"); System.out.println("Current State: " + originator.getState()); originator.getStateFromMemento(careTaker.get(0)); System.out.println("First saved State: " + originator.getState()); originator.getStateFromMemento(careTaker.get(1)); System.out.println("Second saved State: " + originator.getState()); } }
当对特定角色进行访问的时候,须要经过访问者进行访问。一个对象不太方便被你直接访问的时候,你须要将本身的引用交给访问者,经过访问者去访问该对象。好比说,化学课,想看一个细胞结构,因为肉眼没法直接看到微观世界的玩意,须要经过显微镜间接访问。
下降对象或者说事物之间通信的复杂性,下降耦合。好比说分布式系统中,不是须要实时反馈的操做,咱们无需直接对接,只需将想作的事告诉中间件,中间件告诉另一个系统。好比说,访问(用户点击)一条新闻操做,同时须要记录是谁访问了什么新闻,同时给新闻浏览次数加1,还要实时更新用户喜爱...总之要更新n个数据库表,甚至还要操做像ES,Mongo等多个中间件数据。可是对于用户来讲,我只是作了一个点击操做,但愿获得的结果就是看条新闻啊,你这么多操做,搞这么慢,用户体验不好啊,并且并发量也很低,那不如作成两个小系统,A系统,拉取新闻,推送,并组装一个信息扔给MQ中间件,ok,结束,用户看到新闻。而后B系统监听,获得这个消息,进行各类更新,这里,这个中间件就是咱们的中介。再好比说,MVC中的控制层就是展现层和模型层的中介。再好比说,下面这个聊天室:
public class ChatRoom { public static void showMessage(User user, String message){ System.out.println(new Date().toString() + " [" + user.getName() +"] : " + message); } } @Data public class User { private String name; public User(String name){ this.name = name; } public void sendMessage(String message){ ChatRoom.showMessage(this,message); } public static void main(String[] args) { User robert = new User("Robert"); User john = new User("John"); robert.sendMessage("Hi! John!"); john.sendMessage("Hello! Robert!"); } }
构建一种翻译方式,将某种语言或描述翻译成咱们很好理解的语言或者描述。这里很好理解的意思是看得懂,看的快。原本我也想举什么编译器这种高大上的,将底层语言甚至机械语言和咱们使用的高级编程语言。后来想了想,其实Map就能够看做一个很好的编译器,key你能够存放一个很是小的字符串,value理论上你能够存听任何东西,因此代码就不写了。
呜呼,废了好长的时间,终于把这些设计模式有回顾了一遍,感受对他们其中的某些有了更深入的理解吧。就把这篇文章看成本身的一个小字典吧,未来须要什么看什么,点看看一看,顺即可以继续丰富下内容。有些东西就该本身去记录一下,毕竟好记性不如烂笔头嘛。又快十一点了,睡觉...