软构复习(3)

第九章 面向复用的软件构造技术

复用的类型
软件复用:最主要的是代码复用,但也有其他方面。
Source code level:methods, statements, etc Module level:class and interface Library level:API Architecture level:framework
白盒复用:源代码可见、可修改和扩展(对应继承)
黑盒复用:源代码不可见,不能修改,只能通过API接口使用(对应委托)
代码复用:即直接复制代码(不推荐)
类的复用:inheritance 继承 delegation 委托

继承(inheritance )能做的事委托(delegation)也能做,继承要求严格父子关系
组合优先于继承,注:组合是委派的一种形式。

框架(framework)
框架:一组具体类、抽象类、及其之间的连接关系
开发者根据framework的规约,填充自己的代码进去,形成完整系统
API和框架的区别:主控端在用户/框架

LSP(重点)

子类型多态:客户端可以用统一的方式处理不同类型的对象。

LSP原则
能被Java静态类型检测检测出的
子类型可以增加方法,但不可以删除方法
子类型需要实现抽象类型中的所有未实现的方法
子类型中重写的方法必须返回相同的类型或者子类型(满足协变)
子类型中重写的方法必须使用同样类型的参数(或符合逆变的参数)
子类型中重写的方法不能抛出额外的异常(协变)

不能被静态类型检测出的
更强的不变量
更弱的前置条件
更强的后置条件(与上条综合即规约更强)

协变
父类型→子类型:越来越具体specific
返回值类型:不变或变得更具体
异常的类型:也是如此。
Java中数组是协变的: 对T[]数组,可以保存类型T及其子类型的数据

反协变、逆变
父类型→子类型:越来越不具体specific
参数类型:要相反的变化,要不变或越来越抽象
注意:Java不支持反协变!Java识别其为重载(而非重写)

泛型中的LSP
泛型不满足协变 List<String>不是List<Object>的子类型
Object是所有泛型的父g类,List<?>是List<Object>的父类

Interface Comparator< T >
int compare(T o1, T o2): Compares its two arguments for order

如果你的ADT需要比较大小,或者要放入Collections或Arrays进行排序,可以实现Comparator接口并且override compare()函数

另一种方法:让ADT实现Comparable接口,然后override compareTo() 方法

与使用Comparator的区别:不需要构建新的Comparator类,比较代码放在ADT内部

委托(Delegation)
委派/委托:一个对象请求另一个对象的功能
如果子类只需要复用父类中的一小部分方法,可以通过委托机制调用。
委托是复用的一种常用形式。(CRP原则:尽量使用委托进行复用)

Use 使用:通过方法的参数传递(use_a)
Association 关联:通过类的属性传递(has_a)

class B{
	void b(A a){  //use 使用
		...	
	}
}
class B{
	A a; //Association 关联
	....
}

composition/aggregation 组合/聚合(可认为是Association的两种具体形态)

聚合运行时可更改绑定对象(较弱的关联)

聚合B类销毁时,A类可能不会销毁(可能还有指向其的指针);组合B类销毁时,A类同时被销毁

第十章 面向可维护性的软件构造技术

(本章重点:SOLID、正则表达式)

可维护性度量指标
圈复杂度(Cyclomatic Complexity)、代码行数、可维护性指数(MI)、继承的层次数、类之间的耦合度、单元测试的覆盖度

模块化编程
高内聚(High cohension) 低耦合(Low coupling)
耦合(Coupling):不同模块之间的相互依赖性
内聚(Cohension):模块内功能和职责的一致性

耦合和内聚之间的权衡:即不能同时高/同时低

SOLID设计原则

SRP(单一责任原则)
OCP(开放-封闭原则)
LSP(Liskov替换原则)
ISP(接口聚合原则)
DIP(依赖转置原则)

SRP(单一责任原则)
把类拆分,使得每个类只完成一个功能。(不应该有多于一个的原因使得类发生变化)
SRP是最简单的原则,却是最难做好的原则

