一止长渊

设计模式

N 人看过
字数:7.4k字 | 预计阅读时长:33分钟

设计模式的目的是为了降低代码的耦合性,同时也保证代码可维护性和扩展性,一共有 23 种设计模式。
分为以下三类:

  • 创建型设计模式

注重于隐藏对象的创建逻辑,使用时无需 new 对象,直接获取对象

  • 结构性模型

注重已有对象之间的相互组合

  • 行为型模式

注重对象之间相互调用的行为,关注对象之间的通信

1.工厂模式

应用场景:如某一个系列有众多产品,但是属于同系,客户端只会用到这些同系产品的某一个,就可以使用工厂模式。
方式:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
实现:同系产品都实现同一个接口(这里接口定义了行为),工厂类根据名称创建该接口的实例对象。
image.png

2.抽象工厂模式

抽象工厂模式简单而言就是工厂的工厂,我们通过一个超级工厂来创建其他的工厂,这里的抽象字面含义指的是工厂类是继承于抽象工厂类。
应用场景:如我们有多个系列,每个系列下有同族的众多产品,客户端只会用到某个系列中的某一个产品,这里就可以使用抽象工厂模式。这里每个系列下          的产品都实现该系列下定义的接口(接口提供该系列的行为模式),这里的系列就对应于工厂,工厂都继承自一个抽象工厂类(抽象工厂类定义了类模              板),用户通过超级工厂,选择某个系列就是选择某个工厂,然后通过该工厂来选择对象(该系列下的产品)。
image.png

3.单例模式

将构造函数私有化,然后暴露一个接口来返回创建好的单例对象,可以避免频繁地创建和销毁对象的开销
image.png
按照单例对象创建时间可分为:

  • 饿汉模式(类加载期间就创建好对象了,这是线程安全的)
public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton (){}
    public static Singleton getInstance() {
    return instance;
    }
}
  • 懒汉模式:只在用户第一次调用获取对象的方法时,才创建实例对象
    • 线程安全(创建实例的方法有 Sychronized 修饰)
public class Singleton {
    private static Singleton instance;
    private Singleton (){}

    public static Sychronized Singleton getInstance() {
    if (instance == null) {
        instance = new Singleton();
    }
    return instance;
    }
}
  - 线程不安全
public class Singleton {
    private static Singleton instance;
    private Singleton (){}

    public static Singleton getInstance() {
    if (instance == null) {
        instance = new Singleton();
    }
    return instance;
    }
}
  • 双重锁(成员变量有 vloatile 修饰,防止指令重排)
public class Singleton {
    private volatile static Singleton singleton;
    private Singleton (){}
    public static Singleton getSingleton() {
    if (singleton == null) {
        synchronized (Singleton.class) {
        if (singleton == null) {
            singleton = new Singleton();
        }
        }
    }
    return singleton;
    }
}
  • 登记类

与饿汉模式不同,这里成员变量初始化是在外部调用时才初始化,这是利用将初始化成员变量放置在内部类之中,这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,只有通过显式调用 getInstance 方法时,才会显式装载内部类 SingletonHolder 类

