设计模式篇—《行为型设计模式》

1、策略模式

简介:

  • 策略模式(Strategy Pattern)中一个类的行为或其算法可以在运行时更改
  • 策略模式创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象,策略对象改变 context 对象的执行算法。
  • 举例:
    • 跟不同类型的MM约会,要用不同的策略,有的请电影比较好,有的则去吃小吃效果不错,有的去海边浪漫最合适,单目的都是为了得到MM的芳心,追MM锦囊中有好多Strategy哦。
    • 策略模式:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。
  • 意图:
    • 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
  • 主要解决:
    • 在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。
  • 何时使用:
    • 一个系统有许多许多类,而区分它们的只是他们直接的行为。
  • 如何解决:
    • 将这些算法封装成一个一个的类,任意地替换。
  • 关键代码:
    • 实现同一个接口。

适用场景:

  • 1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。
  • 2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。
  • 3、JAVA AWT 中的 LayoutManager。
  • 4、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
  • 5、一个系统需要动态地在几种算法中选择一种。
  • 6、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

结构:

image-20210415090833823

1、我们将创建一个定义活动的 Strategy 接口和实现了 Strategy 接口的实体策略类。

2、Context 是一个使用了某种策略的类。

3、StrategyPatternDemo,我们的演示类使用 Context 和策略对象来演示 Context 在它所配置或使用的策略改变时的行为变化。

代码实现:

1、创建一个接口和它的实现类

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 OperationSubtract implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 - num2;
   }
}

public class OperationMultiply implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 * num2;
   }
}

2、创建 Context 类。

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

3、使用 Context 来查看当它改变策略 Strategy 时的行为变化。

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));
   }
}
	策略模式定义了一些算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。
	需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该类接口。
	设计一个抽象类(可有可无,属于辅助类),提供辅助函数。
	策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。
	因此,策略模式多用在算法策略系统中,外部用户只需要决定用哪个算法即可。

2、观察者模式

简介:

  • 当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。
  • 举例:
    • 想知道咱们公司最新MM情报吗?加入公司的MM情报邮件组就行了,tom负责搜集情报,他发现的新情报不用一个一个通知我们,直接发布给邮件组,我们作为订阅者(观察者)就可以及时收到情报啦
    • 观察者模式:观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。
    • 这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
  • 意图:
    • 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
  • 主要解决:
    • 一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
  • 何时使用:
    • 一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
  • 如何解决:
    • 使用面向对象技术,可以将这种依赖关系弱化。
  • 关键代码:
    • 在抽象类里有一个 ArrayList 存放观察者们。

适用场景:

  • 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。
  • 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。
  • 3、一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用
  • 4、一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度
  • 5、一个对象必须通知其他对象,而并不知道这些对象是谁
  • 6、需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制

结构:

image-20210415200741801

  • 观察者模式使用三个类 Subject、Observer 和 Client。

  • Subject 对象带有绑定观察者到 Client 对象和从 Client 对象解绑观察者的方法。

  • 我们创建 Subject 类、Observer 抽象类和扩展了抽象类 Observer 的实体类。

  • ObserverPatternDemo,我们的演示类使用 Subject 和实体类对象来演示观察者模式。

代码实现:

1、创建 Subject 类。

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

2、创建 Observer 类。

public abstract class Observer {
   protected Subject subject;
   public abstract void update();
}

3、创建实体类观察者

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 HexaObserver extends Observer{
 
   public HexaObserver(Subject subject){
      this.subject = subject;
      this.subject.attach(this);
   }
 
   @Override
   public void update() {
      System.out.println( "Hex String: " 
      + Integer.toHexString( subject.getState() ).toUpperCase() ); 
   }
}

4、使用 Subject 和实体观察者对象。

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);
   }
}
观察者模式很好理解,类似于邮件订阅和RSS订阅,当我们浏览一些博客或wiki时,经常会看到RSS图标,就这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。
简单来说就是:
	当一个对象有变化时,其他依赖该对象的对象都会受到通知,并且随着变化,对象之间是一种一对多的关系。

