一、可维护性的度量与准则
软件演化:对软件进行持续的更新
软件大部分成本来自维护阶段
圈复杂度:衡量代码的结构复杂度
代码行数LOC、MI可维护指数
OO设计模式,OO设计原则
1、模块化编程
- 要求:高内聚,低耦合,分离关注点,信息隐藏
- 五个衡量标准:可分解性,可组合性,可理解性,可持续性(发生变化时影响范围最小),出现异常后的保护(出现异常时受影响范围最小)
- 五个规定:直接影视,尽可能少的接口,尽可能小的接口,显式接口,信息隐藏
2、OO设计原则:SOLID
(1)SRP:单一责任原则(The Single Responsibility Principle)
- 不应该有多于1个原因让你的ADT发生变化
- 一个类,一个责任
- 否则导致引入额外包,占据资源;频繁重新配置部署
(2)OCP:开放封闭原则(open/closed principle)
- 对扩展性的开放:模块是可扩展的
- 对修改的封闭:模块自身的的代码不应该修改
- 关键解决方案:抽象技术
(3)LSP:Liskov替换原则
(4)ISP:接口隔离原则(Interface Segregation Principle)
- 不能强迫客户端依赖于它们不需要的接口,只提供必须的接口
(5)DIP:依赖转置原则(Dependency Inersion)
- 抽象模块不应依赖于具体模块
- delegation时,要通过interface建立,而不是具体子类
二、面向可维护性的设计模式
1、工厂方法模式 factory
- 虚拟构造器,当client不知道要创建哪个具体类的实例,或者不想在client代码中指明要具体创建的实例时,用工厂方法
public interface Trace{
...
}
public class FileTrace implements Trace{
...
}
public class SystemTrace implements Trace{
...
}
public interface TraceFactory{
public Trace getTrace();
public Trace getTrace(String type);
}
public Factory1 implements TraceFactory{
public Trace getTrace(){
return new SystemTrace();
}
}
public Factory2 implements TraceFactory{
public Trace getTrace(String type){
if(type.equals("file"){
return new FileTrace();
}
}
}
//use it
Trace log1 = new Factroy1().getTrace();
Trace log2 = new Factroy2().getTrace("file");
- 静态工厂方法
public class TraceFactory1{
public static Trace getTrace(){
return new SystemTrace();
}
}
//use:
Trace log1 = TraceFactory1.getTrace();
2、抽象工厂方法模式
- 客户端要得到一组对象,对象之间要搭配(固定搭配)
- 提供接口以创建一组相关/相互依赖的对象
- 本质是把多个产品的method组合在一起
3、Proxy:代理模式
- 对象比较私密敏感,不希望直接被客户端访问
- 委派
Proxy 和 Adaptor
Adaptor:消除不兼容,目的是以客户端期望的统一方式建立联系
Proxy:隔离对复杂对象的访问,降低代价
4、observer观察者模式
- 粉丝对偶像感兴趣,希望随时知道偶像的一举一动
粉丝到偶像那里注册,偶像发生变化,就对送给已注册的粉丝(毁掉callback粉丝的特定功能) - 使用委派
//偶像:
public class Subject{
private List<Observer> observers = new ArrayList<>(); //维持一组粉丝列表
private int state;
//粉丝注册
public void attach(Observer ob){
this.observers.add(ob);
}
//改变状态时,通知粉丝
public void setState(int state){
this.state = state;
notifyAllobs();
}
//调用粉丝的update操作,向粉丝广播变化
private void notifyAllobs(){
for(Observer ob : this.observers){
ob.update();
}
}
}
//粉丝
public abstract class Observer{
protected Subject subject; //粉丝关注的偶像
public abstract void update();
}
public class Fens extends Observer{
public Fens(Subject sub){
this.subject = sub;
this.subject.attach(this); //这是相反方向的delegation
}
@Override
//该方法被偶像调用
public void update(){
System.out.println( "Binary String: " + Integer.toBinaryString( subject.getState() ) );
}
}
5、visitor访问者模式
- 为ADT预留一个将来可扩展功能的接入点,外部实现的功能代码可以在不改变ADT本身的情况下
- OCP原则
public interface Item{
public int accept(ShoppingCarVisitor visitor);
}
public class Book implements Item{
private int price;
...
@Override
public int accept(ShoppingCarVisitor visitor){
visitor.visit(this); //delegate到visitor
}
}
public class Fruit implements Item{
private int weight;
...
@Override
public int accept(ShoppingCarVisitor visitor){
visitor.visit(this); //delegate到visitor
}
}
//visitor
public interface ShoppingCarVisitor{
public int visit(Book book);
public int visit(Fruit fruit);
}
public class ShoppingCarVisitorImpl implements ShoppingCarVisitor{
public int visit(Book book){
.......
}
public int visit(Fruit fruit){
......
}
}
客户端使用:
5、设计模式对比
(1)只用继承,不用委派
- 核心:OCP/DIP
- Adaptor适配器:OCP,扩展一个adaptor和一个统一接口
- Proxy代理模式:DIP,对象比较敏感,客户端和对象间进行隔离
- Template模板模式:单纯基于override
(2)两颗继承树,两个层次的委派:OCP - Strategy策略模式:OCP
- Iterator 迭代器模式:OCP,Interable 和Interator
- Factory工厂模式:OCP
- Abstract Factory抽象工厂模式
- 观察者模式
- 访问者模式
- 状态模式、
- 备忘录模式
三、面向可维护性的构造技术
1、State-based construction
(1)基于自动机编程:将程序看做是一个优先状态,侧重状态转化
(2)状态模式
- delegation委派
public class Context{
State state;
public Context(State s){this.state = s;}
//转换状态
public void move(Char c){this.state = state.move(c);}
public boolean accept(){return this.state.accept();}
public State getstate(){return this.state;}
}
public interface State{
public State move(char c);
public boolean accept();
}
public State1 implements State{
public static State1 intance = new State1(); //单例模式sigleton,创建一个自身实例
public State move(Char c){
switch(c){
case 'a':return State2.intance;
case 'b':return State1.intance;
default: throw new IllegalArgumentException();
}
}
//该状态为非可接受状态
public boolean accept(){
return false;
}
}
public State2 implements State{
public static State2 intance = new State2(); //单例模式sigleton,创建一个自身实例
public State move(Char c){
switch(c){
case 'a':return State1.intance;
case 'b':return State1.intance;
default: throw new IllegalArgumentException();
}
}
//该状态为非可接受状态
public boolean accept(){
return true;
}
}
(3)备忘录模式
- 记录对象的历史状态,以便于“回滚”
二、语法驱动的构造
(1)节点:
- nonterminals 非终端节点
- terminals 终端节点/叶节点
(2)语法
- x::= yz 连接y和z
- x::= y* y重复>=0次
- x::= y|z 或
- x::= y? x等于y或x为空
- x::=y+ y重复>0次
- x::=[a-c] 等价于 x::=‘a’|‘b’|‘c’
- x::=[aeiou]
- x::=[^a-c] 等价于x::=‘d’|‘e’|‘f’|…‘y’|‘z’
(3)语法分析树:Parse Tree
- 注意箭头:箭头经过是一种合法表述
(4)Markdown和HTML
- Markdown不能嵌套法定义,HTML可以嵌套定义
(5)正则表达式
- . 表示一个任意字符
- \d 表示[0-9]
- \D 表示[^0-9]
- \s 表示空白,如tab,newline,空格
- \S 表示[^\s]除了\s表示的所有字符
- \w 表示[a-zA-Z_0-9]
- \W (大写)表示除了\w表示的所有字符
- \ . ,\ ( ,\ *,\ +,等表示纯粹的符号
如:
‘http://’([a-z]+’.’)+[a-z]+(’:’[0-9]+)?’/’
表示成正则表达式:
http://([a-z]+\.)+[a-z]+(:[0-9]+)?/
(6)正则表达式的使用
- 典例:
String.spilt, java.util.regx.Pattern
import java.util.regex*;
String a = "http://wwww/hit/cn";
Pattern regex = Pattern.compile("http://([a-z]+\\.)+[a-z]+(:[0-9]+)?/"); //compile:编译
Matcher m = regex.matcher(a);
if(m.matches()){
String ur1 = m.group(1); //第一个匹配的部分
}