public class Singleton {
    private static class SingletonHolder {
    private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton (){}
    public static final Singleton getInstance() {
    return SingletonHolder.INSTANCE;
    }
}

4.建造者模式

从字面上看,一个复杂的对象是一步一步通过简单的对象建立组合起来的,一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。还是拿产品系列来说,两个系列的产品,要从每个系列中挑出一个产品进行搭配组合成套餐销售,这时候就可以利用建造者模式,将最后组合的套餐当做复杂的对象,这个套餐可以有两个系列中的产品自由组合。因为都是产品所以可以抽离出一个公共接口 Item,然后两个系列去实现 Item 接口,系列之下的产品同宗同源,但是有微小的差异,那么可以通过继承系列的类覆写实现微小差异,然后最后通过 Builder 自由组合这些产品。
我们假设一个快餐店的商业案例,其中,一个典型的套餐可以是一个汉堡(Burger)和一杯冷饮(Cold drink)。汉堡(Burger)可以是素食汉堡(Veg Burger)或鸡肉汉堡(Chicken Burger),它们是包在纸盒中。冷饮(Cold drink)可以是可口可乐(coke)或百事可乐(pepsi),它们是装在瓶子中。我们将创建一个表示食物条目(比如汉堡和冷饮)的 Item 接口和实现 Item 接口的实体类,以及一个表示食物包装的 Packing 接口和实现 Packing 接口的实体类,汉堡是包在纸盒中,冷饮是装在瓶子中。然后我们创建一个 Meal 类,**带有 Item 的 ArrayList **和一个通过结合 Item 来创建不同类型的 Meal 对象的 MealBuilder。BuilderPatternDemo 类使用 MealBuilder 来创建一个 Meal。
image.png

5.原型模式

字面解释:就是以一个对象实例为原型,复制出一个新的对象
场景:创建一个对象需要消耗很多的资源,例如多次的 SQL 查询注入属性,我们就可以利用这种方式,可以避免创建对象时需要多次 SQL 查询,只要以第一次创建的对象为原型复制一个出
来就好了
如何使用:这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。
其中 Shape 抽象类需要实现 Cloneable 接口,用于后续的复制对象。

image.png
抽象类实现 Cloneable 接口

public abstract class Shape implements Cloneable {

   private String id;
   protected String type;

   abstract void draw();

   public String getType(){
      return type;
   }

   public String getId() {
      return id;
   }

   public void setId(String id) {
      this.id = id;
   }

   public Object clone() {
      Object clone = null;
      try {
         clone = super.clone();
      } catch (CloneNotSupportedException e) {
         e.printStackTrace();
      }
      return clone;
   }
}

扩展抽象类

public class Rectangle extends Shape {

   public Rectangle(){
     type = "Rectangle";
   }

   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}

public class Square extends Shape {

   public Square(){
     type = "Square";
   }

   @Override
   public void draw() {
      System.out.println("Inside Square::draw() method.");
   }
}

原型提前创建,放入缓存中,后续取出为缓存的复制,避免了创建对象的资源消耗

import java.util.Hashtable;

public class ShapeCache {

   private static Hashtable<String, Shape> shapeMap
      = new Hashtable<String, Shape>();

   public static Shape getShape(String shapeId) {
      Shape cachedShape = shapeMap.get(shapeId);
      return (Shape) cachedShape.clone(); // 返回的缓存中的复制体
   }

   // 对每种形状都运行数据库查询,并创建该形状
   // shapeMap.put(shapeKey, shape);
   // 例如,我们要添加三种形状
   public static void loadCache() {
      Circle circle = new Circle();
      circle.setId("1");
      shapeMap.put(circle.getId(),circle);

      Square square = new Square();
      square.setId("2");
      shapeMap.put(square.getId(),square);

      Rectangle rectangle = new Rectangle();
      rectangle.setId("3");
      shapeMap.put(rectangle.getId(),rectangle);
   }
}

6.适配器模式

将现有的一个接口添加一些功能来应对用户新增加的需求,在原有的接口功能仍然可以使用的情况下,增加扩展性。
优点:适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
缺点:过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现。
例如现有一个类 AudioPlayer 可以播放 mp3 格式歌曲,但用户希望 AudioPlayer 在现有基础上还可以播放 VLC 和 Mp4 格式歌曲,那么可以对 AudioPlayer 内部播放 Mp4 歌曲,实质上是调用的成员变量 MediaAdpter 对象中的 AdvancedMeidPlayer 进行播放相应格式歌曲。
image.png

MediaAdapter 实现 MediaPlayer 接口是为了保证和 AudioPlayer 同样的 Play 接口。

public class MediaAdapter implements MediaPlayer {

   AdvancedMediaPlayer advancedMusicPlayer;

   public MediaAdapter(String audioType){
      if(audioType.equalsIgnoreCase("vlc") ){
         advancedMusicPlayer = new VlcPlayer();
      } else if (audioType.equalsIgnoreCase("mp4")){
         advancedMusicPlayer = new Mp4Player();
      }
   }

