软件构造复习笔记(4)——第四章

一、可复用性的度量,形态与外部表现

1、软件复用分类

  • 面向复用编程:开发出可复用软件
  • 基于复用编程:利用已有复用软件搭建应用系统

模块越小,复用范围越大

2、复用层次

(1) 代码层面:最主要

  • 白盒复用:源代码可见,可修改,可扩展
    典型做法:复制已有代码
  • 黑盒复用:源代码不可见,不能修改
    典型做法:通过API接口来使用

(2)模块层面的复用

  • 继承inheritance
    委托delegation

(3)API/Package层面的复用

  • 远程端API
    开发者调用可复用库(API)

(4) Framework框架复用:

  • 一组类,抽象类,及之间的连接关系
  • Framework预留接口,开发者根据Framework规约填充自己的代码
  • Framework调用开发者所写的程序
  • 白盒框架:通过代码层面的继承进行框架扩展
    黑盒框架:通过实现特定接口/delegation进行框架扩展

library 和 Framework 为系统层面的复用

(3)任何实体都可以复用

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

1、LSP(Liskov 替换准则)

(1)子类型多态

  • 客户端可以使用统一的方式处理不同类型的对象
  • 子类型的变量可以代替父类型的变量
    任何使用a的场景都可以用c1和c2代替:
    Animal a = new Animal();
    Animal c1 = new Cat();
    Cat c2 = new Cat();

(2) 规则:

  • 子类型只能增加方法,但不能删除方法
  • 子类型必须实现父类型中所有未实现方法
  • 子类型中重写的方法,参数类型与父类相同(或符合逆变 co-variance,即为其父类型);返回值类型与与父类型相同,或为父类型返回值的子类型
  • 子类型中重写的方法不能抛出额外异常。异常:协变
  • 更强的不变性RI(不变量要保持),前置条件变弱,后置条件变强
    如果父类型是不可变的,则所有子类型也应为不可变的

协变:返回值类型,异常类型变为原来的子类型
逆变:参数类型变为原来的父类型
Java不支持逆变,java遇到逆变看成overload

(3)泛型中的LSP

  • 泛型不支持协变
    List<Integer> myInts = new ArrayList<>();
    Number为Integer的父类型
    List<Number> myNum = myInts; // 错误!!
  • 通配符?,表示任何类型的父类型
    public static void printList(List<?> list){…}
    List l1 = Arrays.asList(1,2,3);
    printList(l1); // 可以

2、委派和组合

(1)委派

  • 一个对象请求实现另一个对象的功能

(2)委派和继承区别

  • 委派不用该类型的所有方法;发生在object层面
    继承要用该类型的所有方法:发生在class层面

(3)委派的分类

  • 临时性的委派:依赖关系 ,UML表示----->
    在类的某个方法中,参数为另一个类,方法内操作调用这个参数类的方法
  • 永久性委派:关联关系,UML表示——>
    另一个类为该类的一个属性
    class Duck{
    Flyable f = new CannotFly();;
    public Duck(Flyable f){
    this.f = f;
    }
    }
  • 更强的委派
    class Duck{
    Flyable f = new FlyWithWings();
    public Duck(){
    }
    public fly(){
    this.f.fly();
    }
    }
  • 更弱的委派,可动态变化
    class Duck{
    Flyable f ;
    public Duck(Flyable f){
    this.f = f;
    }
    }

3、Framework设计

  • 典例:程序扩展
  • 白盒框架:继承关系 override
    黑盒框架:delegation

三、面向复用的设计模式

(一)结构型模式

1、适配器模式 Adapter

  • 将某个类/接口转化成客户期望的其他形式,通过增加一个接口,将已有的子类封装起来,客户面向该接口编程

2、装饰器模式 Decorator

  • 每个子类实现不同特征,特征的任意组合
    使用继承会造成组合爆炸,大量代码重复
  • 为对象增加不同侧面的特征,每个特征构造子类,通过委派增加到对象上
    具体代码例子:
/*
* ADT接口定义
 */
public interface Stack{
	public void push(Item e);
	public Item pop();
}

/*
* 共性/最基本功能实现
*/
public class ArrayStack implements Stack{
......
	@Override
	public void push(Item e){
		操作...
	}
	
	@Override 
	public Item pop(){
		操作...
	}
}
/*
* 增加一个用于decorator 的基础抽象类
*/
public abstract class StackDecorator implements Stack{
	protected final Stack stack; // 只能子类使用,且为final
	public StackDecorator(Stack stack){
		this.stack = stack;  // 委派
	}
	@Override
	public void push(Item e){
		this.stack.push(e);
	}
	
	@Override 
	public Item pop(){
		this.stack.pop();
	}
}
/*
* 具体实现类,其中可以在原来的共性方法上增加新特性
* 可以增加新方法
*/
public class UndoStack extends StackDecorator implements Stack{
	private final Undolog log = new Undolog();
	public UndoStack(Stack stack){
		super(stack);
	}
	@Override
	public void push(Item e){
		log.append(e);        // 增加新的操作
		super.push(e);
	}
	// 增加新的方法
	public void undo(){
	 ...
	 }
}
/*
* 客户端使用,套马甲
*/
Stack t = new UndoStack(new ArrayStack());
Stack t1 = new SyschesStack(new UndoStack(new ArrayStack())); 

3、外观模式 Facade

  • 客户端通过一个简化的接口静态方法来访问复杂系统内的功能。对复杂系统做封装

(二)行为类模式

1、策略模式 Strategy

  • 一个任务有不同算法,客户端根据需要切换动态算法
    为不同算法构造抽象接口,利用委派,运行时动态传入
  • 例子:两种不同结算方式
    在这里插入图片描述
    方式1:
    在这里插入图片描述
    方式2:
    在这里插入图片描述
    支付行为类:
    在这里插入图片描述
    客户端:
    在这里插入图片描述

2、模板模式 Template Method

  • 做事情的步骤一样,但具体方法不一样
  • 使用继承和重写实现
    在这里插入图片描述

3、Iterator 迭代器模式

(1)Iterable 接口:实现接口的集合对象是可迭代遍历的

// 需要自己写的方法
public interface Iterable<T>{
	Iterator<T> iterator();     //接口:迭代器
}

public interface Iterator<E>{
	public boolean hasNext();
	public E next();
	public void remove();
}
  • 构造自己的可迭代类:让自己的集合类实现Iterable接口,并实现自己的独特Iterator迭代器(hasNext, next, remove)
public class Pair<E> implements Iteratable<E>{
	private final E first, second;
	public Pair(E f, E s) { first = f; second = s; }
	@Override
	public Iterator<E> iterator(){
		return new PairIterator();
	}
	private class PairIterator implements Iterator<E>{
		private boolean seenFirst = false, seenSecond = false;	
		@Override
		public boolean hasNext() { return !seenSecond; }
		@Override
		public E next() {
			if(!seenFirst){seenFirst = true; return first;}
			if (!seenSecond) { seenSecond = true; return second;}
			throw new NoSuchElementException();
		}
		@Override
		public void remove(){
			throw new UnsupportedOperationException();
		}
	}
}
//此时,可以按照属性遍历类Pair
Pair<String> pair = new Pair<String>("foo", "bar");
for (String s : pair) { ... }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值