面向对象思想.md 11.0 KB
Newer Older
C
CyC2018 已提交
1 2 3
# 一、三大特性

## 封装
C
CyC2018 已提交
4

C
CyC2018 已提交
5
利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体。数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。用户无需知道对象内部的细节,但可以通过对象对外提供的接口来访问该对象。
C
CyC2018 已提交
6

C
CyC2018 已提交
7
优点:
C
CyC2018 已提交
8

C
CyC2018 已提交
9 10 11 12 13
- 减少耦合:可以独立地开发、测试、优化、使用、理解和修改
- 减轻维护的负担:可以更容易被程序员理解,并且在调试的时候可以不影响其他模块
- 有效地调节性能:可以通过剖析确定哪些模块影响了系统的性能
- 提高软件的可重用性
- 降低了构建大型系统的风险:即使整个系统不可用,但是这些独立的模块却有可能是可用的
C
CyC2018 已提交
14

C
CyC2018 已提交
15
以下 Person 类封装 name、gender、age 等属性,外界只能通过 get() 方法获取一个 Person 对象的 name 属性和 gender 属性,而无法获取 age 属性,但是 age 属性可以供 work() 方法使用。
C
CyC2018 已提交
16

C
CyC2018 已提交
17
注意到 gender 属性使用 int 数据类型进行存储,封装使得用户注意不到这种实现细节。并且在需要修改 gender 属性使用的数据类型时,也可以在不影响客户端代码的情况下进行。
C
CyC2018 已提交
18

C
CyC2018 已提交
19
```java
C
CyC2018 已提交
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
public class Person {

    private String name;
    private int gender;
    private int age;

    public String getName() {
        return name;
    }

    public String getGender() {
        return gender == 0 ? "man" : "woman";
    }

    public void work() {
        if (18 <= age && age <= 50) {
            System.out.println(name + " is working very hard!");
        } else {
            System.out.println(name + " can't work any more!");
        }
    }
C
CyC2018 已提交
41 42
}
```
C
CyC2018 已提交
43

C
CyC2018 已提交
44
## 继承
C
CyC2018 已提交
45

C
CyC2018 已提交
46
继承实现了 **IS-A** 关系,例如 Cat 和 Animal 就是一种 IS-A 关系,因此 Cat 可以继承自 Animal,从而获得 Animal 非 private 的属性和方法。
C
CyC2018 已提交
47

C
CyC2018 已提交
48 49
继承应该遵循里氏替换原则,子类对象必须能够替换掉所有父类对象。

C
CyC2018 已提交
50
Cat 可以当做 Animal 来使用,也就是说可以使用 Animal 引用 Cat 对象。父类引用指向子类对象称为 **向上转型**
C
CyC2018 已提交
51

C
CyC2018 已提交
52
```java
C
CyC2018 已提交
53
Animal animal = new Cat();
C
CyC2018 已提交
54
```
C
CyC2018 已提交
55

C
CyC2018 已提交
56
## 多态
C
CyC2018 已提交
57

C
CyC2018 已提交
58 59
多态分为编译时多态和运行时多态:

C
CyC2018 已提交
60 61
- 编译时多态主要指方法的重载
- 运行时多态指程序中定义的对象引用所指向的具体类型在运行期间才确定
C
CyC2018 已提交
62

C
CyC2018 已提交
63
运行时多态有三个条件:
C
CyC2018 已提交
64

C
CyC2018 已提交
65 66 67
- 继承
- 覆盖(重写)
- 向上转型
C
CyC2018 已提交
68

C
CyC2018 已提交
69
下面的代码中,乐器类(Instrument)有两个子类:Wind 和 Percussion,它们都覆盖了父类的 play() 方法,并且在 main() 方法中使用父类 Instrument 来引用 Wind 和 Percussion 对象。在 Instrument 引用调用 play() 方法时,会执行实际引用对象所在类的 play() 方法,而不是 Instrument 类的方法。
C
CyC2018 已提交
70 71

