Java 24种设计模式

目录

引子

设计模式原则

创建者模式

1.简单工厂模式

2.工厂模式

3.抽象工厂模式

4.单例模式

5.建造者模式

6.原型模式

结构型模式

7.代理模式

8.适配器模式

9.桥接模式

10.装饰模式

11.外观模式

12.组合模式

13.享元模式

行为型模式

14.策略模式

15.观察者模式

16.责任链模式

17.模板方式模式

18.状态模式

19.迭代器模式

20.命令模式

21.备忘录模式

22.访问者模式

23.中介者模式

24.解释器模式


引子

设计模式是很多程序员总结出来的最佳实践。曾经在刚开始写项目的时候学习过设计模式,在开发过程中,也主动或者被动的使用过。现在写代码虽说不会特意明确在用哪种设计模式,但潜移默化的写出来公认的最佳实践代码,毕竟看的比较清爽。为什么再看一遍设计模式,主要有几个原因:第一,很多优秀的源码基本都使用了设计模式,明确设计模式能够更好的看源码。第二,很多中间件设计理念也是基于设计模式的,还有其他的语言,都有自己的设计最佳实践。对于我来说,设计模式始于java,不止于java。第三,有了这种规范,可以更好的和他人沟通,言简意赅。

设计模式原则

很多优秀的文章和书籍都讲的很明白了,我说下自己的体会。 1.单一职责原则,就是一个类只负责做一件事情。这样就可以做到解耦合的效果,让代码看起来比较清爽,也体现了java的封装性。还有个原则叫迪米特法则,就是一个对象对另一个对象有尽量少的了解,说的也是解耦合的事情。 2.里氏替换原则和依赖导致原则,说的是继承的事情。父类可以做的事情,子类都可以去做,子类可以尽量去依赖父类去做事情;但是反过来,父类不能依赖子类去做一些事情。体现了java的继承特性。 3.接口隔离原则,接口也应该尽可能的隔离开来。其实类写多了,的确耦合性低,为了让他们交流起来,用的最多的就是接口,毕竟只需要知道做什么,怎么做,去访问那个具体的类吧。 4.开闭原则,对修改关闭,对拓展开放。就是代码需要有很好的延展性,对原有代码结构不能破坏。

创建者模式

创建者模式就是为了用优雅的方式创建我们使用的类。

1.简单工厂模式

这个用的比较少,就是有个工厂,告诉你我要什么东西,你造好了给我就行。比如说:

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
    }
}

2.工厂模式

这个其实和简单工厂模式差不太多,就是将工厂继续拆分,比如说刚刚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;
    }
}复制代码

3.抽象工厂模式

抽象工厂模式主要设计产品组的概念,就是某一个工厂生产出配套的一系列产品。例如,在生产足球的同时,SoccerFactory还可以生产与之配套的足球杂志。

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();
    }
}复制代码

4.单例模式

单例模式有很多种形式,最佳实践应该是两重判断,保证只new出来一个。单例可以说是非常普遍的设计模式了。单例就是指在服务容器的生命周期中只能有这么一个。比如说Servlet、Spring中注入的Bean等等都是单例的。

public class ShiroUtils {
    // 通过私有变量保存单例对象【添加了 volatile 修饰】
    public  volatile  static Session session;

    // 提供公共获取单例对象的方法
    public static Session getSession() {
        if (session == null){
            synchronized(ShiroUtils.class){
                if (session == null){
                    session = SecurityUtils.getSubject().getSession();
                }
            }
        }
        return session;
    }
}

5.建造者模式

将一个复杂对象分布创建。如果一个超大的类的属性特别多,我们可以把属性分门别类,不同属性组成一个稍微小一点的类,再把好几个稍微小点的类窜起来。比方说一个电脑,可以分成不同的稍微小点的部分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代码中,大量的配置不得不让配置类采用建造者模式,这样层次比较清晰。

6.原型模式

原型模式用的比较少,用于创建重复对象。需要实现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
    }
}

结构型模式

上面的设计模式可以帮助我们非常优雅的创建出来对象,下面看几个对象关系之间的模型。

7.代理模式

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

8.适配器模式

适配器,顾名思义,是让两个不兼容的东西可以一起工作。例如插座的电源是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");
    }
}

9.桥接模式

就是用于抽象化和实现化的解耦。又是解耦,貌似设计模式就是教我们如何优雅的解耦。提高了代码的拓展性,并且可以实现代码的动态切换。 最开始的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
    }
}

10.装饰模式

