软件复用
最常见的两种方式:=继承和组合
组合提供了一个利用已存在的软件组件来创建新的应用程序的办法。是Hasa关系
组合中类的关系是在运行时才确定的,即在对象没有创建运行前,整体类是不会知道自己将持有特定借口下的哪个实现类。
组合有助于保持每个类被封装,并被集中在单个任务上。是大型系统软件实现即插即用的首选方法。
继承不多讲了,参考06-多态。
继承允许我们根据自己的实现来覆盖重写父类的实现细节,父类的实现对于子类是可见的。称为白盒复用。
组合的整体类和部分类之间不会去关心各自的实现细节,即它们之间的细节是不可见的,称为黑盒复用。
框架
对一类相似问题的骨架解决方案。
由于框架必须确保一个类的所有实例都对相关的信息作出合适的响应,因此延迟方法做为父类定义的一部分
框架需要的不只是一个类,一个框架通常是由大量的可以协同工作的类组成的。
//抽象类——插入排序
abstract class InsertionSorter{
public abstract int size(); // abstract methods
public abstract boolean lessThan(int i, int j);
public abstract void swap(int i, int j);
public void sort () {
int n = size();
for (int i = 1; i < n; i++) {
int j = i - 1;
while (j >= 0 && lessThan(j+1, j)) {
swap(j, j+1);
j = j - 1;
}
}
}
}
class EmployeeSorter extends InsertionSorter {
Employee data[];
int size;
public EmployeeSorter(Employee d[], int n) {
data = d;
size = n;
}
public int size() {
return size;
}
public boolean lessThan(int i, int j) {
return data[i].startingYear < data[j].startingYear;
}
public void swap(int i, int j) {
Employee temp = data[i];
data[i] = data[j];
data[j] = temp;}
}
public void swap(int i, int j) {
Employee temp = data[i];
data[i] = data[j];
data[j] = temp;}
}
public class TestSorter {
public static void main(String[] args) {
Employee e []= {new Employee("张三", 2000, 2000),new Employee("李四", 3000, 1995),new Employee("王二", 2500, 1994)};
EmployeeSorter es=new EmployeeSorter(e,3);
es.sort();
for(int i=0; i<3;i++)
e[i].printEmployee();
}
}
基类不再需要改变。
特化子类满足不同的需求。
对象互连
一种考虑对象互连的方式就是研究可视性和依赖性这两个概念。
可视性描述了关于名称的特性,若通过该名称句柄可以存取对象,且对象的名称是合法的且代表该对象,那么在这个特定环境下该对象就是可见的。
依赖性将两个对象或者类联系起来,在不存在另外一个对象的条件下,如果一个对象的存在无任何意义,就说该对象依赖于另外那个对象。
耦合(coupling)和内聚(cohesion)的思想提供了一个框架,用于评价对象和类的应用是否有效。
耦合描述类之间的关系,内聚描述类内部的关系。
耦合性是指在一个软件结构中不同模块之间相互依赖的强弱程度。
-
内部数据耦合发生在当一个类的实例直接修改另外一个类中的本地数据值(实例变量)时。
-
全局数据耦合发生在两个或者更多个类型都依赖于公用的全局数据结构而绑定到一起的时候
-
组件耦合发生在一个类包含的数据字段或数值为另外一个类的实例时
-
参数耦合发生在一个类必须调用另外一个类的服务和例程时,此时两个类之间所发生的唯一关系就是一个类需要为另一个类提供参数数目、类型和返回值类型。
-
子类耦合是面向对象编程所特有的,描述了一个类与其父类之间的关系。通过继承,子类的实例可以被看成父类的实例。
类的(内部)内聚性是该结构中各个元素之间绑定程度的量度。
迪米特法则:通过限制对象之间的互连来减少其耦合度。
对于被依赖的类来说,尽量地将逻辑封装在类的内部,对外除了提供public方法,不对外泄漏任何信息。
朋友:只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。
直接的朋友:耦合的方式有很多种,称出现在成员变量、方法参数、方法返回值中的类为直接的朋友
狭义的迪米特法则:如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一类的某一个方法的话,可以通过第三者转发这个调用。
广义迪米特法则:一个模块设计的好坏的一个重要标志就是该模块在多大程度上将自己的内部数据与实现的有关细节隐藏起来。尽可能少地与其他实体发生相互作用。
迪米特法则的目的在于降低类与类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,使得相互间存在尽可能少的依赖关系。
迪米特法则弊端:迪米特法则是一种面向对象系统设计风格的一种法则,尤其适合做大型复杂系统设计指导原则。
过度使用迪米特法则,也会造成系统的不同模块之间的通信效率降低,使系统的不同模块之间不容易协调等缺点。
同时,因为迪米特法则要求类与类之间尽量不直接通信,如果类之间需要通信就通过第三方转发的方式,这就直接导致了系统中存在大量的中介类,这些类存在的唯一原因是为了传递类与类之间的相互调用关系,这就毫无疑问的增加了系统的复杂度。
UML
这一部分只需要知道在设计模式中需要用到的部分即可。
类:具有属性和方法
接口:
使用对象类的图形表示方法,接口名前➕interface
重点在类图:
关系的表示:
泛化:
带空心箭头的实线,指向父类 / 父接口
实现:
类实现接口,用空心箭头和虚线表示,指向接口。
关联:
是类和对象间连接的描述。HASA关系
即被关联B类 以成员变量 的形式出现在关联类A 中。
用 实线+箭头 表示,指向被关联元素B。
关联可以是双向(不需加箭头),也可以是单向(指向成员变量的那个类)。
依赖:
是一种使用关系,表现为函数中的参数(use a)、或局部变量、或静态方法的调用。
用虚线+箭头表示,指向被依赖的一方。
聚合:
表示对象之间部分和整体之间的关系,但关系比较松散。
用空心菱形+实线+箭头表示,菱形头指向整体,箭头指向部分。
在聚合关系中,成员对象是整体对象的一部分,但成员对象可以脱离整体对象而存在。
组合:
表示对象之间部分和整体的关系,但关系很紧密。
用实心菱形 + 实线 + 箭头表示,指向部分。
组合关系中整体对象控制成员对象的生命周期,一但整体对象不存在,成员对象也就不存在了。“同生共死”