面向对象思想.md 11.3 KB
Newer Older
C
CyC2018 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
<!-- GFM-TOC -->
* [一、三大特性](#一三大特性)
    * [封装](#封装)
    * [继承](#继承)
    * [多态](#多态)
* [二、类图](#二类图)
    * [泛化关系 (Generalization)](#泛化关系-generalization)
    * [实现关系 (Realization)](#实现关系-realization)
    * [聚合关系 (Aggregation)](#聚合关系-aggregation)
    * [组合关系 (Composition)](#组合关系-composition)
    * [关联关系 (Association)](#关联关系-association)
    * [依赖关系 (Dependency)](#依赖关系-dependency)
* [三、设计原则](#三设计原则)
    * [S.O.L.I.D](#solid)
    * [其他常见原则](#其他常见原则)
* [参考资料](#参考资料)
<!-- GFM-TOC -->


# 一、三大特性

## 封装
C
CyC2018 已提交
23

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

C
CyC2018 已提交
26
优点:
C
CyC2018 已提交
27

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

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

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

C
CyC2018 已提交
38
```java
C
CyC2018 已提交
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
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 已提交
60 61
}
```
C
CyC2018 已提交
62

C
CyC2018 已提交
63
## 继承
C
CyC2018 已提交
64

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

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

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

C
CyC2018 已提交
71
```java
C
CyC2018 已提交
72
Animal animal = new Cat();
C
CyC2018 已提交
73
```
C
CyC2018 已提交
74

C
CyC2018 已提交
75
## 多态
C
CyC2018 已提交
76

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

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

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

C
CyC2018 已提交
84 85 86
- 继承
- 覆盖(重写)
- 向上转型
C
CyC2018 已提交
87

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

```java
C
CyC2018 已提交
91
public class Instrument {
C
CyC2018 已提交
92

C
CyC2018 已提交
93 94 95
    public void play() {
        System.out.println("Instument is playing...");
    }
C
CyC2018 已提交
96 97
}

C
CyC2018 已提交
98
public class Wind extends Instrument {
C
CyC2018 已提交
99

C
CyC2018 已提交
100 101 102
    public void play() {
        System.out.println("Wind is playing...");
    }
C
CyC2018 已提交
103 104
}

C
CyC2018 已提交
105
public class Percussion extends Instrument {
C
CyC2018 已提交
106

C
CyC2018 已提交
107 108 109
    public void play() {
        System.out.println("Percussion is playing...");
    }
C
CyC2018 已提交
110 111
}

C
CyC2018 已提交
112
public class Music {
C
CyC2018 已提交
113

C
CyC2018 已提交
114 115 116 117 118 119 120 121
    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 已提交
122 123 124
}
```

C
CyC2018 已提交
125
# 二、类图
C
CyC2018 已提交
126

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

C
CyC2018 已提交
129
## 泛化关系 (Generalization)
C
CyC2018 已提交
130

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

C
CyC2018 已提交
133
<div align="center"> <img src="pics/SoWkIImgAStDuU8goIp9ILLmJyrBBKh.png"/> </div><br>
C
CyC2018 已提交
134 135 136 137

```text
@startuml

C
CyC2018 已提交
138
title Generalization
C
CyC2018 已提交
139

C
CyC2018 已提交
140 141 142
class Vihical
class Car
class Trunck
C
CyC2018 已提交
143

C
CyC2018 已提交
144 145
Vihical <|-- Car
Vihical <|-- Trunck
C
CyC2018 已提交
146 147 148 149

@enduml
```

C
CyC2018 已提交
150
## 实现关系 (Realization)
C
CyC2018 已提交
151

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

C
CyC2018 已提交
154
<div align="center"> <img src="pics/SoWkIImgAStDuU8goIp9ILK8IatCoQn.png"/> </div><br>
C
CyC2018 已提交
155 156 157 158

```text
@startuml

C
CyC2018 已提交
159
title Realization
C
CyC2018 已提交
160

C
CyC2018 已提交
161 162 163
interface MoveBehavior
class Fly
class Run
C
CyC2018 已提交
164

C
CyC2018 已提交
165 166
MoveBehavior <|.. Fly
MoveBehavior <|.. Run
C
CyC2018 已提交
167 168 169 170

@enduml
```

C
CyC2018 已提交
171
## 聚合关系 (Aggregation)
C
CyC2018 已提交
172 173 174

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

C
CyC2018 已提交
175
<div align="center"> <img src="pics/SoWkIImgAStDuU8goIp9ILLmJ4ylIar.png"/> </div><br>
C
CyC2018 已提交
176 177 178 179

```text
@startuml

C
CyC2018 已提交
180
title Aggregation
C
CyC2018 已提交
181

C
CyC2018 已提交
182 183 184 185
class Computer
class Keyboard
class Mouse
class Screen
C
CyC2018 已提交
186

C
CyC2018 已提交
187 188 189
Computer o-- Keyboard
Computer o-- Mouse
Computer o-- Screen
C
CyC2018 已提交
190 191 192 193

@enduml
```

C
CyC2018 已提交
194
## 组合关系 (Composition)
C
CyC2018 已提交
195 196 197

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

C
CyC2018 已提交
198
<div align="center"> <img src="pics/SoWkIImgAStDuU8goIp9ILLmpiyjo2_.png"/> </div><br>
C
CyC2018 已提交
199 200 201 202

```text
@startuml

C
CyC2018 已提交
203
title Composition
C
CyC2018 已提交
204

C
CyC2018 已提交
205 206 207
class Company
class DepartmentA
class DepartmentB
C
CyC2018 已提交
208

C
CyC2018 已提交
209 210
Company *-- DepartmentA
Company *-- DepartmentB
C
CyC2018 已提交
211 212 213 214

@enduml
```

C
CyC2018 已提交
215
## 关联关系 (Association)
C
CyC2018 已提交
216

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

C
CyC2018 已提交
219
<div align="center"> <img src="pics/SoWkIImgAStDuU8goIp9ILLmB2xEJyv.png"/> </div><br>
C
CyC2018 已提交
220 221 222 223

```text
@startuml

C
CyC2018 已提交
224
title Association
C
CyC2018 已提交
225

C
CyC2018 已提交
226 227
class School
class Student
C
CyC2018 已提交
228

C
CyC2018 已提交
229
School "1" - "n" Student
C
CyC2018 已提交
230 231 232 233

@enduml
```

C
CyC2018 已提交
234
## 依赖关系 (Dependency)
C
CyC2018 已提交
235

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

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

C
CyC2018 已提交
242
<div align="center"> <img src="pics/LOun2W9134NxVugmbJPp15d4LalxC4O.png"/> </div><br>
C
CyC2018 已提交
243 244 245 246

```text
@startuml

C
CyC2018 已提交
247
title Dependency
C
CyC2018 已提交
248

C
CyC2018 已提交
249 250
class Vihicle {
    move(MoveBehavior)
C
CyC2018 已提交
251 252
}

C
CyC2018 已提交
253 254
interface MoveBehavior {
    move()
C
CyC2018 已提交
255 256
}

C
CyC2018 已提交
257
note "MoveBehavior.move()" as N
C
CyC2018 已提交
258

C
CyC2018 已提交
259
Vihicle ..> MoveBehavior
C
CyC2018 已提交
260

C
CyC2018 已提交
261
Vihicle .. N
C
CyC2018 已提交
262 263 264 265

@enduml
```

C
CyC2018 已提交
266
# 三、设计原则
C
CyC2018 已提交
267

C
CyC2018 已提交
268
## S.O.L.I.D
C
CyC2018 已提交
269

C
CyC2018 已提交
270 271 272 273 274 275 276
| 简写 | 全拼 | 中文翻译 |
| :--: | :--: | :--: |
| 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 已提交
277

C
CyC2018 已提交
278
### 1. 单一责任原则
C
CyC2018 已提交
279

C
CyC2018 已提交
280
> 修改一个类的原因应该只有一个。
C
CyC2018 已提交
281 282 283 284 285

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

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

C
CyC2018 已提交
286
### 2. 开放封闭原则
C
CyC2018 已提交
287

C
CyC2018 已提交
288
> 类应该对扩展开放,对修改关闭。
C
CyC2018 已提交
289 290 291 292 293

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

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

C
CyC2018 已提交
294
### 3. 里氏替换原则
C
CyC2018 已提交
295

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

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

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

C
CyC2018 已提交
302
### 4. 接口分离原则
C
CyC2018 已提交
303

C
CyC2018 已提交
304
> 不应该强迫客户依赖于它们不用的方法。
C
CyC2018 已提交
305 306 307

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

C
CyC2018 已提交
308
### 5. 依赖倒置原则
C
CyC2018 已提交
309

C
CyC2018 已提交
310
> 高层模块不应该依赖于低层模块,二者都应该依赖于抽象;</br>抽象不应该依赖于细节,细节应该依赖于抽象。
C
CyC2018 已提交
311 312 313 314 315

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

依赖于抽象意味着:

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

C
CyC2018 已提交
320
## 其他常见原则
C
CyC2018 已提交
321 322 323

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

C
CyC2018 已提交
324 325 326 327 328 329 330
| 简写    | 全拼    | 中文翻译 |
| :--: | :--: | :--: |
|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 已提交
331

C
CyC2018 已提交
332
### 1. 迪米特法则
C
CyC2018 已提交
333

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

C
CyC2018 已提交
336
### 2. 合成复用原则
C
CyC2018 已提交
337

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

C
CyC2018 已提交
340
### 3. 共同封闭原则
C
CyC2018 已提交
341 342 343

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

C
CyC2018 已提交
344
### 4. 稳定抽象原则
C
CyC2018 已提交
345 346 347

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

C
CyC2018 已提交
348
### 5. 稳定依赖原则
C
CyC2018 已提交
349 350 351

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

C
CyC2018 已提交
352
# 参考资料
C
CyC2018 已提交
353

C
CyC2018 已提交
354 355 356 357 358 359
- 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)
C
CyC2018 已提交
360 361 362 363




C
CyC2018 已提交
364
</br><div align="center"> <img src="https://cyc-1256109796.cos.ap-guangzhou.myqcloud.com/%E5%85%AC%E4%BC%97%E5%8F%B7%20%E6%B5%B7%E6%8A%A5.png" width="500px"> </div></br>