   @Override
   public void play(String audioType, String fileName) {
      if(audioType.equalsIgnoreCase("vlc")){
         advancedMusicPlayer.playVlc(fileName);
      }else if(audioType.equalsIgnoreCase("mp4")){
         advancedMusicPlayer.playMp4(fileName);
      }
   }
}

AudioPlayer 进行扩展

public class AudioPlayer implements MediaPlayer {
   MediaAdapter mediaAdapter;

   @Override
   public void play(String audioType, String fileName) {

      //播放 mp3 音乐文件的内置支持
      if(audioType.equalsIgnoreCase("mp3")){
         System.out.println("Playing mp3 file. Name: "+ fileName);
      }
      //mediaAdapter 提供了播放其他文件格式的支持
      else if(audioType.equalsIgnoreCase("vlc")
         || audioType.equalsIgnoreCase("mp4")){
         mediaAdapter = new MediaAdapter(audioType);
         mediaAdapter.play(audioType, fileName);
      }
      else{
         System.out.println("Invalid media. "+
            audioType + " format not supported");
      }
   }
}

7.桥接模式

桥接模式就是将抽象化和实现化解耦,简单意义上讲就是在底层 API 和用户使用之间加了一层,这一层的目的就是为了解耦,便于用户选择底层那个 API 来使用
这里的 Shape 就是中间层,这里的 Shape 是抽象类,将底层的 DrawAPI 和用户之间使用 Circle 解耦开,使得用户可以指定使用具体 DrawAPI 来进行使用,而不需改变原有的使用习惯。
image.png
底层 API 统一接口

public interface DrawAPI {
   public void drawCircle(int radius, int x, int y);
}

中间层将底层 API 和用户使用隔离开,将使用和底层实现隔离开

public abstract class Shape {
   protected DrawAPI drawAPI;
   protected Shape(DrawAPI drawAPI){
      this.drawAPI = drawAPI;
   }
   public abstract void draw();
}

用户使用

public class Circle extends Shape {
   private int x, y, radius;

   public Circle(int x, int y, int radius, DrawAPI drawAPI) {
      super(drawAPI);
      this.x = x;
      this.y = y;
      this.radius = radius;
   }

   public void draw() {
      drawAPI.drawCircle(radius,x,y);
   }
}

8.过滤器模式

这种模式允许开发人员使用不同的标准来过滤一组对象,由于是不同的标准,可以抽象处一套标准的接口,接口主要目的就是过滤对象列表返回过滤后的对象。
还是拿产品说事,我们有一个系列,系列下有很多的产品,我们需要根据用户年龄段、用户性别等不同特征来过滤出适合用户的产品列表,这时候就可以使用过滤器模式,首先抽象出一个标准接口 Criteria,然后分别根据年龄段特征、用户性别特征分别实现一个 Criteria 接口,实现的标准就是过滤符合该特征的产品列表,最后我们可以根据用户的特征组合来搭配不同标准来过滤出符合用户的产品列表。

  • Criteria 接口
    • AgeCriteria 类:年龄段过滤标准
    • GenderCriteria 类:性别过滤标准
  • 推荐类 Recomend(内部就是用户年龄段和性别来使用相应标准来过滤产品)
    • public Recomend(int age, String gender){内部就是根据 age 和 gender 选择相应过滤接口}
    • List filterByPersonFeature(List)

image.png

9.组合模式

组合模式根据树形结构的方式来构造对象,简单而言:就是一个对象内部有一个成员变量是自己对象组的列表,也就是相当于 children 的关系,该类提供了修改相同对象组的方式。
例如:一个雇员 A,手下也有很多雇员来管理,就形成了一个金字塔形的结构,雇员 A 提供了修改其雇员列表的接口,便于人员调动离职。
image.png

10.装饰器模式

装饰:从字面上看就是在原有底部部件不变化的情况下,加上一些功能。专业点就是向一个现有的对象增加一些功能而不改变其代码结构。
我们可以联想到子类继承的方式来实现这样的功能,该种模式是运用在不想增加很多子类的情况下还想扩展类的功能。
例如:我们只有绘制形状的对象 Circle 和 Rrctangle,我们想增加一项功能那就是可以绘制某种颜色的对象而不采用子类继承覆写的方式,就可以使用装饰器模式。这里的 ShapeDecorator 之所以去实现 Shape 接口,就是同一定义了 draw 行为模式,然后在 draw 中添加上自己的功能。
image.png

public abstract class ShapeDecorator implements Shape {
   protected Shape decoratedShape;

