【菜鸟日记】软件构造笔记5.2(详细版)

5.2.1 设计可复用的类

设计可复用的类的方法

  1. 继承和重写(3.4中有讲过)

多态和重载

(a)多态的概念:多态性是指为不同类型的实体提供一个接口,或者使用一个符号来表示多个不同的类型。多态性分为子类型多态性、参数多态性、特殊多态性、LSP(李氏替代原则)和行为子类型多态性
(b)常见的多态的例子:一个函数可以有多个同名的实现(方法重载);一个类型名字可以代表多个类型(泛型编程);一个变量名字可以代表多个类的实例(子类型)
(c)重载的规则:1)子类型可以添加方法,但不能删除方法;2)concrete类中必须实现所有未定义的方法;3)重写方法必须返回相同的类型或者子类型;4)重写方法参数类型必须和之前的相同。如:重写equal(Object o) 不能修改Object,修改后只能算重载,不算重写。5)重写可能不会引起其他的异常。
(d)LSP:相同或者更强的不变量;所有方法相同或者更强的后置条件;所有方法相同或者更弱的前置条件

【菜鸟日记】软件构造笔记5.2(详细版)
(e)型变:型变是指当子类型关系出现在更加复杂类型中时,如何处理新类型中的子类型关系。即输入输出类型可能会发生改变。型变分为协变、逆变和不变。

【菜鸟日记】软件构造笔记5.2(详细版)
【菜鸟日记】软件构造笔记5.2(详细版)
(f)java中的泛型是不型变的:如 ArrayList 是 List的子类 – List 不是 List的子类。但是 List 是 List<? extends Animal>的子类, List 也是List<? super Cat>的子类。
(g)泛型:虚拟机中没有泛型类型对象,所有对象都属于普通类。泛 型信息只存在于编译阶段,在运行时会被”擦除” 。定义泛型类型时,会自动提 供一个对应的原始类型(非泛型类型),原始类型的名字就是去掉类型参数后的 泛型类型名。类型变量会被擦除,替换为限定类型,如果没 有限定类型则为Object类型。

【菜鸟日记】软件构造笔记5.2(详细版)
【菜鸟日记】软件构造笔记5.2(详细版)
(h)泛型查询类的类型只能用getclass不能用instanceof

【菜鸟日记】软件构造笔记5.2(详细版)

为什么java的泛型编程不型变

【菜鸟日记】软件构造笔记5.2(详细版)
下面这张图更加直观

【菜鸟日记】软件构造笔记5.2(详细版)

关于extends和super与list

【菜鸟日记】软件构造笔记5.2(详细版)

【菜鸟日记】软件构造笔记5.2(详细版)
【菜鸟日记】软件构造笔记5.2(详细版)
【菜鸟日记】软件构造笔记5.2(详细版)

委托

定义:委托是指一个对象依赖于另外一个对象的部分功能 ,委托可看做是在实体之间共享代码和数据的低层机制,有利于复用,直白地说就是隐式地借用另一个类的方法来实现本类的功能。例如:
【菜鸟日记】软件构造笔记5.2(详细版)

【菜鸟日记】软件构造笔记5.2(详细版)

对比委托和继承(以栈为例子)

【菜鸟日记】软件构造笔记5.2(详细版)

关于委托的一个非常容易错的例子

这个咋看以为最后会打印A中的bar中的信息,其实不然,编译器会自动默认this是当前程序即B,所以调用的是B中的bar(),this的指代和常规思维不一样!!!!!
【菜鸟日记】软件构造笔记5.2(详细版)

关联关系

当定义一个变量时,会产生关联关系。
【菜鸟日记】软件构造笔记5.2(详细版)

依赖关系

当一个对象的实施需要借助其他对象的实施,例如
【菜鸟日记】软件构造笔记5.2(详细版)

组合和聚合

两者的差别:组合成员生命周期相同,而聚合成员生命周期无直接联系。如:大学与院系间是组合(若大学不复存在,则院系的存在也毫无意义),院系与教师间是聚合(院系不在了,老师还可以进入别的学院继续教授知识)

聚合的常见例子:注意,聚合用空心的菱形来表示

【菜鸟日记】软件构造笔记5.2(详细版)

组合的常见例子

当人去世后,心脏的生命周期也就结束了;当窗口关闭后,窗口上的菜单的生命周期也一样结束。

【菜鸟日记】软件构造笔记5.2(详细版)

对象之间的联系小结(其中聚合一般可以用关联关系替代)

【菜鸟日记】软件构造笔记5.2(详细版)

一个比较生动的图可以帮助理解

【菜鸟日记】软件构造笔记5.2(详细版)

5.2.2设计系统级可复用框架和库

设计 API

设计API一定要一开始就设计好,一旦发布,难以修改,因为可能会给别人带来很大影响,造成信任度降低。

什么样的API是好的API?

  • 功能单一
  • 命名不太长,但是意思清晰
  • 适合分解和合并模块
  • 既要体积小,但必要的性能不能丢

关于API类的设计

  • 尽量使用委托和组合
  • 不要因为单纯的复用实现而 使用继承,继承违反了封装原则

关于API方法的设计

  • 模块能做到的,就尽量不要客户端去做,减少模板代码的使用。
  • 要么正常结束,产生期望结果;要么整体失败,不产生任何的结果。例如copy,不要有copy一部分的情况,这样客户端无法判断实现情况,会造成未知的影响。
  • 对所有可访问数据提供String形式的访问方法,避免客户端去解析
  • 用空集合或者0长度数组, 不要用null(这样可以区分是越界还是本来就是空集合或者长度为0的数组)。
  • 尽量使用父接口,这样API更加灵活,比如尽量用Collection ,而不是list

设计框架

有框架尽量使用框架
在junit中的应用
【菜鸟日记】软件构造笔记5.2(详细版)

Java Collections 框架

【菜鸟日记】软件构造笔记5.2(详细版)

collection接口

【菜鸟日记】软件构造笔记5.2(详细版)

Iterator

【菜鸟日记】软件构造笔记5.2(详细版)

set

  • 增加了无重复元素的要求
  • 强制equals和hashCode计算

兼容问题

  • 向上兼容:向上兼容/向前兼容(forward),站在旧版软件 的立场,基于旧方法编写的软件不经修改就能在新版环境中运行
  • 向下兼容: 向后兼容/向下兼容(downward),站在新版 本的立场讨论对过去版本的兼容性问题,新版软件中可以支持旧版中 的方法