OCP(开放/封闭原则)
对扩展性的开放:模块的行为应该是可扩展的
对修改的封闭
关键的解决方案:抽象技术
例:如果有多种类型的Server,那么针对每一种新出现的Server,不得不修改Server类的内部具体实现;
通过构造一个抽象的Server类:AbstractServer,该抽象类中包含针对所有类型的Server都通用的代码,从而实现了对修改的封闭;当出现新的Server类型时,只需从该抽象类中派生出具体的子类ConcreteServer即可,从而支持了对扩展的开放。

LSP(Liskov替换原则)
详情见第九节LSP原则
子类型必须能够替换其基类型
派生类必须能够通过其基类的接口使用,客户端无需了解二者之间的差异

ISP(接口隔离原则)
尽量和专用接口连接(客户端不应依赖于它们不需要的方法)

DIP(依赖转置原则)
尽量依赖抽象类而不是具体类
也就是delegation的时候,通过interface来建立联系,而非具体子类
派生类必须能够通过其基类的接口使用,客户端无需了解二者之间的差异

正则表达式
*:重复0-多次
|:选择 a|b
?:0次或1次 x ::= y ? x为y或空串
[a-c]:'a' | 'b' | 'c'
[\^a-c] :'d' | 'e' | 'f' …(相当于补)

正则语法(Regular grammar)
正则语法:简化之后可以表达为一个产生式而不包含任何非终止节点
正则表达式:左侧为非终结符,右侧没有非终结符

.  	Any character (may or may not match line terminators)
\d 	A digit: [0-9]
\D 	A non-digit: [^0-9]
\s 	A whitespace character: [ \t\n\x0B\f\r]
\S 	A non-whitespace character: [^\s]
\w 	A word character: [a-zA-Z_0-9]
\W 	A non-word character: [^\w]
^ The beginning of a line
$ The end of a line
\b A word boundary
\B A non-word boundary
\A The beginning of the input
\G The end of the previous match
\Z The end of the input but for the final terminator, if any
\z The end of the input

注意有的字符需要进行转义

Java中的正则表达式
Pattern是对regex正则表达式进行编译之后得到的结果
Matcher:利用Pattern对输入字符串进行解析
Matcher对象只能通过Pattern静态方法创建,不能new

第十一章 设计模式

分为以下三类模式:创建型模式、结构型模式、行为型模式

创建型模式(Creational patterns)

工厂方法模式(Factory Method pattern)
当client不知道要创建哪个具体类的实例,或者不想再client代码中指明要具体创建的实例时,用工厂方法。
工厂模式:将创建一个对象的方法委托给另一个类(工厂类)来实现
Client用工厂方法来创建实例,得到的实例类型是抽象接口而非具体类

interface TraceFactory {
    Trace getTrace();	...
}
public class SystemTraceFactory implements TraceFactory {
    public Trace getTrace() {return new SystemTrace();}
}
public class FileTraceFactory implements TraceFactory {
    public Trace getTrace() {return new FileTrace();}
}
Trace log1 = new SystemTraceFactory().getTrace();
Trace log2 = new FileTraceFactory().getTrace();

静态工厂方法(在工厂方法前加static):既可以在ADT内部实现,也可以单独创建工厂类
该设计模式是OCP(扩展/开放原则)的一个体现。
优点:实现信息隐藏
缺点:需要额外创建工厂类,程序更复杂