   public ShapeDecorator(Shape decoratedShape){
      this.decoratedShape = decoratedShape;
   }

   public void draw(){
      decoratedShape.draw();
   }
}

public class RedShapeDecorator extends ShapeDecorator {

   public RedShapeDecorator(Shape decoratedShape) {
      super(decoratedShape);
   }

   @Override
   public void draw() {
      decoratedShape.draw();
      setRedBorder(decoratedShape);
   }

   private void setRedBorder(Shape decoratedShape){
      System.out.println("Border Color: Red");
   }
}

public class DecoratorPatternDemo {
   public static void main(String[] args) {

      Shape circle = new Circle();
      ShapeDecorator redCircle = new RedShapeDecorator(new Circle());
      ShapeDecorator redRectangle = new RedShapeDecorator(new Rectangle());
      //Shape redCircle = new RedShapeDecorator(new Circle());
      //Shape redRectangle = new RedShapeDecorator(new Rectangle());
      System.out.println("Circle with normal border");
      circle.draw();

      System.out.println("\nCircle of red border");
      redCircle.draw();

      System.out.println("\nRectangle of red border");
      redRectangle.draw();
   }
}

11.外观模式

外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。例如:系统有很多功能,每个功能由一个类来实现,通过将各功能统一放置在一个接口中,然后让客户端调用这个接口,隐藏了各个功能的复杂性。
image.png

12.享元模式

主要目的:重用现有的对象,避免频繁的创建和销毁类,核心就是利用工厂类中成员变量 HashMap 来存储创建过的对象,下次请求先去 HashMap 查找,如果没有则创建放入到 HashMap 中然后返回。
image.png

import java.util.HashMap;

public class ShapeFactory {
   private static final HashMap<String, Shape> circleMap = new HashMap<>();

   public static Shape getCircle(String color) {
      Circle circle = (Circle)circleMap.get(color);

      if(circle == null) {
         circle = new Circle(color);
         circleMap.put(color, circle);
         System.out.println("Creating circle of color : " + color);
      }
      return circle;
   }
}

13.代理模式

代理模式就是有点类似 AOP 切向中的代理,利用接口实现返回与现有对象相同接口的对象,只不过这个对象织入了增强的逻辑。
核心:为其他对象提供一种代理以控制对这个对象的访问,主要为了隐藏创建对象的复杂性,例如需要大量的属性注入,这里就是可以利用代理模式来隐藏创建对象的复杂性,使得用户可以比较轻易的使用。
image.png

public class ProxyImage implements Image{

   private RealImage realImage;
   private String fileName;

   public ProxyImage(String fileName){
      this.fileName = fileName;
   }

   @Override
   public void display() {
      if(realImage == null){
         realImage = new RealImage(fileName);
      }
      realImage.display();
   }
}

14.责任链模式

责任链模式就是一条链上各司其职,对于传入的请求一次经过链上的每一环进行处理,每一个环处理完毕该环应该做的事情后,显示调用下一环来处理该请求,这样一环套一环就形成了一个责任链。
核心:每一个环中提供一个 set 方法来设置下一环,然后在 logMessage 先处理自己的事情后,再显示调用下一环处理。
场景:例如日志中,ERROR 级别的日志在完成向终端输入 ERROR 错误信息后,还会保存到文件中,这里就是 ErrorLogger -> FileLogger
image.png

public abstract class AbstractLogger {
   public static int INFO = 1;
   public static int DEBUG = 2;
   public static int ERROR = 3;

