【软件构造】6.3 面向可维护性的构造技术

本文介绍面向可维护性的构造技术,包括状态驱动编程,如基于自动机编程、状态模式和备忘录模式;表驱动编程,有直接查表、索引查表和阶梯查表;语法驱动构造,涵盖语法组成、操作符、递归、解析树,还提及Markdown与HTML、正则语法与表达式、解析器及Java中的正则表达式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

面向可维护性的构造技术

因为lab3和各种考试鸽了两周 希望可以补回来

状态驱动的编程

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

基于自动机的编程

将程序视为有穷自动机,将程序的执行分解为一系列的步骤,每个步骤是一段代码片段的执行,这段代码片段只有一个入口点。可以是函数、其他路径、或仅仅是一个循环体。每一个步骤与其他步骤的联系通过改变代表“状态”的变量值完成,程序的控制流由该变量值决定。程序在任意两个时刻的状态,只能有表示“状态”的变量取值不同。程序的执行可以看作是各自动步骤的不断循环。
使用枚举类型定义状态。
使用二维数组定义状态转换表。
与控制系统的设计思想类似。侧重于对“状态”及“状态转换”的抽象和编程。

状态模式 State Pattern

在这里插入图片描述
behavioral pattern

class Context { 
	State state; //保存对象的状态 
	//设置初始状态 
	public Context(State s) {state = s;} //设置delegation关系
	//接收外部输入,开启状态转换 
	public void move(char c) {
		state = state.move(c); //将改变状态的“动作”delegate到state对象,每次状态转换之后形成新状态,替换原状态
	}
	//判断是否达到合法的最终状态 
	public boolean accept() {  return state.accept();  } 
	public State getState() {  return this.state; } 
}

//状态接口 
public interface State { 
	State move(char c); 
	boolean accept(); 
}

状态类:

class State1 implements State { 
	static State1 instance = new State1();  //singleton模式  
	private State1() {}   
	public State move (char c) { 
		switch (c) { 
			case ‘a’: return State2.instance;   //返回新状态的singleton实例 
			case 'b': return State1.instance; 
			default: throw new IllegalArgumentException(); 
		} 
	} 
	public boolean accept() { return false; } //该状态非可接受状态 
}
class State2 implements State { 
	static State2 instance = new State2(); 
	private State2() {} 
	public State move (char c) { 
		switch (c) { 
			case 'a': return State1.instance; 
			case 'b': return State1.instance; 
			default: throw new IllegalArgumentException(); 
		} 
	} 
	public boolean accept() {return true;} 
}

在这里插入图片描述

public static void main(String[] args) { 
	Context context = new Context(State1.instance); 
	for (int i = 0; i < args.length; i++) { 
		context.move(args[i]); 
		if(context.accept()) 
			break; 
	} 
}

每个state类可以使用singletons 方式创建实例
方便添加新的类

备忘录模式 Memento Pattern

记住对象的历史状态,以便于“回滚”
在这里插入图片描述
例:

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() { //保存历史状态,delegate到memento实现
		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); 
	}
}
class Memento { 
	private State state; 
	public Memento(State state) { 
		this.state = state; 
	}
	public State getState() { 
		return state; 
	}
}
class Caretaker { 
	private List<Memento> mementos = new ArrayList<>();
	public void addMemento(Memento m) { 
		mementos.add(m); 
	}
	public Memento getMemento() { 
		return mementos.get(mementos.size()-1); 
	}
}

表驱动的编程

将代码中复杂的if-else和switch-case语句从代码中分离出来,通过“查表”的方式完成,从而提高可维护性

private static double getPrice2(MovieType movieType, int daysRented) {
	final double initialCharge[] = { 2, 0, 1.5 }; 
	final double initialDays[] = { 2, 0, 3   }; 
	final double multiplier[] = { 1.5, 3, 1.5 };
	int intType = movieType.ordinal();
	double price = initialCharge[movieType]; 
	if (daysRented > initialDays[movieType]) 
		price += (daysRented - initialDays[movieType]) * multiplier[movieType];
	return price;
}
enum MovieType { Regular, NewRelease, Childrens };

直接查表

直接用索引查
例如:
在这里插入图片描述

