【菜鸟日记】软件构造笔记5.2(详细版)
文章目录
5.2.1 设计可复用的类
设计可复用的类的方法
- 继承和重写(3.4中有讲过)
多态和重载
(a)多态的概念:多态性是指为不同类型的实体提供一个接口,或者使用一个符号来表示多个不同的类型。多态性分为子类型多态性、参数多态性、特殊多态性、LSP(李氏替代原则)和行为子类型多态性
(b)常见的多态的例子:一个函数可以有多个同名的实现(方法重载);一个类型名字可以代表多个类型(泛型编程);一个变量名字可以代表多个类的实例(子类型)
(c)重载的规则:1)子类型可以添加方法,但不能删除方法;2)concrete类中必须实现所有未定义的方法;3)重写方法必须返回相同的类型或者子类型;4)重写方法参数类型必须和之前的相同。如:重写equal(Object o) 不能修改Object,修改后只能算重载,不算重写。5)重写可能不会引起其他的异常。
(d)LSP:相同或者更强的不变量;所有方法相同或者更强的后置条件;所有方法相同或者更弱的前置条件
(e)型变:型变是指当子类型关系出现在更加复杂类型中时,如何处理新类型中的子类型关系。即输入输出类型可能会发生改变。型变分为协变、逆变和不变。
(f)java中的泛型是不型变的:如 ArrayList 是 List的子类 – List 不是 List的子类。但是 List 是 List<? extends Animal>的子类, List 也是List<? super Cat>的子类。
(g)泛型:虚拟机中没有泛型类型对象,所有对象都属于普通类。泛 型信息只存在于编译阶段,在运行时会被”擦除” 。定义泛型类型时,会自动提 供一个对应的原始类型(非泛型类型),原始类型的名字就是去掉类型参数后的 泛型类型名。类型变量会被擦除,替换为限定类型,如果没 有限定类型则为Object类型。
(h)泛型查询类的类型只能用getclass不能用instanceof
为什么java的泛型编程不型变
下面这张图更加直观
关于extends和super与list
委托
定义:委托是指一个对象依赖于另外一个对象的部分功能 ,委托可看做是在实体之间共享代码和数据的低层机制,有利于复用,直白地说就是隐式地借用另一个类的方法来实现本类的功能。例如:
对比委托和继承(以栈为例子)
关于委托的一个非常容易错的例子
这个咋看以为最后会打印A中的bar中的信息,其实不然,编译器会自动默认this是当前程序即B,所以调用的是B中的bar(),this的指代和常规思维不一样!!!!!
关联关系
当定义一个变量时,会产生关联关系。
依赖关系
当一个对象的实施需要借助其他对象的实施,例如
组合和聚合
两者的差别:组合成员生命周期相同,而聚合成员生命周期无直接联系。如:大学与院系间是组合(若大学不复存在,则院系的存在也毫无意义),院系与教师间是聚合(院系不在了,老师还可以进入别的学院继续教授知识)
聚合的常见例子:注意,聚合用空心的菱形来表示
组合的常见例子
当人去世后,心脏的生命周期也就结束了;当窗口关闭后,窗口上的菜单的生命周期也一样结束。
对象之间的联系小结(其中聚合一般可以用关联关系替代)
一个比较生动的图可以帮助理解
5.2.2设计系统级可复用框架和库
设计 API
设计API一定要一开始就设计好,一旦发布,难以修改,因为可能会给别人带来很大影响,造成信任度降低。
什么样的API是好的API?
- 功能单一
- 命名不太长,但是意思清晰
- 适合分解和合并模块
- 既要体积小,但必要的性能不能丢
关于API类的设计
- 尽量使用委托和组合
- 不要因为单纯的复用实现而 使用继承,继承违反了封装原则
关于API方法的设计
- 模块能做到的,就尽量不要客户端去做,减少模板代码的使用。
- 要么正常结束,产生期望结果;要么整体失败,不产生任何的结果。例如copy,不要有copy一部分的情况,这样客户端无法判断实现情况,会造成未知的影响。
- 对所有可访问数据提供String形式的访问方法,避免客户端去解析
- 用空集合或者0长度数组, 不要用null(这样可以区分是越界还是本来就是空集合或者长度为0的数组)。
- 尽量使用父接口,这样API更加灵活,比如尽量用Collection ,而不是list
设计框架
有框架尽量使用框架
在junit中的应用
Java Collections 框架
collection接口
Iterator
set
- 增加了无重复元素的要求
- 强制equals和hashCode计算
兼容问题
- 向上兼容:向上兼容/向前兼容(forward),站在旧版软件 的立场,基于旧方法编写的软件不经修改就能在新版环境中运行
- 向下兼容: 向后兼容/向下兼容(downward),站在新版 本的立场讨论对过去版本的兼容性问题,新版软件中可以支持旧版中 的方法