   protected int level;

   //责任链中的下一个元素
   protected AbstractLogger nextLogger;

   public void setNextLogger(AbstractLogger nextLogger){
      this.nextLogger = nextLogger;
   }

   public void logMessage(int level, String message){
      if(this.level <= level){
         write(message);
      }
      if(nextLogger !=null){
         nextLogger.logMessage(level, message);
      }
   }

   abstract protected void write(String message);

}

public class ConsoleLogger extends AbstractLogger {

   public ConsoleLogger(int level){
      this.level = level;
   }

   @Override
   protected void write(String message) {
      System.out.println("Standard Console::Logger: " + message);
   }
}

public class ErrorLogger extends AbstractLogger {

   public ErrorLogger(int level){
      this.level = level;
   }

   @Override
   protected void write(String message) {
      System.out.println("Error Console::Logger: " + message);
   }
}

public class FileLogger extends AbstractLogger {

   public FileLogger(int level){
      this.level = level;
   }

   @Override
   protected void write(String message) {
      System.out.println("File::Logger: " + message);
   }
}

public class FileLogger extends AbstractLogger {

   public FileLogger(int level){
      this.level = level;
   }

   @Override
   protected void write(String message) {
      System.out.println("File::Logger: " + message);
   }
}

15.命令模式

命令模式就是将命令包裹在对象中,然后传递给实现命令者,这过程就涉及到三者:命令发出者(请求)、命令、命令执行者。
image.png

// 请求
public class Stock {

   private String name = "ABC";
   private int quantity = 10;

   public void buy(){
      System.out.println("Stock [ Name: "+name+",
         Quantity: " + quantity +" ] bought");
   }
   public void sell(){
      System.out.println("Stock [ Name: "+name+",
         Quantity: " + quantity +" ] sold");
   }
}

// 命令1
public class BuyStock implements Order {
   private Stock abcStock;

   public BuyStock(Stock abcStock){
      this.abcStock = abcStock;
   }

   public void execute() {
      abcStock.buy();
   }
}

// 命令2
public class SellStock implements Order {
   private Stock abcStock;

   public SellStock(Stock abcStock){
      this.abcStock = abcStock;
   }

   public void execute() {
      abcStock.sell();
   }
}

// 命令执行者
import java.util.ArrayList;
import java.util.List;

public class Broker {
   private List<Order> orderList = new ArrayList<Order>();

   public void takeOrder(Order order){
      orderList.add(order);
   }

   public void placeOrders(){
      for (Order order : orderList) {
         order.execute();
      }
      orderList.clear();
   }
}

// 测试
public class CommandPatternDemo {
   public static void main(String[] args) {
      Stock abcStock = new Stock();

      BuyStock buyStockOrder = new BuyStock(abcStock);
      SellStock sellStockOrder = new SellStock(abcStock);

      Broker broker = new Broker();
      broker.takeOrder(buyStockOrder);
      broker.takeOrder(sellStockOrder);

      broker.placeOrders();
   }
}

16.解释器模式

通常用在评估语言,因为语言的判断有许多且或的判断,那么就可以利用解释器模式通过将判断进行且或的组合从而判断语言的正确性。
image.png

// 原子判断
public class TerminalExpression implements Expression {

   private String data;

   public TerminalExpression(String data){
      this.data = data;
   }

   @Override
   public boolean interpret(String context) {
      if(context.contains(data)){
         return true;
      }
      return false;
   }
}

// 与判断
public class AndExpression implements Expression {

   private Expression expr1 = null;
   private Expression expr2 = null;

   public AndExpression(Expression expr1, Expression expr2) {
      this.expr1 = expr1;
      this.expr2 = expr2;
   }

   @Override
   public boolean interpret(String context) {
      return expr1.interpret(context) && expr2.interpret(context);
   }
}

// 或判断
public class OrExpression implements Expression {

   private Expression expr1 = null;
   private Expression expr2 = null;

