面向对象 (Object-Oriented, OO) 是一种非常实用的系统化软件开发方法
面向过程和面向对象
以一个问题引入:把大象装进冰箱,需要几步?
一般先打开冰箱,然后把大象装进冰箱,最后关上冰箱
面向过程:
关心我该怎么做?一步步去实现这个功能
对于上述问题:
- 我打开冰箱
- 我把大象装进冰箱里
- 我关上冰箱门
面向对象
关心我该让谁去做?去调用对象的操作来实现这个功能
对于上述问题
创建对象:大象,冰箱
- 冰箱打开门
- 大象钻进冰箱
- 冰箱关上门
面向对象基础
面向对象 = 对象 + 分类 + 继承 + 通过消息的通信
可以说,采用这四个概念开发的软件系统是面向对象的
对象
客观世界由许多具体的事物、事件、概念和规则组成,这些均可被看成对象
在面向对象的系统中,对象是基本的运行时的实体,它既包括数据 (属性) ,也包括作用于数据的操作 (行为) 。一个对象通常可由对象名、属性和方法 3 个部分组成
消息
对象之间进行通信的一种构造叫做消息。的一个消息发送给某个对象时,包含要求接收对象去执行某些活动的信息。接收到信息的对象经过解释,然后予以相应
类似于方法调用的传参
类
一个类定义了一组大体上相似的对象。一个类所包含的方法和数据描述一组对象的共同行为和属性。把一组对象的共同特征加以抽象并存储在一个类中是面向对象技术最重要的一点。是否建立了一个丰富的类库,是衡量一个面向对象程序设计语言成熟与否的重要标志
类是在对象之上的抽象,对象是类的具体化,是类的实例。在分析与设计时,通常把注意力集中在类上,而不是具体的对象。也不必逐个定义每个对象,只需对类做出定义,而对类的属性进行不同赋值即可得到该类的对象实例
类可以分为三种:实体类、接口类 (边界类) 和控制类。控制类的对象用来控制活动流,充当协调者
有些类之间存在一般和特殊关系,即一些类是某个类的特殊情况,某个类是一些类的一般情况。特殊类是一般类的子类,一般类是特殊类的父类
通常,把一个类和这个类的所有对象称为 “类及对象” 或对象类
方法重载
方法重载方式
- 方法名相同,参数个数不同
- 方法名相同,参数类型不同
- 方法名相同,参数类型顺序不同
以 java 为例,java 的函数 (方法) 的格式如下
1
2
3
4
5
6
/*
权限修饰符 返回值类型 方法名(参数类型1 参数名1, 参数类型2 参数名2,···)
{
方法体
}
*/
以下为例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Test
{
// 原方法
public void sum(int a, double b)
{
System.out.println(a + b);
}
// 1. 方法名相同,参数个数不同
public void sum(int a, double b, int c)
{
System.out.println(a + b + c);
}
// 2. 方法名相同,参数类型不同
public void sum(int a, int b)
{
System.out.println(a + b);
}
// 3. 方法名相同,参数类型顺序不同
public void sum(double a, int b)
{
System.out.println(a + b);
}
}
面向对象三大特征
面向对象的三个基本特征是:封装、继承、多态
封装
封装是一种信息隐蔽技术,它的目的是使对象的使用者和生产者分离,使对象的定义和实现分开。从程序设计者来看,对象是一个程序模块;从用户来看,对象为他们提供了所希望的行为
也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Person{
private String name; // 使用 private 限制权限
private int age;
public void setName(String name){
// 通过 public 方法提供修改对象属性的途径
this.name = name;
}
public String getName(){
// 通过 public 方法提供获取对象属性的途径
return name;
}
public void setAge(int age){
if (age >= 0 && age <= 150)
this.age = age;
}
public int getAge(){
return age
}
public void run(){
System.out,println("润!")
}
}
继承
继承是父类和子类之间共享数据和方法的机制。这是类之间的一种关系,在定义和实现一个类的时候,可以在一个已经存在的父类的基础上进行,把这个已经存在的类所定义的内容作为自己的内容,并加入若干新的内容
一个父类可以有多个子类,这些子类都是父类的特例,父类描述了这些子类的公共属性和方法。一个子类可以继承它的父类 (或祖先类) 中的属性和方法,这些属性和操作在子类中不必定义,子类中还可以定义自己的属性和方法
如果子类只从一个父类得到继承,称为 “单重继承” 。如果一个子类有两个或更多个父类,则称为 “多重继承”
注:Java 中一个子类只能有一个父类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 接上一段代码看
public class Student extends Person{
// 使用关键字 extends 指明要继承的父类
private int id;
public void setId(int id){
this.id = id;
}
public int getId(){
return id;
}
public void study(){
System.out.println(getName() + "正在学习");
}
// 重写父类方法
public void run(){
System.out,println(getName + "想润")
}
}
多态
在收到消息时,对象要予以响应。不同对象收到同一消息可以产生完全不同的结果,这一现象称为多态。在使用多态的时候,用户可以发送一个通用的消息,而实现的细节则由接收对象自行决定。这样,同一消息就可以调用不同的方法
多态的实现受到继承的支持,利用类的继承的层次关系,把具有通用功能的消息存放在高层次,而不同的实现这一功能的行为放在较低层次,在这些低层次上生成的对象能够给通用消息以不同的响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 父类
public class Person{
public void work(){
System.out.println("工作");
}
}
// 子类 1
public class Student extends Person{
// 重写父类方法
public void work(){
System.out.println("上学");
}
public void run(){
System.out.println("Only the young CAN RUN!");
}
}
// 子类 2
public class Worker extends Person{
// 重写父类方法
public void work(){
System.out.println("上班");
}
public void sleep(){
System.out.println("睡觉");
}
}
// main
public class main{
public static void main(String[] args){
// 编译看左边,运行看右边
Person stu = new Student();
stu.work;
// 不可调用 stu.run(); 方法
Person wok = new Worker();
wok.work;
// 不可调用 wok.sleep(); 方法
}
}
// 运行输出:
// 上学
// 上班
多态的形式
多态有不同的形式,Cardelli 和 Wegner 把它分为 4 类
参数多态:应用比较广泛的多态,被称为最纯的多态
包含多态:在许多语言中都存在,最常见的例子就是子类型化,即一个类型是另一个类型的子类型
过载多态:同一个名字在不同的上下文中所代表的含义不同
动态绑定和静态绑定
绑定是一个把过程调用和响应调用所执行的代码加以结合的过程。在一般的程序设计语言中,绑定是在编译时进行的,叫做静态绑定。动态绑定则是在运行时进行的。因此,一个给定的过程调用和代码的结合直到调用发生时才进行
动态绑定是和类的继承以及多态相联系的。在继承关系中,子类是父类的一个特例,所以父类可以出现的地方,子类对象也可以出现。因此在运行过程中,当一个对象发送消息请求服务时,要根据接收对象的具体情况将请求的操作与实现的方法进行连接,即动态连接
面向对象分析
面向对象分析 (Object-Oriented Analysis, OOA) 的目的是为了获得对应用问题的理解。理解的目的是确定系统的功能、性能要求
面向对象分析包含 5 个活动:认定对象、组织对象、描述对象间的相互作用、确定对象的操作、定义对象的内部信息
面向对象设计
面向对象设计 (Object-Oriented Design, OOD) 是将 OOA 所创建的分析模型转化为设计模型,其目标是定义系统构造蓝图。通常的情况是,由概念模型生成的分析模型被装入到相应的执行环境中时,需要考虑实现问题加以调整和增补,如根据所用编程语言是否支持多继承或继承,而调整类结构。OOA 与 OOD 之间不存在鸿沟,采用一致的概念和一致的表示法,OOD 同样应遵循抽象、信息隐蔽、功能独立、模块化等设计准则
面向对象设计的活动
OOD 在复用 OOA 的模型的基础上,包含与 OOA 对应如下五个活动
- 识别类及对象
- 定义属性
- 定义服务
- 识别关系
- 识别包
面向对象设计原则
- 单一责任原则
就一个类而言,应该仅有一个引起它变化的原因。即,当需要修改某个类的时候原因有且只有一个,让一个类只做一种类型责任
- 开放-封闭原则
软件实体 (类、模块、函数等) 应该是可以扩展的,即开放的;但是不可修改的,即封闭的
- 里氏替换原则
子类型必须能够替换掉他们的基 (父) 类型。即,在任何父类可以出现的地方,都可以用子类的实例来赋值给父类型的引用。当一个子类型的实例应该能够替换任何其超类的实例时,它们之间才具有是一个 (is-a) 关系
- 依赖倒置原则
抽象不应该依赖于细节,细节应该依赖于抽象。即,高层不应该依赖于底层模块,二者都应该依赖于抽象
- 接口分离原则
不应该强迫客服依赖于它们不用的方法。接口属于客户,不属于它所在的类层次结构。即:依赖于抽象,不要依赖于具体,同时在抽象级别不应该有对于细节的依赖。这样做的好处就在于可以最大限度地应对可能的变化
以上为面向对象方法中的五大原则。除了这五大原则之外,Robert C. Martin 提出的面向对象设计原则还包括以下几个
- 重用发布等价原则
重用的颗粒就是发布的粒度
- 共同封闭原则
包中的所有类对于同一类性质的变化应该是共同封闭的。一个变化若对一个包产生影响,则将对该包中的所有类产生影响,而对于其他的包不造成任何影响
- 共同重用原则
一个包中的所有类应该是共同重用的。如果重用了包中的一个类,那么就要重用包中的所有类
- 无环依赖原则
在包的依赖关系图中不允许存在环,即包之间的结构必须是一个直接的无环图形
- 稳定依赖原则
朝着稳定的方向进行依赖
- 稳定抽象原则
包的抽象程度应该和其稳定程度一致
面向对象测试
就测试而言,用面向对象方法开发的系统测试与其他方法开发的系统测试没有什么不同
一般来说,对面向对象软件的测试可分为下列 4 个层次进行
- 算法层
- 类层
- 模板层
- 系统层
面向对象程序设计
程序设计范型 (Programming Paradigm) 是人们在程序设计时所采用的基本方式模型,决定了程序设计时采用的思维方式、使用的工具,同时又有一定的应用范畴。其发展经历了过程程序设计、模块化程序设计、函数程序设计、逻辑程序设计,发展到现在的面向对象程序设计范型
面向对象程序设计 (Object-Oriented Programming, OOP) 的实质是选用一种面向对象程序设计语言 (Object-Oriented Programming Language, OOPL) ,采用对象、类及其相关概念所进行的程序设计。它的关键在于加入了类和继承性,从而进一步提高了抽象程度。特定的 OOP 概念一般是通过 OOPL 中特定的语言机制来体现的
OOP 现在已经扩展到系统分析和软件设计的范畴,出现了面向对象分析和面向对象设计的概念,这部分在前面已经有所体现