3、模板方法模式

简介:

  • 在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。
  • 它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。
  • 模板方法模式:模板方法模式准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。
  • 意图:
    • 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
  • 主要解决:
    • 一些方法通用,却在每一个子类都重新写了这一方法。
  • 何时使用:
    • 有一些通用的方法。
  • 如何解决:
    • 将这些通用算法抽象出来。
  • 关键代码:
    • 在抽象类实现,其他步骤在子类实现。

适用场景:

  • 1、在造房子的时候,地基、走线、水管都一样,只有在建筑的后期才有加壁橱加栅栏等差异。
  • 2、西游记里面菩萨定好的 81 难,这就是一个顶层的逻辑骨架。
  • 3、spring 中对 Hibernate 的支持,将一些已经定好的方法封装起来,比如开启事务、获取 Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存。
  • 4、有多个子类共有的方法,且逻辑相同。
  • 5、重要的、复杂的方法,可以考虑作为模板方法。

结构:

image-20210415201934797

1、我们将创建一个定义操作的 Game 抽象类,其中,模板方法设置为 final,这样它就不会被重写。

2、CricketFootball 是扩展了 Game 的实体类,它们重写了抽象类的方法。

3、TemplatePatternDemo,我们的演示类使用 Game 来演示模板模式的用法。

代码实现:

1、创建一个抽象类,它的模板方法被设置为 final。

public abstract class Game {
   abstract void initialize();
   abstract void startPlay();
   abstract void endPlay();
 
   //模板
   public final void play(){
 
      //初始化游戏
      initialize();
 
      //开始游戏
      startPlay();
 
      //结束游戏
      endPlay();
   }
}

2、创建扩展了上述类的实体类。

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 Football extends Game {
 
   @Override
   void endPlay() {
      System.out.println("Football Game Finished!");
   }
 
   @Override
   void initialize() {
      System.out.println("Football Game Initialized! Start playing.");
   }
 
   @Override
   void startPlay() {
      System.out.println("Football Game Started. Enjoy the game!");
   }
}

3、使用 Game 的模板方法 play() 来演示游戏的定义方式。

public class TemplatePatternDemo {
   public static void main(String[] args) {
 
      Game game = new Cricket();
      game.play();
      System.out.println();
      game = new Football();
      game.play();      
   }
}

4、迭代器模式

简介:

  • 迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式。
  • 这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。
  • 举例:
    • 我爱上了Mary,不顾一切的向她求婚。 Mary:“想要我跟你结婚,得答应我的条件” 我:“什么条件我都答应,你说吧” Mary:“我看上了那个一克拉的钻石” 我:“我买,我买,还有吗?” Mary:“我看上了湖边的那栋别墅” 我:“我买,我买,还有吗?” Mary:“我看上那辆法拉利跑车” 我脑袋嗡的一声,坐在椅子上,一咬牙:“我买,我买,还有吗?” ……
    • 迭代子模式:迭代子模式可以顺序访问一个聚集中的元素而不必暴露聚集的内部表象。多个对象聚在一起形成的总体称之为聚集,聚集对象是能够包容一组对象的容器对象。迭代子模式将迭代逻辑封装到一个独立的子对象中,从而与聚集本身隔开。迭代子模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以上的迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色变化。
  • 意图:
    • 提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
  • 主要解决:
    • 不同的方式来遍历整个整合对象。
  • 何时使用:
    • 遍历一个聚合对象。
  • 如何解决:
    • 把在元素之间游走的责任交给迭代器,而不是聚合对象。
  • 关键代码:
    • 定义接口:hasNext, next。