   public OrExpression(Expression expr1, Expression expr2) {
      this.expr1 = expr1;
      this.expr2 = expr2;
   }

   @Override
   public boolean interpret(String context) {
      return expr1.interpret(context) || expr2.interpret(context);
   }
}

//
public class InterpreterPatternDemo {

   //规则:Robert 和 John 是男性
   public static Expression getMaleExpression(){
      Expression robert = new TerminalExpression("Robert");
      Expression john = new TerminalExpression("John");
      return new OrExpression(robert, john);
   }

   //规则:Julie 是一个已婚的女性
   public static Expression getMarriedWomanExpression(){
      Expression julie = new TerminalExpression("Julie");
      Expression married = new TerminalExpression("Married");
      return new AndExpression(julie, married);
   }

   public static void main(String[] args) {
      Expression isMale = getMaleExpression();
      Expression isMarriedWoman = getMarriedWomanExpression();

      System.out.println("John is male? " + isMale.interpret("John"));
      System.out.println("Julie is a married women? "
      + isMarriedWoman.interpret("Married Julie"));
   }
}

17.迭代器模式

通过集合类内部封装一个实现了 Iterator 接口的类,通过该内部类来迭代访问集合内部的数据,用户而不需要去关心集合内部遍历的细节,Iterator 对外提供了一个统一的遍历集合的行为模式。
image.png

public interface Iterator {
   public boolean hasNext();
   public Object next();
}

public interface Container {
   public Iterator getIterator();
}

public class NameRepository implements Container {
   public String[] names = {"Robert" , "John" ,"Julie" , "Lora"};

   @Override
   public Iterator getIterator() {
      return new NameIterator();
   }

   private class NameIterator implements Iterator {

      int index;

      @Override
      public boolean hasNext() {
         if(index < names.length){
            return true;
         }
         return false;
      }

      @Override
      public Object next() {
         if(this.hasNext()){
            return names[index++];
         }
         return null;
      }
   }
}
public class NameRepository implements Container {
   public String[] names = {"Robert" , "John" ,"Julie" , "Lora"};

   @Override
   public Iterator getIterator() {
      return new NameIterator(); // 这里getIterator始终返回的是一个新的对象,所以index总是初始化为默认值0
   }

   private class NameIterator implements Iterator {

      int index;

      @Override
      public boolean hasNext() {
         if(index < names.length){
            return true;
         }
         return false;
      }

      @Override
      public Object next() {
         if(this.hasNext()){
            return names[index++];
         }
         return null;
      }
   }
}

18.中介者模式

用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互,该类通常处理不同类之间的通信。
也就是每个对象在调用自己的功能时,其实是在调用中介对象提供的功能。
image.png

import java.util.Date;

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 String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

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

   public void sendMessage(String message){
      ChatRoom.showMessage(this,message); // 对象发出消息其实是在利用中介ChatRoom提供的功能
   }
}

public class MediatorPatternDemo {
   public static void main(String[] args) {
      User robert = new User("Robert");
      User john = new User("John");

      robert.sendMessage("Hi! John!");
      john.sendMessage("Hello! Robert!");
   }
}

19.备忘录模式

备忘录模式就是可以保存对象状态(成员变量)到另一个封装的对象中,然后该对象可以通过保存的存档来恢复自身的成员变量到旧值。
故对象内部需要提供一个保存成员变量到另一个对象的方法以及恢复成员变量到指定的存档。
这里的 Memento 就是存档,用来保存 Originator 的成员变量,Originator 类提供一个主动保存存档的方法 saveStateToMemento,以及恢复状态到指定存档 getStateFromMemento,存档保存在 CareTaker 中
image.png

public class Originator {
   private String state;

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

   public String getState(){
      return state;
   }

   // 保存存档,返回是一个新建的存档对象
   public Memento saveStateToMemento(){
      return new Memento(state);
   }

   // 将成员变量恢复至指定的存档
   public void getStateFromMemento(Memento Memento){
      state = Memento.getState();
   }
}

// 存档,拥有和对象Originator一样的成员变量
public class Memento {
   private String state;

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