```java
C
CyC2018 已提交
72
public class Instrument {
C
CyC2018 已提交
73

C
CyC2018 已提交
74 75 76
    public void play() {
        System.out.println("Instument is playing...");
    }
C
CyC2018 已提交
77 78
}

C
CyC2018 已提交
79
public class Wind extends Instrument {
C
CyC2018 已提交
80

C
CyC2018 已提交
81 82 83
    public void play() {
        System.out.println("Wind is playing...");
    }
C
CyC2018 已提交
84 85
}

C
CyC2018 已提交
86
public class Percussion extends Instrument {
C
CyC2018 已提交
87

C
CyC2018 已提交
88 89 90
    public void play() {
        System.out.println("Percussion is playing...");
    }
C
CyC2018 已提交
91 92
}

C
CyC2018 已提交
93
public class Music {
C
CyC2018 已提交
94

C
CyC2018 已提交
95 96 97 98 99 100 101 102
    public static void main(String[] args) {
        List<Instrument> instruments = new ArrayList<>();
        instruments.add(new Wind());
        instruments.add(new Percussion());
        for(Instrument instrument : instruments) {
            instrument.play();
        }
    }
C
CyC2018 已提交
103 104 105
}
```

C
CyC2018 已提交
106
# 二、类图
C
CyC2018 已提交
107

