Design Pattern/设计模式: Introduction & Part VIII

Author Avatar
Cesare (MINGJU LI) Feb 06, 2020

设计模式以及综述

设计模式对于一名软件开发工程师来说可谓是必备的技能和知识,然而在程序员社区中也充斥着不少对于设计模式的反对之音。有人认为设计模式的知识对于工程开发至关重要,但也有许多经验丰富的程序员认为设计模式应当是长期的软件开发的积累所留下的不可名状的抽象艺术,对于市面上不少介绍设计模式的书籍嗤之以鼻。

对于刚入门的软件开发工程师而言,你若问他(包括现在的我自己)“我们为什么要使用工厂模式?使用工厂模式能够带来什么好处?”之类的问题,他也是很难回答的。所以盲目的掌握设计模式中那些拗口的名称,或者干巴巴的“方便继承”之类的描述是没有意义的。

这一系列的博客的目的并不是一蹴而就的讲解全部的设计模式知识,这样的知识没有日积月累的开发经验是很难去对其有深入的体会的。我只是希望能在这一系列的博客中,借助《图解设计模式》一书,跟随书中的顺序对其中的设计模式知识进行某种程度的理解和总结,以至于能够对诸君有所启迪,开发出更加优美和友好的代码。

我的能力有限,若是文中出现什么谬误也请诸君不吝赐教,提前感谢。

《图解设计模式》一书的作者是结城浩先生,全书共计十个部分,预计会分为十篇博客进行介绍。书中的代码为java所写,虽然java并不是我比较擅长的代码,但是为了不引起错误贻笑大方,所以还是按照书中的java进行讨论。本博客介绍其中的第八部分:管理状态。因为每一章节中代码都占据了比较多的部分,为了更好地理解相关概念,建议阅读代码优先从Main.java的部分开始。

Chapter 17: Observer模式–发送状态变化通知

在Observer模式(观察者模式)中,当被观察对象的状态发生变化时,会通知给观察者。

当对象间存在一对多的关系时,我们可以使用观察者模式,这样当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并且被自动更新。

// Subject.java: 被观察的对象
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();
        }
    }  
}
// Observer.java: 所有Observer的抽象类
public abstract class Observer {
   protected Subject subject;
   // 对于每一个Observer来说都有一个被观察的对象
   public abstract void update();
}
// BinaryObserver.java: Observer 1
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())); 
    }
}
// OctalObserver.java: Observer 2
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() ) ); 
    }
}
// HexaObserver.java: Observer 3
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() ); 
    }
}
public class Main {
   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);
   }
}

Chapter 18: Memento模式–保存对象状态

我们在使用各种软件的时候,最常见的快捷键之一就是Ctrl+Z,表示撤销的操作。在大部分的软件中这个操作都是如此的重要,在我们不小心犯下某些错误的时候使我们能够快速的恢复至之前的情况。使用面向对象编程的方式我们也可以实现类似的“撤销”的功能,我们可以事先保存实例的相关信息,然后在撤销时根据保存的信息将实例恢复至原来的状态。

而要能够这么做的话,我们需要一个能够自由访问实例内部结构的权限,但是这样会导致被妥善封装的类的封装性遭到破坏。而备忘录模式(Memento Pattern)通过引入表示实例状态的角色,可以在保存和恢复实例时有效的防止对象的封装性遭到破坏(在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态)。

Memento模式可以实现以下功能

  1. Undo
  2. Redo
  3. History
  4. Snapshot

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

// Memento
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 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();
    }
}
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);
    }
}
public class Main {
    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());
    }
}

Chapter 19: State模式–用类表示状态

在面向对象编程中,我们往往是用类表示对象的。但是事实上我们要面对的情况往往要复杂的多,并不是每个类都是含义清晰明了的具体对象,在之前的学习中,不难发现我们有很多的类扮演了比较抽象的角色。而这一章节中我们要使用类来表示状态。这样我们就能通过切换类来方便的改变对象的状态。

我们将创建一个State接口和实现了State接口的实体状态类。Context是一个带有某个状态的类(这个类具有多种状态,但是每一时刻只有一个状态)。Main类使用Context和状态对象来演示Context在状态改变时的行为变化。

// State.java
public interface State {
    public void doAction(Context context);
}
// StartState.java: State1
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";
    }
}
// StartState.java: State2
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";
    }
}
// Context.java: have 2 states-Start and Stop
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 Main {
    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());
    }
}