软件构造系列学习笔记(6.3)————可维修性的构造技术

可维修性的构造技术

学了这么多OO设计模式,不外乎都是 delegation + subtying,万变不离其宗。

除了OO,还有什么其他能够提升软件可维护性的构造技术?——本节从委派+子类型跳出来,学习以下三个方面:
(1) 基于状态的构造技术 (2) 表驱动的构造技术 (3) 基于语法的构造技术

目录

  • 基于状态的构造技术
  • 表驱动的构造技术
  • 基于语法的构造技术

基于状态的构造技术

基于状态的构造技术使用有限状态机来定义程序的行为、控制程序的执行。根据当前状态,决定下一步要执行什么操作、执行操作之后要转移到什么新的状态。

基于自动机的编程(Automata-based programming)

核心思想:将程序看作是一个有限状态自动机,侧重于对“状态”及“状态转换” 的抽象和编程。
这里写图片描述

程序的执行被分解为一组自动执行的步骤。各步骤之间的通讯通过“状态变量”进行。程序执行就可看作是各自动步骤的不断循环。 使用枚举类型enum定义状态 ,使用二维数组定义状态转换表。

State transition[][] = { 
    { State.Initial, State.Final, State.Error }, 
    { State.Final, State.Initial, State.Error } 
}; 

应用领域:

  • 高可靠性系统:军事应用、航空航天工业、汽车工业
  • 嵌入式系统
  • 移动系统
  • 可视化系统
  • Web应用
  • 客户端-服务器应用

状态模式 (behavioral pattern)

这里写图片描述

示例 - 有限状态机:
这里写图片描述
这里写图片描述
这里写图片描述

main函数
这里写图片描述

备忘录模式 (behavioral pattern)

记住对象的历史状态,以便于“回滚”。
备忘录设计模式定义了三种不同的角色:

  • Originator:需要“备忘”的类
  • Caretaker:添加originator的备忘记录和恢复
  • Memento:备忘录,记录originator对象的历史状态

这里写图片描述

例子:
Originator类:

class Originator { 
    private State state;
    public void setState(State state) { 
        System.out.println("Originator: Setting state to " +state.toString()); 
        this.state = state; 
    }
    public Memento save() { 
        System.out.println("Originator: Saving to Memento."); 
        return new Memento(state); 
    } 
    public void restore(Memento m) { 
        state = m.getState(); 
        System.out.println("Originator: State after restoring from Memento: " + state); 
    }
}

Memento类:

class Memento { 
    private State state;
    public Memento(State state) { 
        this.state = state; 
    }
    public State getState() { 
        return state; 
    }
}

Caretaker类:

class Caretaker { 
    private ArrayList<Memento> mementos = new ArrayList<>();
    public void addMemento(Memento m) { 
        mementos.add(m); 
    }
    public Memento getMemento() { 
        return mementos.get(1); 
    }
}

主函数:

public class Demonstration { 
    public static void main(String[] args) { 
        Caretaker caretaker = new Caretaker(); 
        Originator originator = new Originator();
        originator.setState("State1"); 
        originator.setState("State2"); 
        caretaker.addMemento( originator.save() );
        originator.setState("State3"); 
        caretaker.addMemento( originator.save() );
        originator.setState("State4"); 
        originator.restore( caretaker.getMemento() ); 
    } 
}

结果:

Originator: Setting state to State1 
Originator: Setting state to State2 
Originator: Saving to Memento. 
Originator: Setting state to State3 
Originator: Saving to Memento. 
Originator: Setting state to State4 
Originator: State after restoring from Memento: State3

表驱动的构造技术(Table-driven construction)

表驱动编程的核心思想:将代码中复杂的if-elseswitch-case语句从代码中分离出来,通过“查表”的方式完成,从而提高可维护性。

Direct Access Tables(直接访问表)

只需简单地通过一个或多个索引“查找”。
这里写图片描述

Indexed Access Tables(索引访问表)

有时直接索引是一个问题,特别是如果可能的值域很大。例如,如果您想使用产品ID(8位数字),并制作一张映射200个产品的表格。
这里写图片描述

Stair-Step Access Tables(阶梯访问表)

表格中的条目对数据范围有效,而不适用于不同的数据点。
这里写图片描述

语法驱动的构造(Grammar-based construction )