C
CyC2018 已提交
108
以下类图使用 [PlantUML](https://www.planttext.com/) 绘制,更多语法及使用请参考:http://plantuml.com/ 。
C
CyC2018 已提交
109

C
CyC2018 已提交
110
## 泛化关系 (Generalization)
C
CyC2018 已提交
111

C
CyC2018 已提交
112
用来描述继承关系,在 Java 中使用 extends 关键字。
C
CyC2018 已提交
113

C
CyC2018 已提交
114
![](index_files/SoWkIImgAStDuU8goIp9ILLmJyrBBKh.png)
C
CyC2018 已提交
115 116 117 118

```text
@startuml

C
CyC2018 已提交
119
title Generalization
C
CyC2018 已提交
120

C
CyC2018 已提交
121 122 123
class Vihical
class Car
class Trunck
C
CyC2018 已提交
124

C
CyC2018 已提交
125 126
Vihical <|-- Car
Vihical <|-- Trunck
C
CyC2018 已提交
127 128 129 130

@enduml
```

C
CyC2018 已提交
131
## 实现关系 (Realization)
C
CyC2018 已提交
132

C
CyC2018 已提交
133
用来实现一个接口,在 Java 中使用 implements 关键字。
C
CyC2018 已提交
134

C
CyC2018 已提交
135
![](index_files/SoWkIImgAStDuU8goIp9ILK8IatCoQn.png)
C
CyC2018 已提交
136 137 138 139

```text
@startuml

C
CyC2018 已提交
140
title Realization
C
CyC2018 已提交
141

C
CyC2018 已提交
142 143 144
interface MoveBehavior
class Fly
class Run
C
CyC2018 已提交
145

C
CyC2018 已提交
146 147
MoveBehavior <|.. Fly
MoveBehavior <|.. Run
C
CyC2018 已提交
148 149 150 151

@enduml
```

C
CyC2018 已提交
152
## 聚合关系 (Aggregation)
C
CyC2018 已提交
153 154 155

表示整体由部分组成,但是整体和部分不是强依赖的,整体不存在了部分还是会存在。

C
CyC2018 已提交
156
![](index_files/SoWkIImgAStDuU8goIp9ILLmJ4ylIar.png)
C
CyC2018 已提交
157 158 159 160

```text
@startuml

C
CyC2018 已提交
161
title Aggregation
C
CyC2018 已提交
162

C
CyC2018 已提交
163 164 165 166
class Computer
class Keyboard
class Mouse
class Screen
C
CyC2018 已提交
167

C
CyC2018 已提交
168 169 170
Computer o-- Keyboard
Computer o-- Mouse
Computer o-- Screen
C
CyC2018 已提交
171 172 173 174

@enduml
```

C
CyC2018 已提交
175
## 组合关系 (Composition)
C
CyC2018 已提交
176 177 178

和聚合不同,组合中整体和部分是强依赖的,整体不存在了部分也不存在了。比如公司和部门,公司没了部门就不存在了。但是公司和员工就属于聚合关系了,因为公司没了员工还在。

C
CyC2018 已提交
179
![](index_files/SoWkIImgAStDuU8goIp9ILLmpiyjo2_.png)
C
CyC2018 已提交
180 181 182 183

```text
@startuml

C
CyC2018 已提交
184
title Composition
C
CyC2018 已提交
185

C
CyC2018 已提交
186 187 188
class Company
class DepartmentA
class DepartmentB
C
CyC2018 已提交
189

C
CyC2018 已提交
190 191
Company *-- DepartmentA
Company *-- DepartmentB
C
CyC2018 已提交
192 193 194 195

@enduml
```

C
CyC2018 已提交
196
## 关联关系 (Association)
C
CyC2018 已提交
197

C
CyC2018 已提交
198
表示不同类对象之间有关联,这是一种静态关系,与运行过程的状态无关,在最开始就可以确定。因此也可以用 1 对 1、多对 1、多对多这种关联关系来表示。比如学生和学校就是一种关联关系,一个学校可以有很多学生,但是一个学生只属于一个学校,因此这是一种多对一的关系,在运行开始之前就可以确定。
C
CyC2018 已提交
199

C
CyC2018 已提交
200
![](index_files/SoWkIImgAStDuU8goIp9ILLmB2xEJyv.png)
C
CyC2018 已提交
201 202 203 204

```text
@startuml

C
CyC2018 已提交
205
title Association
C
CyC2018 已提交
206

C
CyC2018 已提交
207 208
class School
class Student
C
CyC2018 已提交
209

C
CyC2018 已提交
210
School "1" - "n" Student
C
CyC2018 已提交
211 212 213 214

@enduml
```

C
CyC2018 已提交
215
## 依赖关系 (Dependency)
C
CyC2018 已提交
216

C
CyC2018 已提交
217
和关联关系不同的是,依赖关系是在运行过程中起作用的。A 类和 B 类是依赖关系主要有三种形式:
C
CyC2018 已提交
218

C
CyC2018 已提交
219 220 221
- A 类是 B 类方法的局部变量;
- A 类是 B 类方法当中的一个参数;
- A 类向 B 类发送消息,从而影响 B 类发生变化。
C
CyC2018 已提交
222

C
CyC2018 已提交
223
![](index_files/LOun2W9134NxVugmbJPp15d4LalxC4O.png)
C
CyC2018 已提交
224 225 226 227

```text
@startuml

C
CyC2018 已提交
228
title Dependency
C
CyC2018 已提交
229

C
CyC2018 已提交
230 231
class Vihicle {
    move(MoveBehavior)
C
CyC2018 已提交
232 233
}

C
CyC2018 已提交
234 235
interface MoveBehavior {
    move()
C
CyC2018 已提交
236 237
}

C
CyC2018 已提交
238
note "MoveBehavior.move()" as N
C
CyC2018 已提交
239

C
CyC2018 已提交
240
Vihicle ..> MoveBehavior
C
CyC2018 已提交
241

C
CyC2018 已提交
242
Vihicle .. N
C
CyC2018 已提交
243 244 245 246

@enduml
```

C
CyC2018 已提交
247
# 三、设计原则
C
CyC2018 已提交
248

C
CyC2018 已提交
249
## S.O.L.I.D
C
CyC2018 已提交
250

C
CyC2018 已提交
251 252 253 254 255 256 257
| 简写 | 全拼 | 中文翻译 |
| :--: | :--: | :--: |
| SRP | The Single Responsibility Principle    | 单一责任原则 |
| OCP | The Open Closed Principle              | 开放封闭原则 |
| LSP | The Liskov Substitution Principle      | 里氏替换原则 |
| ISP | The Interface Segregation Principle    | 接口分离原则 |
| DIP | The Dependency Inversion Principle     | 依赖倒置原则 |
C
CyC2018 已提交
258

C
CyC2018 已提交
259
### 1. 单一责任原则
C
CyC2018 已提交
260

C
CyC2018 已提交
261
> 修改一个类的原因应该只有一个。
C
CyC2018 已提交
262 263 264 265 266

换句话说就是让一个类只负责一件事,当这个类需要做过多事情的时候,就需要分解这个类。

如果一个类承担的职责过多,就等于把这些职责耦合在了一起,一个职责的变化可能会削弱这个类完成其它职责的能力。

C
CyC2018 已提交
267
### 2. 开放封闭原则
C
CyC2018 已提交
268

C
CyC2018 已提交
269
> 类应该对扩展开放,对修改关闭。
C
CyC2018 已提交
270 271 272 273 274

扩展就是添加新功能的意思,因此该原则要求在添加新功能时不需要修改代码。

符合开闭原则最典型的设计模式是装饰者模式,它可以动态地将责任附加到对象上,而不用去修改类的代码。

C
CyC2018 已提交
275
### 3. 里氏替换原则
C
CyC2018 已提交
276

C
CyC2018 已提交
277
> 子类对象必须能够替换掉所有父类对象。
C
CyC2018 已提交
278

C
CyC2018 已提交
279
继承是一种 IS-A 关系,子类需要能够当成父类来使用,并且需要比父类更特殊。
C
CyC2018 已提交
280 281 282

如果不满足这个原则,那么各个子类的行为上就会有很大差异,增加继承体系的复杂度。

C
CyC2018 已提交
283
### 4. 接口分离原则
C
CyC2018 已提交
284

C
CyC2018 已提交
285
> 不应该强迫客户依赖于它们不用的方法。
C
CyC2018 已提交
286 287 288

因此使用多个专门的接口比使用单一的总接口要好。

C
CyC2018 已提交
289
### 5. 依赖倒置原则
C
CyC2018 已提交
290

C
CyC2018 已提交
291
> 高层模块不应该依赖于低层模块,二者都应该依赖于抽象;</br>抽象不应该依赖于细节,细节应该依赖于抽象。
C
CyC2018 已提交
292 293 294 295 296

高层模块包含一个应用程序中重要的策略选择和业务模块,如果高层模块依赖于低层模块,那么低层模块的改动就会直接影响到高层模块,从而迫使高层模块也需要改动。

依赖于抽象意味着:

C
CyC2018 已提交
297 298 299
- 任何变量都不应该持有一个指向具体类的指针或者引用;
- 任何类都不应该从具体类派生;
- 任何方法都不应该覆写它的任何基类中的已经实现的方法。
C
CyC2018 已提交
300

C
CyC2018 已提交
301
## 其他常见原则
C
CyC2018 已提交
302 303 304

除了上述的经典原则,在实际开发中还有下面这些常见的设计原则。

C
CyC2018 已提交
305 306 307 308 309 310 311
| 简写    | 全拼    | 中文翻译 |
| :--: | :--: | :--: |
|LOD|    The Law of Demeter                   | 迪米特法则   |
|CRP|    The Composite Reuse Principle        | 合成复用原则 |
|CCP|    The Common Closure Principle         | 共同封闭原则 |
|SAP|    The Stable Abstractions Principle    | 稳定抽象原则 |
|SDP|    The Stable Dependencies Principle    | 稳定依赖原则 |
C
CyC2018 已提交
312

C
CyC2018 已提交
313
### 1. 迪米特法则
C
CyC2018 已提交
314

C
CyC2018 已提交
315
迪米特法则又叫作最少知识原则(Least Knowledge Principle,简写 LKP),就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。
C
CyC2018 已提交
316

C
CyC2018 已提交
317
### 2. 合成复用原则
C
CyC2018 已提交
318

C
CyC2018 已提交
319
尽量使用对象组合,而不是通过继承来达到复用的目的。
C
CyC2018 已提交
320

C
CyC2018 已提交
321
### 3. 共同封闭原则
C
CyC2018 已提交
322 323 324

一起修改的类,应该组合在一起(同一个包里)。如果必须修改应用程序里的代码,我们希望所有的修改都发生在一个包里(修改关闭),而不是遍布在很多包里。

C
CyC2018 已提交
325
### 4. 稳定抽象原则
C
CyC2018 已提交
326 327 328

最稳定的包应该是最抽象的包,不稳定的包应该是具体的包,即包的抽象程度跟它的稳定性成正比。

C
CyC2018 已提交
329
### 5. 稳定依赖原则
C
CyC2018 已提交
330 331 332

包之间的依赖关系都应该是稳定方向依赖的,包要依赖的包要比自己更具有稳定性。

C
CyC2018 已提交
333
# 参考资料
C
CyC2018 已提交
334

C
CyC2018 已提交
335 336 337 338 339 340
- Java 编程思想
- 敏捷软件开发:原则、模式与实践
[面向对象设计的 SOLID 原则](http://www.cnblogs.com/shanyou/archive/2009/09/21/1570716.html)
[看懂 UML 类图和时序图](http://design-patterns.readthedocs.io/zh_CN/latest/read_uml.html#generalization)
[UML 系列——时序图(顺序图)sequence diagram](http://www.cnblogs.com/wolf-sun/p/UML-Sequence-diagram.html)
[面向对象编程三大特性 ------ 封装、继承、多态](http://blog.csdn.net/jianyuerensheng/article/details/51602015)