一个装饰类,在原来类的基础上增加一点功能。是不是和代理模式很像,我甚至可以将整个代码搬过来照样可以说的通的。这两个模式意思上有点差别,代理模式是原对象做不了那件事,必须让代理对象去做,主导侧重于代理对象,比如说买车。装饰模式是说,就是让原对象直接去做这件事,只是功能上增强一点,主导在于原对象。比如说炒菜的时候撒点盐。

11.外观模式

又称门面模式,就是一个门面,一个操作无需让对象知道其内部实现的复杂度,尽量让用户感知到是非常简单的。这就是为什么我们controller层尽量(或者说一定)少些业务逻辑,让controller层只是起到一个传参和通用性参数校验的功能,剩下的全交给service去做吧。我们还需要在代码中不断将“长得”特别长的代码封装成一个方法,“让处处都有好看的外观”。看一下我们曾写过的代码,这里只起到了传参的作用,究竟这个足球是怎么创建出来的,客户端不必担心。

   public static void main(String[] args) {
        Ball soccer = EasyBallFactory.createBall("soccer");
        System.out.println(soccer.create()); //give you a soccer
    }

12.组合模式

组合模式是将存在某种包含关系的数据组织在一起,典型的例子就是树状结构。例如菜单功能,一个菜单除了自己该有的属性,还可能包含子菜单,创建的时候可以使用递归的方法。

@Data
public class Menu{
    private String name;
    private int type;
    private List<Menu> childMenus;
}复制代码

13.享元模式

享元模式尽可能的让用户复用已经有的对象,从而避免造成反复创建对象的资源浪费。首先就会想到数据库连接池还有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
    }
}复制代码

行为型模式

创建了对象,对象之间有了结构关系,就要看下怎么更加优雅的相互作用了。

14.策略模式

定义一组算法, 将每个算法都封装起来, 并且使它们之间可以互换。可以说是一组算法的封装,根据客户端给出的不同要求,进行不同的运算。比如下面这个简易计算器。

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));
   }
}

15.观察者模式

定义了一种一对多的依赖关系,当一个对象(被观察者)状态改变的时候,所有依赖于该对象的观察者都会被通知,从而进行相关操作。很多中间件都依赖于观察者模式,例如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();
    }
}

16.责任链模式

责任链模式为请求创建一个接收者对象的链,对发送者和接受者进行解耦合。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);
}

17.模板方式模式

一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。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();
    }
}

18.状态模式

简单来说,就是一个对象有不同的状态,根据状态不同,可能有不同的行为。

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());
   }
}

19.迭代器模式

提供一个方法,可以顺序访问一个对象内部的各个元素,不需要知道内部构造。现在基本很少自己实现迭代器了,基本成熟的框架或者强大的JDK都会给出访问的方法,比如说java中iterator。这样做主要是进一步封装对象内部的结构,让行为和结构想耦合。这个不举例子了,用过iterator这个的小伙伴应该都清楚,就是不停的next,去访问下一个元素。

20.命令模式

命令模式是将请求以命令的形式包裹在对象中,并传递给对象,调用对象寻找到处理该命令的合适的对象,并将该命令传递给相应的对象,该对象执行。简单点说就是不同请求都封装成一个对象,不同的请求调用不同的执行者。

//真正干活的对象
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干活
    }
}

21.备忘录模式

相当于做一个快照,在不破坏对象本身结构的情况下,记录对象的一个状态,合适的时候可以恢复到这种状态。数据库做事务回滚的时候就用了这种方式。这里需要注意的是,对象不与备忘录本身耦合,而是跟备忘录管理类耦合(就是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());
   }
}

22.访问者模式

当对特定角色进行访问的时候,需要通过访问者进行访问。一个对象不太方便被你直接访问的时候,你需要将自己的引用交给访问者,通过访问者去访问该对象。比如说,化学课,想看一个细胞结构,由于肉眼无法直接看到微观世界的玩意,需要通过显微镜间接访问。

23.中介者模式

降低对象或者说事物之间通讯的复杂性,降低耦合。比如说分布式系统中,不是需要实时反馈的操作,我们无需直接对接,只需将想做的事告诉中间件,中间件告诉另外一个系统。比如说,访问(用户点击)一条新闻操作,同时需要记录是谁访问了什么新闻,同时给新闻浏览次数加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!");
   }
}

24.解释器模式

构建一种翻译方式,将某种语言或描述翻译成我们很好理解的语言或者描述。这里很好理解的意思是看得懂,看的快。本来我也想举什么编译器这种高大上的,将底层语言甚至机械语言和我们使用的高级编程语言。后来想了想,其实Map就可以看作一个很好的编译器,key你可以存放一个非常小的字符串,value理论上你可以存放任何东西,所以代码就不写了。

这里贴上其他博主对设计模式的理解博客:设计模式看了又忘,忘了又看?_LieBrother的博客-CSDN博客

  • 6
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值