int daysPerMonth[ ] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
days = daysPerMonth[month-1];

索引查表

如果数字范围很大,直接查表就比较难。比如:用八位数的产品id做一个能查200个产品的表。
1.索引元素可以很小,值可以很大
2.多个索引查找到同样的数据(雇员信息可以通过姓名、雇佣时间、薪水映射到)
3.易于维护:查找方法相互隔离

阶梯查表

表的入口可能是某一范围内的数据而不是具体某一值的数据
在这里插入图片描述

// set up data for grading table 
float rangeLimit[] = { 50.0, 65.0, 75.0, 90.0, 100.0 }; 
String grade[] = { "F", "D", "C", "B", "A" }; 
int maxGradeLevel = grade.length – 1;
 ... 
// assign a grade to a student based on the student's score 
int gradeLevel = 0; 
String studentGrade = "A" ; 
while (( studentGrade == "A" ) && ( gradeLevel < maxGradeLevel )) { 
	if ( studentScore < rangeLimit[ gradeLevel ] ) { 
		studentGrade = grade[ gradeLevel ];   
	}   
	gradeLevel = gradeLevel + 1; 
}

总结:表驱动的编程可以替代复杂的逻辑判断和继承结构。如果程序的逻辑或者继承关系比较混乱,可以尝试用表驱动编程简化。一个关键点是如何查表,另一个关键点是表中的内容放什么。

语法驱动的构造

读取有特定格式的数据,从中抽取正确的内容

语法的组成

用语法定义一系列字符
语法解析树的终止节点(叶节点):无法再向下扩展,通常表示为字符串。
语法解析树的非终止节点(产生式节点):遵循特定规则,利用操作符、终止节点和其他非终止节点构造新的字符串。是语法解析树的内节点。
非终止节点 := 一个终止节点、非终止节点和操作符的表达式
非终止节点之一是根节点

语法的操作符

1.连接 用空格表示
2.重复 用“”表示 (0或多次)
3.选择 用“|”表示
4.可选 用"?"表示 出现或不出现 例x ::= y?
5.一次或多次出现 用“+”表示
6.包含的字符集合 […]
7.不包含的字符集合 [^…]
优先级:
,?,+ 最高,其次是连接,最低的是 |
通过括号调整优先级

例:匹配网址,如http://mit.edu/, http://alibaba.com/
url ::= ‘http://’ hostname ‘/’
hostname ::= word ‘.’ word
word ::= [a-z]+

语法中的递归

例1:匹配网址,如http://didit.csail.mit.edu:4949/
url ::= ‘http://’ hostname (’:’ port)? ‘/’
hostname ::= word ‘.’ word | word ‘.’ hostname ?递归
port ::= [0-9]+
word ::= [a-z]+
例2:
S ::= (B C)* T
B ::= M+ | P B P
C ::= B | E+
非终止节点:S,B,C
终止节点:其余节点
递归表达式:B ::= M+ | P B P

解析树

语法树生成字符串:解析树 展示字符串的每一部分如何与语法的部分对应
先深搜索
例:
递归
在这里插入图片描述
非递归
在这里插入图片描述
在这里插入图片描述

Markdown 与 HTML

在这里插入图片描述

正则语法与正则表达式

正则语法:简化之后可以表达为一个产生式,不包含任何非终止节点
正则表达式符合正则语法
正则表达式去除了引号和空格,只包含叶节点字符,括号和操作符
正则表达式中的特殊操作符:
1."." 任意单一字符
2.“\d" 任意数字 同[0-9]
3.”\s" 任意空白字符,包括空格,tab,换行符
4."\w" 同[a-zA-Z_0-9]
5.转义符:".", “(,)” ,"*","+"

解析器 Parser

parser输入一段文本,与特定的语法规则建立匹配,输出结果。将文本转化为parse tree,利用产生的parse tree进行下一步的处理。
parser generator 是根据语法规则生成parser程序的工具
在这里插入图片描述

Java中的正则表达式

java.util.regex包
1.Pattern:是对regex编译后得到的结果
2.Matcher :利用Pattern对输入字符串进行解析
3.PatternSyntaxException:unchecked exception,表示正则表达式的语法错误
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值