有一类应用,从外部读取文本数据, 在应用中做进一步处理。
具体来说,读取的一个字节或字符序列可能是:

  • 输入文件有特定格式,程序需读取文件并从中抽取正确的内容。
  • 从网络上传输过来的消息,遵循特定的协议。
  • 用户在命令行输入的指令,遵顼特定的格式。
  • 内存中存储的字符串,也有格式需要。

对于这些类型的序列,语法的概念是设计的一个好选择:

  • 使用grammar判断字符串是否合法,并解析成程序里使用的数据结构 。
  • 通常是递归的数据结构 。

正则表达式是一种广泛使用的工具。

语法成分

一个语法由一组产生式节点描述,其中每个产生式节点定义一个非终止节点。
一个非终止节点遵循特定规则,利用 操作符、终止节点和其他非终止节点,构造新的字符串。

语法中的操作符

表达中最重要的三个操作符是:

  • 连接,不是通过一个符号,而是一个空间:x ::= y z //an x is a y followed by a z
  • 重复,以*表示:x ::= y* //an x is zero or more y
  • 联合,也称为交替,如图所示 | :x ::= y | z //an x is a y or a z

三个基本操作符的组合:

  • 可选(0或1次出现),由?表示:x ::= y? //an x is a y or is the empty string
  • 出现1次或多次:以+表示:x ::= y+ //an x is one or more y,equivalent to x ::= y y*
  • 字符类[…],表示长度的字符类,包含方括号中列出的任何字符的1个字符串:x ::= [abc] is equivalent to x ::= 'a' | 'b' | 'c‘
  • 否定的字符类[^…],表示长度,包含未在括号中列出的任何字符的1个字符串:x ::= [^abc] //is equivalent to x ::= 'd' | 'e' | 'f' | ... (all other characters in Unicode)

例子:

  • x ::= (y z | a b)* //an x is zero or more y-z or a-b pairs
  • m ::= a (b|c) d //an m is a, followed by either b or c, followed by d

Example 1: URL

写一个语法表示URL,下面是两个URL
http://stanford.edu/
http://google.com/

我们可以这样写来匹配上面类型的URL

url ::= 'http://' [a-z]+ '.' [a-z]+  '/'

如果我们使用新的非终止节点,将会更容易理解:
这里写图片描述

hostname可以有两个以上的部分,并且可以有一个可选的端口号:
http://didit.csail.mit.edu:4949/
为了处理这样的字符串,语法可以这样写:
这里写图片描述

Example 2: Markdown and HTML

下面是Markdown和HTML的语法:
Markdown:

This is _italic_.

HTML:

Here is an <i>italic</i> word. 

这里写图片描述

正则语法和正则表达式(Regular Grammars and Regular Expressions)

正则语法:简化之后可以表达为一个产生式而不包含任何非终止节点。
这里写图片描述

第三个语法中含有html非终止节点,因此不是正则语法。

正则表达式去除引号和空格,从而表达更简洁(更难懂)。下面介绍一些正则表达式的基本语法。
这里写图片描述

Parsers

parser:输入一段文本,与特定的语法规则建立匹配,输出结果 ,parser文本转化为parse tree, 利用产生的parse tree,进行下一步的处理 。
这里写图片描述

Using regular expressions in Java

Java中提供了java.util.regex 类来使用正则表达式。可以这么使用

//Match a URL
Pattern regex = 
    Pattern.compile("http://([a-z]+\\.)+[a-z]+(:[0-9]+)?/");
Matcher m = regex.matcher(string); 
if (m.matches()) { // then string is a url }
//Extract part of an HTML tag:
Pattern regex = Pattern.compile("<a href=\"([^\"]*)\">"); 
Matcher m = regex.matcher(string); 
if (m.matches()) { 
    String url = m.group(1); 
    // Matcher.group(n) returns the nth parenthesized part of 
    // the regex 
}

Character Classes:
这里写图片描述

Predefined Character Classes:
这里写图片描述

Quantifiers:
这里写图片描述

Boundary Matchers:
这里写图片描述

解释器模式( Interpreter)

给定一种语法,定义该语法的程序内部表示,形成该语法的解释器,将遵循语法规则的文本解释成程序内部的表示(例如一组object) 。
解释语法的“引擎”:遵循语法的文本->OO表示 。

Implementation:

  • 因为语法通常形成树结构,故使用composite模式来表达遵循语法的内容。
  • 针对该层次化树形结构,定义了一组行为来处理结构中的不同类型节点。

这里写图片描述

Interpreter vs. grammar + parser
这里写图片描述

猜你喜欢

转载自blog.csdn.net/fundament/article/details/80214623

相关文章