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

Author Avatar
Cesare (MINGJU LI) Feb 06, 2020

设计模式以及综述

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

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

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

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

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

Chapter 22: Command模式–命令也是类

在一个类的工作的过程中,我们会调用自己或是其他类的方法,虽然调用结果会反映在对象的状态中,但是这并不会留下工作的记录或者日志。如果我们有一个类来表示“进行这项工作”的命令即可,我们称这样的思想为Command模式。

这个模式的关键就是定义三个角色

  1. 使用命令对象的入口-invoker
  2. 命令-command
  3. 命令的执行对象-receiver

例:我们首先创建作为命令的接口Order(command),然后创建作为请求的Stock类(invoker)。实体命令类BuyStock(concretecCommand1)和SellStock(concreteCommand2),实现了Order接口,将执行实际的命令处理。创建作为调用对象的类Broker(receiver),它接受订单并能下订单。Broker对象使用命令模式,基于命令的类型确定哪个对象执行哪个命令。Main类使用Broker类来演示命令模式。

public interface Order {
    void execute();
}
// Receiver
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");
    }
}
// Command 1
public class BuyStock implements Order {
    private Stock abcStock;
    public BuyStock(Stock abcStock){
        this.abcStock = abcStock;
    }
    public void execute() {
        abcStock.buy();
    }
}
// Command2
public class BuyStock implements Order {
    private Stock abcStock;
    public BuyStock(Stock abcStock){
        this.abcStock = abcStock;
    }
    public void execute() {
        abcStock.buy();
    }
}
// Invoker
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 Main {
    public static void main(String[] args) {
        Stock abcStock = new Stock();

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

        Broker broker = new Broker();
        //实际的命令执行者
        System.out.println("------LINE 1------");

        broker.takeOrder(buyStockOrder);
        broker.takeOrder(sellStockOrder);
        System.out.println("------LINE 2------");
        broker.placeOrders();
    }
}
//OUTPUT
------LINE 1------
------LINE 2------
Stock [ Name: ABC, Quantity: 10 ] bought
Stock [ Name: ABC, Quantity: 10 ] sold

Chapter 23: Interpreter模式–语法规则也是类

相比于前几种设计模式,Interpreter模式要更加的复杂。在这个模式中,程序需要解决的问题会被一些比较简单的“迷你语言”表示出来,而我们要编写一段“翻译程序”使其能够理解迷你语言,并且解释和运行迷你程序,这段翻译程序也被成为解释器,这样的话当我们需要解决什么问题的时候,不需要修改我们编写的翻译程序本身,而是修改迷你语言程序即可!

想象用java来实现MySQL!

例:我们将创建一个接口Expression和实现了Expression接口的实体类。定义作为上下文中主要解释器的TerminalExpression类。其他的类OrExpression、AndExpression用于创建组合式表达式。Main类使用Expression类创建规则和演示表达式的解析。

public interface Expression {
    public boolean interpret(String context);
}
// 翻译结果是true/false---表示context含有/不含data
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;
    }
}
// 翻译结果是expr1 OR expr2的结果
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);
    }
}
// 翻译结果是expr1 AND expr2的结果
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 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"));
    }
}
// OUTPUT
John is male? true
Julie is a married women? true