适用场景:

  • 1、JAVA 中的 iterator。
  • 2、访问一个聚合对象的内容而无须暴露它的内部表示。
  • 3、需要为聚合对象提供多种遍历方式。
  • 4、为遍历不同的聚合结构提供一个统一的接口。

结构:

image-20210415202912281

1、我们将创建一个叙述导航方法的 Iterator 接口和一个返回迭代器的 Container 接口。

2、实现了 Container 接口的实体类将负责实现 Iterator 接口。

3、IteratorPatternDemo,我们的演示类使用实体类 NamesRepository 来打印 NamesRepository 中存储为集合的 Names

代码实现:

1、创建接口

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

public interface Container {
   public Iterator getIterator();
}

2、创建实现了 Container 接口的实体类。该类有实现了 Iterator 接口的内部类 NameIterator

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

3、使用 NameRepository 来获取迭代器,并打印名字。

public class IteratorPatternDemo {
   
   public static void main(String[] args) {
      NameRepository namesRepository = new NameRepository();
 
      for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){
         String name = (String)iter.next();
         System.out.println("Name : " + name);
      }  
   }
}

5、责任链模式

简介:

  • 顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。

  • 这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。

  • 在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

  • 举例:

    • 晚上去上英语课,为了好开溜坐到了最后一排,哇,前面坐了好几个漂亮的MM哎,找张纸条,写上“Hi,可以做我的女朋友吗?如果不愿意请向前传”,纸条就一个接一个的传上去了,糟糕,传到第一排的MM把纸条传给老师了,听说是个老处女呀,快跑!
    • 责任链模式:在责任链模式中,很多对象由每一个对象对其下家的引用而接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。
  • 意图:

    避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止

  • 主要解决:

    职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了

  • 何时使用:

    在处理消息的时候以过滤很多道

  • 如何解决:

    拦截的类都实现统一接口

  • 关键代码:

    Handler 里面聚合它自己,在 HanleRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去

适用场景:

  1. 红楼梦中的"击鼓传花"
  2. JS 中的事件冒泡
  3. JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter
  4. 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定
  5. 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求
  6. 可动态指定一组对象处理请求

结构:

image-20210424123312397

  1. 定义抽象类 AbstractLogger ,带有详细的日志记录级别

  2. 定义三种类型的记录器,都扩展了 AbstractLogger

    每个记录器消息的级别是否属于自己的级别,如果是则相应地打印出来,否则将不打印并把消息传给下一个记录器

代码实现:

1、创建抽象的记录器类

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

}

2、创建扩展了该记录器类的实体类

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

3、 创建不同类型的记录器

赋予它们不同的错误级别,并在每个记录器中设置下一个记录器

每个记录器中的下一个记录器代表的是链的一部分

public class ChainPatternDemo {

   private static AbstractLogger getChainOfLoggers(){

      AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
      AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
      AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);

      errorLogger.setNextLogger(fileLogger);
      fileLogger.setNextLogger(consoleLogger);

      return errorLogger;   
   }

   public static void main(String[] args) {
      AbstractLogger loggerChain = getChainOfLoggers();

      loggerChain.logMessage(AbstractLogger.INFO, 
         "This is an information.");

      loggerChain.logMessage(AbstractLogger.DEBUG, 
         "This is an debug level information.");

      loggerChain.logMessage(AbstractLogger.ERROR, 
         "This is an error information.");
   }
}

6、命令模式

简介:

  • 命令模式(Command Pattern)中请求以命令的形式包裹在对象中,并传给调用对象

  • 调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令

  • 举例:

    • 俺有一个MM家里管得特别严,没法见面,只好借助于她弟弟在我们俩之间传送信息,她对我有什么指示,就写一张纸条让她弟弟带给我。这不,她弟弟又传送过来一个COMMAND,为了感谢他,我请他吃了碗杂酱面,哪知道他说:“我同时给我姐姐三个男朋友送COMMAND,就数你最小气,才请我吃面。”
    • 命令模式:命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。系统支持命令的撤消。
  • 意图:

    将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化

  • 主要解决:

    在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适

  • 何时使用:

    在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合

  • 如何解决:

    通过调用者调用接受者执行命令,顺序:调用者→接受者→命令

  • 关键代码:

    定义三个角色:

    1. received 真正的命令执行对象
    2. Command
    3. invoker 使用命令对象的入口

