• Java学习--设计模式之行为型模式(二)


    一、中介者模式(Mediator Pattern)

      1、概念

       中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。

      2、简介

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

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

       何时使用:多个类相互耦合,形成了网状结构。

       如何解决:将上述网状结构分离为星型结构。

       应用实例: MVC 框架,其中C(控制器)就是 M(模型)和 V(视图)的中介者。

       优点: 

        (1)、降低了类的复杂度,将一对多转化成了一对一。

        (2)、各个类之间的解耦。

        (3)、符合迪米特原则。

       缺点:中介者会庞大,变得复杂难以维护。

       使用场景: 

        (1)、系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。

        (2)、想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。

       注意事项:不应当在职责混乱的时候使用。

      3、实例

       我们通过聊天室实例来演示中介者模式。实例中,多个用户可以向聊天室发送消息,聊天室向所有的用户显示消息。我们将创建两个类 ChatRoom 和 User。User 对象使用 ChatRoom 方法来分享他们的消息。演示类 MediatorPatternDemo,我们的演示类使用 User 对象来显示他们之间的通信。

        

        (1)、创建中介类

    import java.util.Date;
    
    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!");
       }
    }

        (4)、演示结果

    1 Thu Jan 31 16:05:46 IST 2013 [Robert] : Hi! John!
    2 Thu Jan 31 16:05:46 IST 2013 [John] : Hello! Robert!

    二、备忘录模式(Memento Pattern)

      1、概念

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

      2、简介

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

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

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

       如何解决:通过一个备忘录类专门存储对象状态。

       关键代码:客户不与备忘录类耦合,与备忘录管理类耦合。

       应用实例:

        (1)、打游戏时的存档。

        (2)、Windows 里的 ctri + z。

        (3)、IE 中的后退。 4

        (4)、数据库的事务管理。

       优点: 

        (1)、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。

        (2)、实现了信息的封装,使得用户不需要关心状态的保存细节。

       缺点:消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。

       注意事项: 

        (1)、为了符合迪米特原则,还要增加一个管理备忘录的类。

        (2)、为了节约内存,可使用原型模式+备忘录模式。

      3、实例

       备忘录模式使用三个类 Memento、Originator 和 CareTaker。Memento 包含了要被恢复的对象的状态。Originator 创建并在 Memento 对象中存储状态。Caretaker 对象负责从 Memento 中恢复对象的状态。演示类 MementoPatternDemo,我们的演示类使用 CareTaker 和 Originator 对象来显示对象的状态恢复。

        

        (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 类

    import java.util.ArrayList;
    import java.util.List;
    
    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)、使用 CareTaker 和 Originator 对象。

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

        (5)、演示结果

    1 Current State: State #4
    2 First saved State: State #2
    3 Second saved State: State #3

    三、观察者模式(Observer Pattern)

      1、概念

       当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。

      2、简介

       意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

       主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

       如何解决:使用面向对象技术,可以将这种依赖关系弱化。

       关键代码:在抽象类里有一个 ArrayList 存放观察者们。

       应用实例: 拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。

       优点: 

        (1)、观察者和被观察者是抽象耦合的。

        (2)、建立一套触发机制。

       缺点: 

        (1)、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。

        (2)、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。

        (3)、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

       使用场景:

        (1)、一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。

        (2)、一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。

        (3)、一个对象必须通知其他对象,而并不知道这些对象是谁。

        (4)、需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。

       注意事项: 

        (1)、JAVA 中已经有了对观察者模式的支持类。

        (2)、避免循环引用。

        (3)、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。

      3、实例

       观察者模式使用三个类 Subject、Observer 和 Client。Subject 对象带有绑定观察者到 Client 对象和从 Client 对象解绑观察者的方法。我们创建 Subject 类、Observer 抽象类和扩展了抽象类 Observer 的实体类。演示类 ObserverPatternDemo,我们的演示类使用 Subject 和实体类对象来演示观察者模式。

        

        (1)、创建 Subject 类

    import java.util.ArrayList;
    import java.util.List;
    
    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);
       }
    }

        (5)、演示结果

    1 First state change: 15
    2 Hex String: F
    3 Octal String: 17
    4 Binary String: 1111
    5 Second state change: 10
    6 Hex String: A
    7 Octal String: 12
    8 Binary String: 1010

    四、状态模式(State Pattern)

      1、概念

       在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。

      2、简介

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

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

       何时使用:代码中包含大量与对象状态有关的条件语句。

       如何解决:将各种具体的状态类抽象出来。

       关键代码:通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。状态模式和命令模式一样,也可以用于消除 if...else 等条件选择语句。

       优点: 

        (1)、封装了转换规则。

        (2)、枚举可能的状态,在枚举状态之前需要确定状态种类。

        (3)、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。

        (4)、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。

        (5)、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

       缺点: 

        (1)、状态模式的使用必然会增加系统类和对象的个数。

        (2)、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。

        (3)、状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

       使用场景: 

        (1)、行为随状态改变而改变的场景。

        (2)、条件、分支语句的代替者。

       注意事项:在行为受状态约束的时候使用状态模式,而且状态不超过 5 个。

      3、实例

       我们将创建一个 State 接口和实现了 State 接口的实体状态类。Context 是一个带有某个状态的类。演示类 StatePatternDemo,我们的演示类使用 Context 和状态对象来演示 Context 在状态改变时的行为变化。

        

        (1)、创建一个接口

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

        (2)、创建实现接口的实体类

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

        (3)、创建 Context 类

    public class Context {
       private State state;
    
       public Context(){
          state = null;
       }
    
       public void setState(State state){
          this.state = state;        
       }
    
       public State getState(){
          return state;
       }
    }

        (4)、使用 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());
       }
    }

        (5)、演示结果

    1 Player is in start state
    2 Start State
    3 Player is in stop state
    4 Stop State

    PS:因作者能力有限,如有误还请谅解

  • 相关阅读:
    前端Ajax/JS/HTML+后端SpringMVC(二)
    前端Ajax/JS/HTML+后端SpringMVC(一)
    Redis 简介及应用
    项目中使用 MyBatis(二)
    L2d插件
    [转载] 栈内存和堆内存
    Hbase排错
    matplotlib中文乱码
    cocos2dx 一些好网站
    esclipe中接入SDK时引用另一个工程或Jar
  • 原文地址:https://www.cnblogs.com/WHL5/p/9204052.html
Copyright © 2020-2023  润新知