public class SystemTraceFactory{
    public static Trace getTrace() {
    	return new SystemTrace();
    }
}
public class TraceFactory {
    public static Trace getTrace(String type) {
        if(type.equals("file") 
           return new FileTrace();
        else if (type.equals("system") 
           return new SystemTrace();
    }
}
Trace log1 = SystemTraceFactory.getTrace();
Trace log2 = TraceFactory.getTrace("system");

结构型模式(Structural patterns)

适配器模式(Adapter)
将某个类/接口转换为client期望的其他形式(主要解决接口不匹配问题)
通过增加一个接口,将已存在的子类封装起来,client面向接口编程,从而隐藏了具体子类

interface Shape {
	void display(int x1, int y1, int x2, int y2);
}
class Rectangle implements Shape {
    void display(int x1, int y1, int x2, int y2) {
    	new LegacyRectangle().display(x1, y1, x2-x1, y2-y1);    
    }
}
class LegacyRectangle {
	void display(int x1, int y1, int w, int h) {...}
}
class Client {
    Shape shape = new Rectangle();
    public display() {
    	shape.display(x1, y1, x2, y2);
    }
}

装饰器模式(Decorator)
对一个类的功能进行扩充(实现特性的组合)
装饰器在运行时组合特性;继承在编译时组合特性

public interface IceCream { //顶层接口
	void AddTopping();
}
public class PlainIceCream implements IceCream{ //基础实现,无填加的冰激凌
    @Override
    public void AddTopping() {
        System.out.println("Plain IceCream ready for some toppings!");
    }
}
/*装饰器基类*/
public abstract class ToppingDecorator implements IceCream{
    protected final IceCream input;
    public ToppingDecorator(IceCream i){
        this.input = i; 
    }
    public abstract void AddTopping();  //留给具体装饰器实现
}

行为型模式(Behavioral patterns)

策略模式(Strategy)
使用功能的时候不访问功能实现,访问接口(在多个功能间灵活切换)
考试时一般要做的:1. 抽象出一个接口类 2. 调用接口

public interface PaymentStrategy {
	public void pay(int amount);
}
public class PaypalStrategy implements PaymentStrategy {
    private String emailId;
    private String password;
    public PaypalStrategy(String email, String pwd){
        this.emailId=email;
        this.password=pwd;
    }
    @Override
    public void pay(int amount) {
    	System.out.println(amount + " paid using Paypal.");
    }
}
public class ShoppingCart {
    ...
    public void pay(PaymentStrategy paymentMethod){
        paymentMethod.pay(amount);
    }
}

模板模式(Template Method)
共性的操作步骤在抽象类中公共实现,差异化的步骤在各个子类中实现
使用继承和重写来实现模板模式
模板模式在框架中应用广泛
抽象类中有一些方法用final来修饰,这些方法即为模板方法

public abstract class OrderProcessTemplate {
    public boolean isGift;
    public abstract void doSelect();
    public abstract void doPayment();
    public abstract void doDelivery();
    public final void processOrder() {}
}
public class NetOrder extends OrderProcessTemplate {
    @Override
    public void doSelect() {}
    @Override
    public void doPayment() {}
    @Override
    public void doDelivery() {}
}

迭代器(Iterator)
将集合类的迭代操作委托给迭代器来实现。
让自己的集合类实现 Iterable 接口,并实现自己的独特 Iterator 迭代器 (hasNext, next, remove) ,允许客户端利用这个迭代器进行显式或隐式的迭代遍历:

Visitor模式
把类中的某些功能委托给别人实现(实现功能时要反过来用到原来的类)

public interface ItemElement {
	public int accept(ShoppingCartVisitor visitor);
}
public class Book implements ItemElement{
    private double price;
    ...
    int accept(ShoppingCartVisitor visitor) {
    	visitor.visit(this);
    }
}
public class Fruit implements ItemElement{
    private double weight;
    ...
    int accept(ShoppingCartVisitor visitor) {
        visitor.visit(this);
    }
}

Visitor vs Iterator

迭代器:以遍历的方式访问集合数据而无需暴露其内部表示,将“遍历”这项功能delegate到外部的iterator对象。
Visitor:在特定ADT上执行某种特定操作,但该操作不在ADT内部实现,而是delegate到独立的visitor对象,客户端可灵活扩展/改变visitor的操作算法,而不影响ADT
迭代器和Vistor模式结构相同,只是方法不同(本质上无区别)

Strategy vs visitor

自己的独特 Iterator 迭代器 (hasNext, next, remove) ,允许客户端利用这个迭代器进行显式或隐式的迭代遍历:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值