适用场景:

  • struts 1 中的 action 核心控制器 ActionServlet 只有一个,相当于 Invoker,而模型层的类会随着不同的应用有不同的模型类,相当于具体的 Command
  • 几乎所有是命令的地方都可以使用命令模式
    • GUI 中每一个按钮都是一条命令
    • 模拟 CMD

结构:

image-20210424123934120

  1. 定义一个命令的接口 Order
  2. 定义作为请求的 Stock
  3. 定义实体命令类 BuyStockSellStock ,实现了 Order 接口,执行实际的命令处理
  4. 定义作为调用对象的类 Broker ,它接受订单并能下订单,Broker 对象使用命令模式,基于命令的类型确定哪个对象执行哪个命令
  5. 定义类 CommandPatternDemo 使用 Broker 类来演示命令模式

代码实现:

1、创建一个命令接口

public interface Order {
   void execute();
}

2、创建一个请求类

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

3、创建实现了 Order 接口的实体类

public class BuyStock implements Order {
   private Stock abcStock;

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

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

public class SellStock implements Order {
   private Stock abcStock;

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

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

4、创建命令调用类

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

5、使用 Broker 类来接受并执行命令

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

7、备忘录模式

简介:

  • 备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象

  • 举例:

    • 同时跟几个MM聊天时,一定要记清楚刚才跟MM说了些什么话,不然MM发现了会不高兴的哦,幸亏我有个备忘录,刚才与哪个MM说了什么话我都拷贝一份放到备忘录里面保存,这样可以随时察看以前的记录啦。
    • 备忘录模式:备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。
  • 意图:

    在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态

  • 主要解决:

    所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态

  • 何时使用:

    很多时候我们总是需要记录一个对象的内部状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先的状态,使得他有"后悔药"可吃

  • 如何解决:

    通过一个备忘录类专门存储对象状态

  • 关键代码:

    客户不与备忘录类耦合,与备忘录管理类耦合

适用场景:

  • 后悔药
  • 打游戏时的存档
  • Windows 里的 ctri + z
  • IE 中的后退
  • 数据库的事务管理
  • 需要保存/恢复数据的相关状态场景
  • 提供一个可回滚的操作

结构:

image-20210424124316300

备忘录模式使用三个类 MementoOriginatorCareTaker

  1. 定义类 Memento 包含了要被恢复的对象的状态
  2. 定义类 Originator 创建并在 Memento 对象中存储状态
  3. 定义类Caretaker 对象负责从 Memento 中恢复对象的状态
  4. 最后使用 MementoPatternDemo 类使用 CareTakerOriginator 对象来显示对象的状态恢复

代码实现:

1、创建 Memento 类

public class Memento {
   private String state;

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

   public String getState(){
      return state;
   }    
}

2、创建 Originator 类

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

3、创建 CareTaker 类

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

4、使用 CareTakerOriginator 对象

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

8、状态模式

简介:

  • 状态模式(State Pattern)中类的行为是基于它的状态改变的

  • 在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象

  • 举例:

    • 跟MM交往时,一定要注意她的状态哦,在不同的状态时她的行为会有不同,比如你约她今天晚上去看电影,对你没兴趣的MM就会说“有事情啦”,对你不讨厌但还没喜欢上的MM就会说“好啊,不过可以带上我同事么?”,已经喜欢上你的MM就会说“几点钟?看完电影再去泡吧怎么样?”,当然你看电影过程中表现良好的话,也可以把MM的状态从不讨厌不喜欢变成喜欢哦。
    • 状态模式:状态模式允许一个对象在其内部状态改变的时候改变行为。这个对象看上去象是改变了它的类一样。状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子类。
  • 意图:

    允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类

  • 主要解决:

    对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为

  • 何时使用:

    代码中包含大量与对象状态有关的条件语句

  • 如何解决:

    将各种具体的状态类抽象出来

  • 关键代码:

    通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法

    而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值

    也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法

    状态模式和命令模式一样,也可以用于消除 if…else 等条件选择语句

适用场景:

  • 打篮球的时候运动员可以有正常状态、不正常状态和超常状态
  • 曾侯乙编钟中,‘钟是抽象接口’,'钟A’等是具体状态,'曾侯乙编钟’是具体环境(Context)
  • 行为随状态改变而改变的场景
  • 条件、分支语句的代替者

结构:

image-20210424124654452

我们将创建一个 State 接口和实现了 State 接口的实体状态类。

Context 是一个带有某个状态的类

StatePatternDemo ,我们的演示类使用 Context 和状态对象来演示 Context 在状态改变时的行为变化

代码实现:

1、创建一个接口 StateInterface和它的实现类

public interface State {
   public void doAction(Context context);
}

public class StartStateImpl 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";
   }
}

2、创建 Context

public class Context {
   private State state;

   public Context(){
      state = null;
   }

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

   public State getState(){
      return state;
   }
}

3、使用 Context 来查看当状态 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());
   }
}

9、访问模式

简介:

  • 访问者模式(Visitor Pattern)使用了一个访问者类,它改变了元素类的执行算法,通过这种方式,元素的执行算法可以随着访问者改变而改变

  • 访问者模式中,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作

  • 举例:

    • 情人节到了,要给每个MM送一束鲜花和一张卡片,可是每个MM送的花都要针对她个人的特点,每张卡片也要根据个人的特点来挑,我一个人哪搞得清楚,还是找花店老板和礼品店老板做一下Visitor,让花店老板根据MM的特点选一束花,让礼品店老板也根据每个人特点选一张卡,这样就轻松多了;
    • 访问者模式:访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。访问者模式使得增加新的操作变的很容易,就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。当使用访问者模式时,要将尽可能多的对象浏览逻辑放在访问者类中,而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。
  • 意图:

    主要将数据结构与数据操作分离

  • 主要解决:

    稳定的数据结构和易变的操作耦合问题。

  • 何时使用:

    需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中

  • 如何解决:

    在被访问的类里面加一个对外提供接待访问者的接口

  • 关键代码:

    在数据基础类里面有一个方法接受访问者,将自身引用传入访问者

适用场景:

  • 您在朋友家做客,您是访问者,朋友接受您的访问,您通过朋友的描述,然后对朋友的描述做出一个判断,这就是访问者模式
  • 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作
  • 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作 "污染"这些对象的类,也不希望在增加新操作时修改这些类

结构:

image-20210424125147042

  1. 创建一个定义接受操作的 ComputerPart 接口
  2. 定义类 KeyboardMouseMonitorComputer 实现 ComputerPart 接口
  3. 定义另一个接口 ComputerPartVisitor ,它定义了访问者类的操作
  4. 定义类 Computer 使用实体访问者来执行相应的动作
  5. 定义类 VisitorPatternDemo 使用 ComputerComputerPartVisitor 类来演示访问者模式的用法

代码实现:

1、定义一个表示元素的接口并实现

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

public class Keyboard  implements ComputerPart {

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

public class Monitor  implements ComputerPart {

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

public class Mouse  implements ComputerPart {

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

public class Computer implements ComputerPart {

   ComputerPart[] parts;

   public Computer(){
      parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()};      
   } 


   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      for (int i = 0; i < parts.length; i++) {
         parts[i].accept(computerPartVisitor);
      }
      computerPartVisitor.visit(this);
   }
}

2、定义一个表示访问者的接口

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

3、创建实现了上述类的实体访问者

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

4、使用 ComputerPartDisplayVisitor 来显示 Computer 的组成部分

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

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

10、中介者模式

简介:

