Java设计模式详解

一、设计模式原则

(1)单一职责原则,就是一个类只负责做一件事情。这样就可以做到解耦合的效果,让代码看起来比较清爽,也体现了java的封装性。还有个原则叫迪米特法则,就是一个对象对另一个对象有尽量少的了解,说的也是解耦合的事情。

(2)里氏替换原则和依赖导致原则,说的是继承的事情。父类可以做的事情,子类都可以去做,子类可以尽量去依赖父类去做事情,但是反过来,父类不能依赖子类去做一些事情。体现了java的继承特性。

(3)接口隔离原则,接口也应该尽可能的隔离开来。其实类写多了,的确耦合性低,为了让他们交流起来,用的最多的就是接口,毕竟只需要知道做什么,怎么做,去访问那个具体的类吧。

(4)开闭原则,对修改关闭,对拓展开放。就是代码需要有很好的延展性,对原有代码结构不能破坏。

二、创建者模式

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

1. 简单工厂模式

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

public interface Ball {

  public String create();

}
public class Basketball implements Ball {

  @Override
  public String create() {
    return "give you a basketball";
  }

}
public class Soccer implements Ball {

  @Override
  public String create() {
    return "give you a soccer";
  }

}
public class EasyBallFactory {

  public static Ball createBall(String name) {
    if ("basketball".equals(name)) {
      return new Basketball();
    } else if ("soccer".equals(name)) {
      return new Soccer();
    } else {
      return null;
    }
  }

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

}

2. 工厂模式

这个其实和简单工厂模式差不太多,就是将工厂继续拆分,比如说刚刚EasyBallFactory是一个总工厂,我们现在拆分成BasketballFactory和SoccerFactory分别生产篮球和足球。某个工厂内部可以根据需求生产不同的产品,比如说SoccerFactory可以生产不同大小的出来。

public interface BallFactory {

  public Ball create();

}
public class BasketballFactory implements BallFactory {

  @Override
  public Ball create() {
    // do something
    return null;
  }

}
public class SoccerFactory 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 {

  public static Session session;

  public static Session getSession() {
    if (null == session) {
      synchronized (ShiroUtils.class) {
        if (null == session) {
          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 AppleBuilder 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("HelloWorld", 18);
    User user2 = user1.clone();
    user1.setAge(25);
    System.out.println(user2.getAge());  // 18
  }

}

三、结构型模式

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

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、Basketball、Soccer接着用,增加新的类。

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 Basketball());
    ballCut.create();  // give you a basketball
    ballCut.setBall(new Soccer());
    ballCut.create();  // give you a soccer
  }

}

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 student1 = new Student("张三");
    Student student2 = new Student("李四");
    teacher.addStudent(student1);
    teacher.addStudent(student2);
    teacher.doNotify();  //张三交卷了 ... 李四交卷了 ...
  }

}

16. 责任链模式

责任链模式为请求创建一个接收者对象的链,对发送者和接受者进行解耦合。filter链就是责任链模式。

public 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();  // Mysql open ... Mysql select ... Mysql close ...
  }

}

18.状态模式

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

public interface State {

  public void doAction(Context context);

}
public class StartState implements State {

  @Override
  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 {

  @Override
  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 State getState() {
    return state;
  }

  public void setState(State state) {
    this.state = state;
  }

}
public class StatePatternDemo {

  public static void main(String[] args) {
    Context context = new Context();
    StartState startState = new StartState();
    startState.doAction(context);  // Player is in start state
    System.out.println(context.getState().toString());  // Start State
    StopState stopState = new StopState();
    stopState.doAction(context);  // Player is in stop state
    System.out.println(context.getState().toString());  // Stop State
  }

}

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 ConcreteCommand extends Command {

  // 干活那个
  private Receiver receiver;

  public ConcreteCommand(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 ConcreteCommand(receiver);
    command.exectue();  // Receiver干活
  }

}

21. 备忘录模式

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

// 备忘录
public class Memento {

  private String state;

  public Memento(String state){
    this.state = state;
  }

  public String getState() {
    return state;
  }

}
// 某对象
public class Originator {

  private String state;

  public Memento saveStateToMemnto() {
    return new Memento(state);
  }

  public void getStateFromMemento(Memento memento) {
    state = memento.getState();
  }

  public String getState() {
    return state;
  }

  public void setState(String state) {
    this.state = state;
  }

}
// 备忘录管理类
public class CareTaker {

  private List<Memento> mementoList = new ArrayList<>();

  public void add(Memento memento) {
    mementoList.add(memento);
  }

  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.saveStateToMemnto());
    originator.setState("State #3");
    careTaker.add(originator.saveStateToMemnto());
    originator.setState("State #4");
    System.out.println("Current State: " + originator.getState());      // Current State: State #4
    originator.getStateFromMemento(careTaker.get(0));
    System.out.println("First saved State: " + originator.getState());  // First saved State: State #2
    originator.getStateFromMemento(careTaker.get(1));
    System.out.println("Second saved State: " + originator.getState()); // Second saved State: State #3
  }

}

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

}
public class User {

  private String name;

  public User(String name) {
    this.name = name;
  }

  public String getName() {
    return 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("Hello! John!");  // Wed Aug 26 19:19:41 CST 2020 [Robert] : Hello! John!
    john.sendMessage("Hello! Robert!");  // Wed Aug 26 19:19:41 CST 2020 [John] : Hello! Robert!
  }

}

24. 解释器模式

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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值