【软件构造】软件构造复习总结3-第四、五章部分

第四五章主要复习了各种设计模式。

第四章

一、可复用性的度量、形态学和外部观察

1.源代码复用;模块复用(类或接口);库复用(API);系统复用(框架)。

2.白盒框架:通过继承和动态绑定实现可扩展性,通过继承框架基类并重写预定义的钩子方法来扩展现有功能

3.黑盒框架:通过定义符合特定接口的组件来重用现有功能,这些组件通过委托与框架集成

二、面向复用的软件构造技术

1.LSP:在任何父类型可一应用的场景,子类型都可以应用。

重写的方法可以有相同的前置和后置条件。也可以有更弱的前置条件或更强的后置条件。

lsposed仓库一直加载_java


2.协变:父类型->子类型:越来越具体specific 返回值类型:不变或变得更具体异常的类型:也是如此。如:

lsposed仓库一直加载_子类_02


逆变:父类型->子类型:越来越具体specific 参数类型:要相反的变化,要不变或越来越抽象。如:

lsposed仓库一直加载_设计模式_03


3.泛型中的LSP:ArrayList是List的子类,ArrayList不是List的子类。

例.

static long sum(List<Number> list) {
		long sum = 0;
		for(Number number : list) {
			sum+= number.longValue();
		}
		return sum;
}
public static void main(String[] args){
		List<Integer> list = List.of(1,2,3);
		System.out.println(sum(list));
}

Sum会报错:

lsposed仓库一直加载_java_04


不可以认为List是List的子类进行传入。

4.<?>通配符:使用例子:

lsposed仓库一直加载_java_05

lsposed仓库一直加载_List_06

客户端:

public static void main(String[] args){
		List<Integer> li = Arrays.asList(1, 2, 3); 
		List<String>  ls = Arrays.asList("one", "two", "three"); 
		printList(li); 
		printList(ls);
}

printList的目标是打印任何类型的列表,但是它无法实现这个目标-它只打印对象实例的列表;它不能打印list、list和list等等,因为它们不是list的子类型。

要编写通用的printList方法,使用List<?>。

控制台:

lsposed仓库一直加载_java_07


5.<? super A>与 <? extends A> :如:

lsposed仓库一直加载_java_08


前者只匹配整数类型的列表,而后者匹配整数超类型(如Integer、Number和Object)的任何类型的列表。

5.Comparator<…>接口,如果ADT需要比大小可以实现该接口并override compareTo(Object o)或compare(Object o1, Object o2)函数。

可以使用Collections.sort(this.o);进行排序

6.委派(delegation):一个对象请求另一个对象的功能。

7.CRP(复合重用原则):类应该通过组合所需功能,而不是通过继承来实现多态和复用。重点理解:

lsposed仓库一直加载_设计模式_09


8.各种委派:

a.Dependency: 临时性的delegation,如:

lsposed仓库一直加载_List_10


b.Association: 永久性的delegation,如:

lsposed仓库一直加载_lsposed仓库一直加载_11


c. Composition: 更强的association,但难以变化,如:

lsposed仓库一直加载_List_12


d.Aggregation: 更弱的association,可动态变化,如:

lsposed仓库一直加载_设计模式_13


三、面向复用的设计模式

1.Structural patterns 结构型模式:处理类或对象的组合。

a.Adapter适配器模式:将某个类/接口转换为client期望的其他形式.

通过增加一个接口,将已存在的子类封装起来,client面向接口编程,从而隐藏了具体子类。

lsposed仓库一直加载_设计模式_14


b. Decorator装饰器模式:为对象增加不同侧面的特性.

对每一个特性构造子类,通过委派机制增加到对象上

lsposed仓库一直加载_lsposed仓库一直加载_15


例子类比:

lsposed仓库一直加载_子类_16


Decorator通过delegate到stack完成基本操作,在decorator基础上通过继承增添新的功能。

c. Facade外观模式:客户端需要通过一个简化的接口来访问复杂系统内的功能。

提供一个统一的接口来取代一系列小接口调用,相当于对复杂系统做了一个封装,简化客户端使用。

2.Behavioral patterns 行为类模式:描述类或对象交互和分配责任的方式。

a. Strategy策略模式:为不同的实现算法构造抽象接口,利用delegation,运行时动态传入client倾向的算法类实例。

lsposed仓库一直加载_设计模式_17


b.Template Method模板模式:共性的步骤在抽象类内公共实现,差异化的步骤在各个子类中实现。

lsposed仓库一直加载_子类_18


与白盒框架类似。

c. Iterator迭代器:客户端希望遍历被放入容器/集合类的一组ADT对象,无需关心容器的具体类型。

lsposed仓库一直加载_java_19


主要实现方式:两个接口,Iterable和Iterator。

