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

Author Avatar
Cesare (MINGJU LI) Feb 06, 2020

设计模式以及综述

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

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

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

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

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

Chapter 13: Visitor模式–访问数据结构并处理数据

在数据结构中通常保存着许多的元素,我们会根据程序的需要对这些元素进行各种各样的“处理”,这样的“处理”的代码放在哪里就成了一个值得思考的问题。通常的做法是把他们放在表示数据结构的类中,但是如果处理有许多种呢?

通常情况下我们不希望去修改表示数据结构的类,为了解决这个问题,我们使用Visitor模式,将数据结构和处理分离开来。具体的方式是编写一个访问者类来访问数据结构中的元素,并且把对元素的处理交给访问者类,当需要增加新的处理时,我们只需要编写新的访问者,并使得数据结构可以接受访问者的访问即可。

通常情况下使用这种设计模式适用于一下两种情况:

  1. 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
  2. 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,也不希望在增加新操作时修改这些类。

例:我们将创建一个定义接受操作的ComputerPart接口。Keyboard、Mouse、Monitor和Computer是实现了ComputerPart接口的实体类。我们将定义另一个接口ComputerPartVisitor,它定义了访问者类的操作。Computer使用实体访问者来执行相应的动作。我们的Main类使用Computer、ComputerPartVisitor类来演示访问者模式的用法。

// ComputerPart.java: Interface
public interface ComputerPart {
   public void accept(ComputerPartVisitor computerPartVisitor);
}
// Keyboard.java: Class 1
public class Keyboard implements ComputerPart {
   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this);
   }
}
// Monitor.java: Class 2
public class Monitor implements ComputerPart {
   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this);
   }
}
// Mouse.java: Class 3
public class Mouse  implements ComputerPart {
   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this);
   }
}
// Computer.java: Class 4
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);
    }
}

这里Class 1/2/3/4表述的是我们前文中所述的数据结构,也就是我们不希望经常对其进行修改的部分

// Visitor interface
public interface ComputerPartVisitor {
   public void visit(Computer computer);
   public void visit(Mouse mouse);
   public void visit(Keyboard keyboard);
   public void visit(Monitor monitor);
}
// The real visitor
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.");
    }
}

这里的ComputerPartDisplayVisitor就是Visitor的类,不难看出,如果我们希望改变处理的方式,只需要在visit函数中进行修改即可,比如对于Monitor来说,可以定义一个returnArea(Monitor monitor),其内容为return monitor.length*monitor.width,而不必要修改Monitor的类。同理我们也可以新进一个新的实现了ComputerPartVisitor接口的类作为新的Visitor,定义不同的“处理”,以此类推,举一反三。

public class Main {
    public static void main(String[] args) {
        ComputerPart computer = new Computer();
        computer.accept(new ComputerPartDisplayVisitor());
    }
}

Chapter 14: Chain of Responsibility模式–推卸责任

我们经常去政府机关办理业务的时候碰到一个情况,在前台可能会让我们去户政科,去了户政科让我们去人力资源办公室等到。我们不断地被一个又一个的踢皮球(当然了,此类情况在今日之中国已经是改变了好多的),这就是我们在本章节中所要学习的Chain of Responsibility模式。

对其下个定义就是将多个对象组成一条职责链,然后按照这条职责链的顺序一个又一个的找出到底应该谁来负责处理,这种模式称为Chain of Responsibility模式。通过使用这种模式,我们避免了请求发送者与接收者耦合在一起,使得双方都成为可以独立复用的组件,让多个对象都有可能接收请求。在工作的时候将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

当一个人被要求做什么事儿的时候,如果可以做就(必须)自己做,如果不能做就将此要求转给另外一个人,下一个人如果可以自己做就(必须)自己做,如果不能就将此要求转给下一个人…

我们这里看一个例子,我们设计一些列的类,有的类解决编号小于指定编号的问题,有的类解决奇数编号的问题…对于一个问题,它会按照某种顺序被一一处理。

// Trouble.java:要被Chain of responsibility处理的“问题”类
public class Trouble{
    private int number;
    public Trouble(int number){
        this.number = number;
    }
    public int getNumber(){
        return number;
    }
    public String toString(){
        return "TROUBLE NO. "+number;
    }
}
// Support.java: 组成Chain of responsibility的处理问题的类,是这个例子中最核心的部分
public abstract class Support{
    private String name;
    private Support next;
    public Support(String name){
        this.name = name;
    }
    public Support setNext(Support next){
        this.next = next;
        return next;
    }
    public final void support(Trouble trouble){
        if(resolve(trouble)){
            done(trouble);
        }else if (next!=null){
            next.support(trouble);
        }else{
            fail(trouble);
        }
    }

    public String toString(){
        return name;
    }

    protected abstract boolean resolve(Trouble trouble);

    protected void done(Trouble trouble){
        System.out.println(trouble+" is resolved by "+this);
    }

    protected void fail(Trouble trouble){
        System.out.println(trouble+" cannot be solved");
    }

}
// NoSupport.java
public class NoSupport extends Support{
    public NoSupport(String name){
        super(name);
    }
    protected boolean resolve(Trouble trouble){
        return false;
    }
}
// LimitSupport.java:只解决编号小于limit的问题
public class LimitSupport extends Support{
    private int limit;
    public LimitSupport(String name, int limit){
        super(name);
        this.limit = limit;
    }
    protected boolean resolve(Trouble trouble){
        if (trouble.getNumber()<limit){
            return true;
        }else{
            return false;
        }
    }
}
// OddSupport.java: 只解决编号为奇数的问题
public class OddSupport extends Support{
    public OddSupport(String name){
        super(name);
    }
    protected boolean resolve(Trouble trouble){
        if(trouble.getNumer()%2==1){
            return true;
        }else{
            return false;
        }
    }
}
// SpecialSupport.java:只解决编号为number的问题
public class LimitSupport extends Support{
    private int number;
    public LimitSupport(String name, int number){
        super(name);
        this.number = number;
    }
    protected boolean resolve(Trouble trouble){
        if (trouble.getNumber()==number){
            return true;
        }else{
            return false;
        }
    }
}
// Main.java
public class Main{
    public static void main(String[] args){
        Support A = new NoSupport("A");
        Support B = new LimitSupport("B",100);
        Support C = new SpecialSupport("C",429);
        Support D = new LimitSupport("D",200);
        Support E = new OddSupport("E");
        Support F = new LimitSupport("F",300);

        A.setNext(B).setNext(C).setNext(D).setNext(E).setNext(F);

        for(int i = 0; i<500; i+=33){
            A.support(new Trouble(i));
        }
    }
}