软件构造复习要点(二)

Levels reusable components

源代码级别的复用

– Requirements 需求

– Design and specifications 设计/规约spec

– Data 数据

– Test cases 测试用例

– Documentation 文档


白盒复用:源代码可见,可修改和扩展

复制已有代码当正在开发的系统,进行修改

可定制化程度高

黑盒复用:源代码不可见,不能修改

只能通过API接口来使用,无法修改代码

简单,清晰

适应性差些

模块级别的复用:类/抽象类/接口

Approaches of reusing a class:

inheritance继承

delegation委托

库级别的复用:API/包

系统级别的复用:框架

框架:一组具体类、抽象类、及其之间的连接关系

白盒框架,通过代码层面的继承进行框架扩展

黑盒框架,通过实现特定接口/delegation进行 框架扩展

External observations of reusability

1. Type Variation 类型可变

泛型

2. Routine Grouping 功能分组

提供完备的细粒度操作,保证功能的完整性,不同场景下复用不同的 操作(及其组合)

3. Implementation Variation 实现可变

ADT有多种不同的实现,提供不同的representations和 abstract function,但具有同样的specification (pre-condition, postcondition, invariants),从而可以适应不同的应用场景

4. Representation Independence 表示独立

内部实现可能会经常变化,但客户端不应受到影响。

表示独立性、信息隐藏

5. Factoring Out Common Behaviors 共性抽取

将共同的行为(共性)抽象出来,形成可复用实体:父类、抽象类

Behavioral subtyping/LSP原则

Compiler-enforced rules in Java (static type checking)

Subtypes can add, but not remove methods 子类型可以增加方法,但不可删

Concrete class must implement all undefined methods 子类型需要实现抽象 类型中的所有未实现方法

Overriding method must return same type or subtype 子类型中重写的方法 必须有相同或子类型的返回值或者符合co-variance的参数

Overriding method must accept the same parameter types 子类型中重写的 方法必须使用同样类型的参数或者符合contra-vshuofaariance的参数**(java不支持参数反协变,会当作重载)**

Overriding method may not throw additional exceptions 子类型中重写的方 法不能抛出额外的异常


LSP原则

前置条件不能强化

后置条件不能弱化

不变量要保持

子类型方法参数:逆变

子类型方法的返回值:协变

异常类型:协变

Covariance (协变)

返回值类型 & 异常的类型:不变或变得更具体

Contravariance (反协变、逆变)

参数类型:要相反的变化,要不变或越来越抽象**(java不支持参数反协变,会当作重载)**

应用中的例子

class Car{
}
class SmallCar extends Car{
}

public static void test(Car c) {
	System.out.print("car");
}
public static void test(Object c) {
	System.out.print("object");
}
public static void main(String []args)
{
   	test(new SmallCar());
}

**console :car **

Generics 泛型

LSP与泛型

ArrayList<String>List<String>的子类