   public String getState(){
      return state;
   }
}

// 存档保存位置
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 class MementoPatternDemo {
   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());
   }
}

20.观察者模式

当被观察的对象成员变量发生变化时,将会通知其所有的观察者了,为此被观察的对象需要有个成员变量是列表用来存放其观察者们,此外观察者需要获取到被观察对象的成员变量,为此观察者需要添加被观察的对象为成员变量,此外添加被观察的对象为成员变量的同时,还将观察者加入到被观察对象的列表中。
缺点:如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
image.png

// 被观察的对象有一个成员变量列表来存放观察者,在setState即成员变量发生变化的时候,通知所有的观察者
public class Subject {

   private List<Observer> observers
      = new ArrayList<Observer>();
   private int state;

   public int getState() {
      return state;
   }

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

   public void attach(Observer observer){
      observers.add(observer);
   }

   public void notifyAllObservers(){
      for (Observer observer : observers) {
         observer.update();
      }
   }
}

// 观察者的公共抽象类
public abstract class Observer {
   protected Subject subject;
   public abstract void update();
}

// 观察者为了可以获取到被观察者的成员变量,这里需要将被观察者塞入到观察者,当做其成员变量的一部分
public class BinaryObserver extends Observer{

   public BinaryObserver(Subject subject){
      this.subject = subject;
      this.subject.attach(this);
   }

   @Override
   public void update() {
      System.out.println( "Binary String: "
      + Integer.toBinaryString( subject.getState() ) );
   }
}

public class OctalObserver extends Observer{

   public OctalObserver(Subject subject){
      this.subject = subject;
      this.subject.attach(this);
   }

   @Override
   public void update() {
     System.out.println( "Octal String: "
     + Integer.toOctalString( subject.getState() ) );
   }
}

public class ObserverPatternDemo {
   public static void main(String[] args) {
      Subject subject = new Subject();

      new HexaObserver(subject);
      new OctalObserver(subject);
      new BinaryObserver(subject);

      System.out.println("First state change: 15");
      subject.setState(15);
      System.out.println("Second state change: 10");
      subject.setState(10);
   }
}

21.状态模式

一个实例对象的行为根据它所处的状态而发生改变,在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
我们将创建一个 State 接口和实现了 State 接口的实体状态类。Context 是一个带有某个状态的类,一般一个对象处于什么状态,是通过 State 中的 doAction 将对象填入,然后对象中还有一个 state 表示当前状态,对象作出的行为实际上是通过 State 中的 doAction 来设置。
image.png

22.空对象模式

就是根据用户的查询条件来给予产品列表中的某一项,当产品列表中没有符合用户查询条件的,就返回一个空对象提供一个默认的行为。
image.png

public abstract class AbstractCustomer {
   protected String name;
   public abstract boolean isNil();
   public abstract String getName();
}

public class RealCustomer extends AbstractCustomer {

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

   @Override
   public String getName() {
      return name;
   }

   @Override
   public boolean isNil() {
      return false;
   }
}

// 空对象,提供默认的行为
public class NullCustomer extends AbstractCustomer {

   @Override
   public String getName() {
      return "Not Available in Customer Database";
   }

   @Override
   public boolean isNil() {
      return true;
   }
}

// 根据用户查询条件,提供产品
public class CustomerFactory {

   public static final String[] names = {"Rob", "Joe", "Julie"}; // 产品列表

   public static AbstractCustomer getCustomer(String name){
      for (int i = 0; i < names.length; i++) {
         if (names[i].equalsIgnoreCase(name)){
            return new RealCustomer(name);
         }
      }
      return new NullCustomer(); // 查不到符合条件的产品则返回一个默认为空的产品
   }
}