  • 中介者模式(Mediator Pattern)提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护

  • 中介者模式是用来降低多个对象和类之间的通信复杂性

  • 举例:

    • 四个MM打麻将,相互之间谁应该给谁多少钱算不清楚了,幸亏当时我在旁边,按照各自的筹码数算钱,赚了钱的从我这里拿,赔了钱的也付给我,一切就OK啦,俺得到了四个MM的电话。
    • 中介者模式:中介者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使他们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。调停者模式将对象的行为和协作抽象化,把对象在小尺度的行为上与其他对象的相互作用分开处理。
  • 意图:

    用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互

  • 主要解决:

    对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理

  • 何时使用:

    多个类相互耦合,形成了网状结构

  • 如何解决:

    将上述网状结构分离为星型结构

  • 关键代码:

    对象 Colleague 之间的通信封装到一个类中单独处理

适用场景:

  • 中国加入 WTO 之前是各个国家相互贸易,结构复杂,现在是各个国家通过 WTO 来互相贸易
  • 机场调度系统
  • MVC 框架,其中C(控制器)就是 M(模型)和 V(视图)的中介者
  • 系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象
  • 想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类

结构:

image-20210424125955492

我们通过聊天室实例来演示中介者模式:多个用户可以向聊天室发送消息,聊天室向所有的用户显示消息

  1. 定义中介类 ChatRoom
  2. 定义用户类 UserUser 对象使用 ChatRoom 方法来分享他们的消息
  3. 定义 MediatorPatternDemo 类使用 User 对象来显示他们之间的通信

代码实现:

1、创建中介类

public class ChatRoom {
   public static void showMessage(User user, String message){
      System.out.println(new Date().toString()
         + " [" + user.getName() +"] : " + message);
   }
}

2、创建 user 类

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

3、使用 User 对象来显示他们之间的通信

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

11、解释器模式

简介:

  • 解释器模式(Interpreter Pattern)实现了一个表达式接口,该接口解释一个特定的上下文

  • 这种模式被用在 SQL 解析、符号处理引擎等

  • 举例:

    • 俺有一个《泡MM真经》,上面有各种泡MM的攻略,比如说去吃西餐的步骤、去看电影的方法等等,跟MM约会时,只要做一个Interpreter,照着上面的脚本执行就可以了。
    • 解释器模式:给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解释器模式将描述怎样在有了一个简单的文法后,使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合。在解释器模式中需要定义一个代表文法的命令类的等级结构,也就是一系列的组合规则。每一个命令对象都有一个解释方法,代表对命令对象的解释。命令对象的等级结构中的对象的任何排列组合都是一个语言。
  • 意图:

    给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子

  • 主要解决:

    对于一些固定文法构建一个解释句子的解释器

  • 何时使用:

    如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题

  • 如何解决:

    构件语法树,定义终结符与非终结符

  • 关键代码:

    构件环境类,包含解释器之外的一些全局信息,一般是 HashMap

适用场景:

  • 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
  • 一些重复出现的问题可以用一种简单的语言来进行表达
  • 一个简单语法需要解释的场景

结构:

image-20210424130311458

  1. 定义一个接口 Expression 和实现了 Expression 接口的实体类
  2. 定义作为上下文中主要解释器的 TerminalExpression 类,其他的类 OrExpressionAndExpression 用于创建组合式表达式
  3. 定义类 InterpreterPatternDemo 使用 Expression 类创建规则和演示表达式的解析

代码实现:

1、创建一个表达式接口并实现

public interface Expression {
   public boolean interpret(String context);
}

2、创建实现了上述接口的实体类

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

3、InterpreterPatternDemo 使用 Expression 类来创建规则,并解析它们

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"));
   }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值