List<String>不是List<Object>的子类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kw6bTY2J-1596861105052)(https://i.loli.net/2020/06/13/SauAgL2Vv3bilWh.png)]

通配符

<? super Integer> 可以接受 : Integer, Number, and Object

<? extends Number>可以接受:Number和number的子类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cpoC8CVP-1596861105059)(https://i.loli.net/2020/06/13/DjAfa2lzXCT64Sm.png)]

Delegation and Composition

Composite Reuse Principle (CRP)

在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,两者都是开闭原则的具体实现规范。

继承我们叫做白箱复用,相当于把所有的实现细节暴露给子类。

组合/聚合也称之为黑箱复用,对类以外的对象是无法获取到实现细节的。

Types of delegation

Dependency:临时性的delegation

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r2l7iKU3-1596861105060)(https://i.loli.net/2020/06/13/RAb327WZJUo4Ffz.png)]

Association: 永久性的delegation

Composition(组合):更强的association,但难以变化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R4wxmSZA-1596861105063)(https://i.loli.net/2020/06/13/xTW2l71ImZd5tMB.png)]

Aggregation(聚合): 更弱的association,可动态变化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aMQ8t7eD-1596861105065)(https://i.loli.net/2020/06/13/q7lkO2r94ztxb1F.png)]

Design Patterns for Reuse

Structural patterns

Adapter

将某个类/接口转换为client期望的其他形式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Gw1Vtmz-1596861105067)(https://i.loli.net/2020/06/18/5H7vGZbglQWieuK.png)]

Decorator

当需要特性的任意组合的时候,可以用装饰器模式

subtyping + delegation

抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类
装饰角色(Decorator):持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口
具体装饰角色(ConcreteDecorator):负责给构件对象“贴上”附加的责任

Decorator

举个例子:

咖啡是一种饮料,咖啡的本质是咖啡豆+水磨出来的。咖啡店现在要卖各种口味的咖啡,如果不使用装饰模式,那么在销售系统中,各种不一样的咖啡都要产生一个类,如果有4中咖啡豆,5种口味,那么将要产生至少20个类(不包括混合口味),非常麻烦。使用了装饰模式,只需要11个类即可生产任意口味咖啡(包括混合口味)。

coffee

public interface Beverage {
	//返回商品描述
	public String getDescription();
	//返回价格
	public double getPrice();
}
//具体被装饰的对象类1
public class CoffeeBean1 implements Beverage {
	private String description = "选了第一种咖啡豆";
	@Override
	public String getDescription() {
		return description;
	}
	@Override
	public double getPrice() {
		return 50;
	}
}
//具体被装饰的对象类2
public class CoffeeBean2 implements Beverage { 
	private String description = "第二种咖啡豆!";
	@Override
	public String getDescription() {
		return description;
	}
	@Override
	public double getPrice() {
		return 100;
	}
}
//装饰器
public class Decorator implements Beverage {
	private String description = "我只是装饰器,不知道具体的描述";
	@Override
	public String getDescription() {
		return description;
	}
	@Override
	public double getPrice() {
		return 0;		//价格由子类来决定
	}
}
public class Milk extends Decorator{
	private String description = "加了牛奶!";
	private Beverage beverage = null;
	public Milk(Beverage beverage){
		this.beverage = beverage;
	}
	public String getDescription(){
		return beverage.getDescription()+"\n"+description;
	}
	public double getPrice(){
		return beverage.getPrice()+20;	//20表示牛奶的价格
	}
}
public class Mocha extends Decorator {
	private String description = "加了摩卡!";
	private Beverage beverage = null;
	public Mocha(Beverage beverage){
		this.beverage = beverage;
	}
	public String getDescription(){
		return beverage.getDescription()+"\n"+description;
	}
	public double getPrice(){
		return beverage.getPrice()+49;	//30表示摩卡的价格
	}
}
public class Soy extends Decorator {
	private String description = "加了豆浆!";
	private Beverage beverage = null;
	public Soy(Beverage beverage){
		this.beverage = beverage;
	}
	public String getDescription(){
		return beverage.getDescription()+"\n"+description;
	}
	public double getPrice(){
		return beverage.getPrice()+30;	//30表示豆浆的价格
	}
}

//test
public class Test {
 
	public static void main(String[] args) {
		Beverage beverage = new CoffeeBean1();	//选择了第一种咖啡豆磨制的咖啡
		beverage = new Mocha(beverage);		//为咖啡加了摩卡
		beverage = new Milk(beverage);		//为咖啡加了牛奶
		System.out.println(beverage.getDescription()+"\n加了摩卡和牛奶的咖啡价格:"+beverage.getPrice());
		
	}
}
//结果
选了第一种咖啡店
加入了摩卡!
加入了牛奶!
加了摩卡和牛奶的咖啡价格:119.0    

重要的一点补充:

装饰模式对客户端的透明性要求程序不要声明一个ConcreteComponent类型的变量,而应当声明一个Component类型的变量。

用上面的例子来说,必须永远把所有的饮料当成饮料来对待,而如果把饮料变成的加摩卡的饮料当成摩卡,而不是饮料,这是不应当发生的。

Adapter和Decorator的关系

装饰模式和适配器模式都是“包装模式(Wrapper Pattern)”,它们都是通过封装其他对象达到设计的目的的。

区别:

理想的装饰模式在对被装饰对象进行功能增强的同时,要求具体构件角色、装饰角色的接口与抽象构件角色的接口完全一致。

适配器模式并不要求对源对象的功能进行增强,但是会改变源对象的接口,以便和目标接口相符合。

拓展:

并且,装饰模式有透明和半透明两种,这两种的区别就在于装饰角色的接口与抽象构件角色的接口是否完全一致。

透明的装饰模式也就是理想的装饰模式。半透明的装饰器模式如下图:

透明,半透明,不透明

Facade

客户 端需要通过一个简化的接口来访问复杂系统内的功能

Behavioral patterns

Strategy

为不同的实现算 法构造抽象接口,利用delegation,运行时动态传入client倾向的算法 类实例

Strategy

Template

共性的步骤在抽象类内公共实现,差 异化的步骤在各个子类中实现

inheritance + overridable

Template

Iterator

对放入集合类里的ADT提供统一的遍历方法

Iterator 迭代器接口
ConcreateIterator 具体实现类
Aggregate 集合类接口
ConcreteAggregate 具体的集合类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XRxSAfWV-1596861105074)(https://i.loli.net/2020/06/18/kXBmFbxuvzgCZRJ.png)]

好处:

一个重要的原因,引入 Iterator 后可以将遍历与实现分离出来。

不论具体集合类如何实现(数组or链表),下面的遍历代码都不用改动

while (iterator.hasNext()) {
    System.out.println(iterator.next().getName()); 
}

Modular programming

High cohesion within modules 高内聚

Loose coupling between modules 低耦合

Functions are assigned to modules in away that groups similar functions together (Separation of concerns) 分离关注点

There are small, simple, well-defined interfaces between modules (Information hiding) 信息隐藏

Five Criteria for Evaluating Modularity

Decomposability (可分解性)

Composability (可组合性)

Understandability (可理解性)

Continuity (可持续性) ——发生变化时受影响范围最小

Protection (出现异常之后的保护) ——出现异常后受影响范围最小

Five Rules of Modularity Design

Direct Mapping (直接映射)

Few Interfaces (尽可能少的接口)

Small Interfaces (尽可能小的接口)

Explicit Interfaces (显式接口)

Information Hiding (信息隐藏)

Coupling and Cohesion(耦合与内聚)

举例:Coupling between HTML, CSS and JavaScript

A well-designed web app modularizes around:

– HTML files which specify data and semantics

– CSS rules which specify the look and formatting of HTML data

– JavaScript which defines behavior/interactivity of page

OO Design Principles: SOLID

(SRP) The Single Responsibility Principle 单一责任原则

(OCP) The Open-Closed Principle 开放-封闭原则

(LSP) The Liskov Substitution Principle Liskov替换原则

(DIP) The Dependency Inversion Principle 依赖转置原则

(ISP) The Interface Segregation Principle 接口聚合原则

具体介绍可以看我之前的博客------可维护性的软件构造原理

未完待续。。。

try-catch-finally

	public static boolean test() {
		try {
			return true;
		}finally {
			return false;
		}
	}
    public static void main(String []args)
    {
    	System.out.print(test());
    }

return false

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值