public class NullPatternDemo {
   public static void main(String[] args) {

      AbstractCustomer customer1 = CustomerFactory.getCustomer("Rob");
      AbstractCustomer customer2 = CustomerFactory.getCustomer("Bob");
      AbstractCustomer customer3 = CustomerFactory.getCustomer("Julie");
      AbstractCustomer customer4 = CustomerFactory.getCustomer("Laura");

      System.out.println("Customers");
      System.out.println(customer1.getName());
      System.out.println(customer2.getName());
      System.out.println(customer3.getName());
      System.out.println(customer4.getName());
   }
}

23.策略模式

在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。也就是说类的行为根据其采用的策略而发生改变
代码实现:因为有很多策略,为此我们抽象处一个策略接口定义策略的行为,类的行为中有一个成员变量是策略接口的实现类,其行为实际是调用策略的行为来实现。
image.png

// 策略接口
public interface Strategy {
   // 策略的执行方式
   public int doOperation(int num1, int num2);
}

// 策略1
public class OperationAdd implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 + num2;
   }
}

// 策略2
public class OperationSubtract 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 OperationSubtract());
      System.out.println("10 - 5 = " + context.executeStrategy(10, 5));

      context = new Context(new OperationMultiply());
      System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
   }
}

24.模板模式

就是一个抽象类已经将行为执行步骤确定下来,你需要做的就是实现该抽象类中这每一个步骤,然后就可以调用行为实现逻辑了。
image.png

public abstract class Game {
   abstract void initialize();
   abstract void startPlay();
   abstract void endPlay();

   //模板,其行为执行步骤已经确定下来,final就不能被子类覆写该执行步骤
   public final void play(){

      //初始化游戏
      initialize();

      //开始游戏
      startPlay();

      //结束游戏
      endPlay();
   }
}

// 子类只需实现相应的每个步骤的方法,方法之间调用顺序以及逻辑由抽象类内部已经确定
public class Cricket extends Game {

   @Override
   void endPlay() {
      System.out.println("Cricket Game Finished!");
   }

   @Override
   void initialize() {
      System.out.println("Cricket Game Initialized! Start playing.");
   }

   @Override
   void startPlay() {
      System.out.println("Cricket Game Started. Enjoy the game!");
   }
}

public class Cricket extends Game {

   @Override
   void endPlay() {
      System.out.println("Cricket Game Finished!");
   }

   @Override
   void initialize() {
      System.out.println("Cricket Game Initialized! Start playing.");
   }

   @Override
   void startPlay() {
      System.out.println("Cricket Game Started. Enjoy the game!");
   }
}

public class TemplatePatternDemo {
   public static void main(String[] args) {

      Game game = new Cricket();
      game.play();
      System.out.println();
      game = new Football();
      game.play();
   }
}

25.访问者模式

访问者模式就是可以在一个类中提供一个接口来放入访问者,其每一个组件都可以接受 accept 外来的访问者,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
image.png

public interface ComputerPart {
   public void accept(ComputerPartVisitor computerPartVisitor);
}

// 每个组件都可以接受访问者,访问者可以访问其内部状态
public class Monitor  implements ComputerPart {

   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this); //传递this,访问者就可以访问组件内部状态
   }
}

public class Keyboard  implements ComputerPart {

   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this);
   }
}

public class Keyboard  implements ComputerPart {

   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this);
   }
}

public interface ComputerPartVisitor {
   public void visit(Computer computer);
   public void visit(Mouse mouse);
   public void visit(Keyboard keyboard);
   public void visit(Monitor monitor);
}

public class ComputerPartDisplayVisitor implements ComputerPartVisitor {

   @Override
   public void visit(Computer computer) {
      System.out.println("Displaying Computer.");
   }

   @Override
   public void visit(Mouse mouse) {
      System.out.println("Displaying Mouse.");
   }

   @Override
   public void visit(Keyboard keyboard) {
      System.out.println("Displaying Keyboard.");
   }

   @Override
   public void visit(Monitor monitor) {
      System.out.println("Displaying Monitor.");
   }
}

public class VisitorPatternDemo {
   public static void main(String[] args) {

      ComputerPart computer = new Computer();
      computer.accept(new ComputerPartDisplayVisitor());
   }
}

本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 (CC BY-NC-ND 4.0) 进行许可。