lsposed仓库一直加载_设计模式_20

lsposed仓库一直加载_设计模式_21

自己的类实现Iterable接口,并创造一个新类实现迭代器。

遍历:

lsposed仓库一直加载_java_22


以实验为例,实验中让实现board的迭代。

首先创造一个BoardIterator实现Iterator接口

public class BoardIterator implements Iterator<PlanningEntry<Resource>>{
	private PlanningEntryCollection pec;
	private int count = 0;
	
	// Abstraction function:
    //	 AF(BoardIterator)={pec , count| pec映射是一系列计划项,count映射是第几个计划项}
    // Representation invariant:
    //   nothing
    // Safety from rep exposure:
    //   所有变量都是private的
    //
	
	/**
	 * BoardIterator的构造器
     * @param pec 一系列计划项
	 * 
	 */
	public BoardIterator(PlanningEntryCollection pec) {
		this.pec = pec;
		Collections.sort(this.pec.getPes());
	}
	
	/**
	 * 
     * @return boolean
	 * 
	 */
	@Override
	public boolean hasNext() {
		return count < pec.getPes().size();
	}

	/**
	 * 
     * @return PlanningEntry<Resource>
	 * 
	 */
	@Override
	public PlanningEntry<Resource> next() {
		PlanningEntry<Resource> n = pec.getPes().get(count);		
		count++;
		return n;
	}

	/**
	 * 
	 * 
	 */
	@Override
	public void remove() {
		throw new UnsupportedOperationException();
	}
}

接下来Board实现Iterable接口

public abstract class Board implements Iterable<PlanningEntry<Resource>>{
	private PlanningEntryCollection pec; 
	
	// Abstraction function:
    //	 AF(Board)={pec | pec映射是一系列计划项}
    // Representation invariant:
    //   nothing
    // Safety from rep exposure:
    //   所有变量都是private的
//
......
......
/**
	 * 迭代器
     * @return Iterator<PlanningEntry<Resource>>
     */
	@Override
	public Iterator<PlanningEntry<Resource>> iterator() {
		return new BoardIterator(pec);
	}
}

第五章

一、高可维护性的度量与构造原则

1.高内聚,低耦合:

lsposed仓库一直加载_设计模式_23


2.SOLID原则:

a.SRP(单一责任原则):不应有多于1个的原因使得一个类发生变化;一个类,一个责任。

b.OCP((面向变化的)开放/封闭原则):

对扩展性的开放:模块的行为应是可扩展的,从而该模块可表现出新的行为以满足需求的变化。

对修改的封闭:模块自身的代码是不应被修改的;扩展模块行为的一般途径是修改模块的内部实现;如果一个模块不能被修改,那么它通常被认为是具有固定的行为。

用抽象方法解决。同时解决了(if/else/switch太多维护困难的问题)

c.LSP(Liskov替换原则):同第四章。

d.ISP(接口隔离原则):客户端只访问自己所需要的端口。

反面例子:

lsposed仓库一直加载_java_24


修改后:

lsposed仓库一直加载_子类_25


e.DIP(依赖转置原则):抽象的模块不应依赖于具体的模块;具体应依赖于抽象。

二、面向可维护性的设计模式

1.工厂方法:

正常客户端:Product p = new ProductTwo();//通过new一个新的对象

工厂方法下:Product p = new ConcreteTwo().makeObject();//通过一个类中的方法创建对象。不暴露内部类的名字。

工厂方法:可以在ADT中也可以单独构建一个类:

lsposed仓库一直加载_设计模式_26


Abstract Factory抽象工厂:把多个工厂方法组合到一起。

2. Proxy代理模式:隔离对复杂 对象的访问,降低难度/代价,定位在“访问/使用行为”

lsposed仓库一直加载_lsposed仓库一直加载_27


3.Observer模式:建立对象间一对多的情况。如偶像更新微博,所有的observer自动打印更新的行为。observer关注偶像,偶像的ADT自动将observer加入关注者名单。

4.Visitor模式:对特定类型的object的特定操作(visit),在运行时将二者动态绑定到一起,该操作可以灵活更改,无需更改被visit的类。

特点:在特定ADT上执行某种特定操作,但该操作不在ADT内部实现,而是delegate到独立的visitor对象,客户端可灵活扩展/改变visitor的操作算法,而不影响ADT。

三、面向可维护性的构造技术

1.State模式:state作为一个接口,有不同的实现类,在别的ADT执行完操作通过delegation到具体的state的实现类自动完成状态转换。

lsposed仓库一直加载_设计模式_28


2.正则表达式:

lsposed仓库一直加载_List_29


剩下具体使用在另一篇博客有稍微详细一点的复习。

/***********************************************************************************/
以上即为软件构造复习总结3-第四、五章部分。