Merge pull request #1 from CyC2018/master

merged from base
This commit is contained in:
tecyang 2018-03-14 10:30:42 +08:00 committed by GitHub
commit adb133a3d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 260 additions and 133 deletions

View File

@ -79,9 +79,9 @@ URI 包含 URL 和 URN目前 WEB 只有 URL 比较流行,所以见到的基
## POST传输实体主体
POST 主要目的不是获取资源,而是传输实体主体数据。
POST 主要目的不是获取资源,而是传输存储在内容实体中的数据。
GET 和 POST 的请求都能使用额外的参数,但是 GET 的参数是以查询字符串出现在 URL 中,而 POST 的参数存储在实体主体部分
GET 和 POST 的请求都能使用额外的参数,但是 GET 的参数是以查询字符串出现在 URL 中,而 POST 的参数存储在内容实体。
```
GET /test/demo_form.asp?name1=value1&name2=value2 HTTP/1.1

View File

@ -161,7 +161,7 @@ public boolean Find(int target, int [][] array) {
**题目要求**
以 O(1) 的空间复杂度和 O(n) 的间复杂度来求解。
以 O(1) 的空间复杂度和 O(n) 的间复杂度来求解。
**解题思路**
@ -195,27 +195,11 @@ public String replaceSpace(StringBuffer str) {
**题目描述**
输入一个链表的头结点,从尾到头反过来打印出每个结点的值。
输入链表的第一个节点,从尾到头反过来打印出每个结点的值。
**解题思路**
典型的"后进先出",可使用栈或者递归。
正向遍历然后调用 Collections.reverse()。
```java
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> ret = new ArrayList<>();
while (listNode != null) {
ret.add(listNode.val);
listNode = listNode.next;
}
Collections.reverse(ret);
return ret;
}
```
使用 Stack
```java
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
@ -245,7 +229,23 @@ public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
}
```
不使用库函数,并且不使用递归的迭代实现,利用链表的头插法为逆序的特性。
正向遍历然后调用 Collections.reverse()。
```java
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> ret = new ArrayList<>();
while (listNode != null) {
ret.add(listNode.val);
listNode = listNode.next;
}
Collections.reverse(ret);
return ret;
}
```
不使用库函数,并且不使用递归。利用链表头插法为逆序的特点。
头结点和第一个节点的区别:头结点是在头插法中使用的一个额外节点,这个节点不存储值;第一个节点就是链表的第一个真正存储值的节点。
```java
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
@ -605,6 +605,19 @@ public int NumberOf1(int n) {
## 16. 数值的整数次方
**题目描述**
给定一个 double 类型的浮点数 base 和 int 类型的整数 exponent。求 base 的 exponent 次方。
**解题思路**
下面的讨论中 x 代表 baseN 代表 exponent。
- 当 x 为偶数时x<sup>N</sup> = (x \* x)<sup>N / 2</sup>
- 当 x 为奇数时x<sup>N</sup> = x \* (x \* x)<sup>N / 2</sup>
因为 (x \* x)<sup>N / 2</sup> 可以通过递归求解并且每递归一次N 都减小一半,因此整个算法的时间复杂度为 logN。
```java
public double Power(double base, int exponent) {
if (exponent == 0) return 1;
@ -662,6 +675,19 @@ private void printNumber(char[] number) {
## 18.1 在 O(1) 时间内删除链表节点
**解题思路**
- 如果链表不是尾节点,那么可以直接将下一个节点的值赋给节点,令节点指向下下个节点,然后删除下一个节点,时间复杂度为 O(1)。
<div align="center"> <img src="../pics//72f9bc11-06a9-40b4-8939-14f72e5cb4c3.png"/> </div><br>
- 否则,就需要先遍历链表,找到节点的前一个节点,然后让前一个节点指向节点的下一个节点,时间复杂度为 O(N)。
<div align="center"> <img src="../pics//2a398239-ee47-4ea1-b2d8-0ced638839ef.png"/> </div><br>
- 综上,如果进行 N 次操作,那么大约需要移动节点的次数为 N-1+N=2N-1其中 N-1 表示不是链表尾节点情况下的移动次数N 表示是尾节点情况下的移动次数。(2N-1)/N \~ 2因此该算法的时间复杂度为 O(1)。
```java
public ListNode deleteNode(ListNode head, ListNode tobeDelete) {
if (head == null || head.next == null || tobeDelete == null) return null;
@ -681,6 +707,17 @@ public ListNode deleteNode(ListNode head, ListNode tobeDelete) {
## 18.2 删除链表中重复的结点
**题目描述**
```html
Input : 1->2->3->3->4->4->5
Output : 1->2->5
```
**解题描述**
递归。
```java
public ListNode deleteDuplication(ListNode pHead) {
if (pHead == null) return null;
@ -988,7 +1025,7 @@ private int height(TreeNode root) {
**题目描述**
下图的矩阵顺时针打印结果为1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10
下图的矩阵顺时针打印结果为1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10
<div align="center"> <img src="../pics//8615d9f7-bd1d-4240-8bb4-02b941d54a6f.png"/> </div><br>
@ -1009,6 +1046,10 @@ public ArrayList<Integer> printMatrix(int[][] matrix) {
## 30. 包含 min 函数的栈
**题目描述**
定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的 min 函数。
```java
private Stack<Integer> stack = new Stack<>();
private Stack<Integer> minStack = new Stack<>();
@ -1369,6 +1410,12 @@ private void backtracking(char[] chars, boolean[] hasUsed, StringBuffer s) {
## 39. 数组中出现次数超过一半的数字
**解题思路**
多数投票问题,可以利用 Boyer-Moore Majority Vote Algorithm 来解决这个问题,使得时间复杂度为 O(n)。
使用 cnt 来统计一个元素出现的次数,当遍历到的元素和统计元素不想等时,令 cnt--。如果前面查找了 i 个元素,且 cnt == 0 ,说明前 i 个元素没有 majority或者有 majority但是出现的次数少于 i / 2 ,因为如果多于 i / 2 的话 cnt 就一定不会为 0 。此时剩下的 n - i 个元素中majority 的数目依然多于 (n - i) / 2因此继续查找就能找出 majority。
```java
public int MoreThanHalfNum_Solution(int[] nums) {
int cnt = 1, num = nums[0];
@ -1813,6 +1860,8 @@ private void merge(int[] nums, int start, int mid, int end) {
## 52. 两个链表的第一个公共结点
**题目描述**
```html
A: a1 → a2
@ -1821,6 +1870,8 @@ A: a1 → a2
B: b1 → b2 → b3
```
**解题思路**
设 A 的长度为 a + cB 的长度为 b + c其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a。
当访问 A 链表的指针访问到链表尾部时,令它从链表 B 的头部开始访问链表 B同样地当访问 B 链表的指针访问到链表尾部时,令它从链表 A 的头部开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。
@ -1840,23 +1891,56 @@ public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
## 53 数字在排序数组中出现的次数
**题目描述**
```html
Input:
1, 2, 3, 3, 3, 3, 4, 6
3
Output:
4
````
**解题思路**
可以用二分查找找出数字在数组的最左端和最右端。
```java
public int GetNumberOfK(int[] array, int num) {
int l = 0, h = array.length - 1;
// 先找出 num 在数组最左端的位置,可以控制二分查找结束时 l 指向该位置
public int GetNumberOfK(int[] nums, int K) {
int first = getFirstK(nums, K);
int last = getLastK(nums, K);
return first == -1 || last == -1 ? 0 : last - first + 1;
}
private int getFirstK(int[] nums, int K) {
int l = 0, h = nums.length - 1;
while (l <= h) {
int m = l + (h - l) / 2;
if (array[m] >= num) h = m - 1;
if (nums[m] >= K) h = m - 1;
else l = m + 1;
}
int cnt = 0;
while (l < array.length && array[l++] == num) cnt++;
return cnt;
if (l > nums.length - 1 || nums[l] != K) return -1;
return l;
}
private int getLastK(int[] nums, int K) {
int l = 0, h = nums.length - 1;
while (l <= h) {
int m = l + (h - l) / 2;
if (nums[m] > K) h = m - 1;
else l = m + 1;
}
if (h < 0 || nums[h] != K) return -1;
return h;
}
```
## 54. 二叉搜索树的第 k 个结点
**解题思路**
利用二叉搜索数中序遍历有序的特点。
```java
TreeNode ret;
int cnt = 0;
@ -1917,6 +2001,12 @@ public void FindNumsAppearOnce(int[] array, int num1[], int num2[]) {
输入一个递增排序的数组和一个数字 S在数组中查找两个数是的他们的和正好是 S如果有多对数字的和等于 S输出两个数的乘积最小的。
**解题思路**
使用双指针,一个指针指向元素较小的值,一个指针指向元素较大的值。指向较小元素的指针从头向尾遍历,指向较大元素的指针从尾向头遍历。
如果两个指针指向元素的和 sum == target那么得到要求的结果如果 sum > target移动较大的元素使 sum 变小一些;如果 sum < target移动较小的元素使 sum 变大一些
```java
public ArrayList<Integer> FindNumbersWithSum(int[] array, int sum) {
int i = 0, j = array.length - 1;

View File

@ -839,9 +839,9 @@ if (uniqueInstance == null) {
有非常多的家电,并且之后会增加家电。
<div align="center"> <img src="../pics//7b8f0d8e-a4fa-4c9d-b9a0-3e6a11cb3e33.jpg"/> </div><br>
<div align="center"> <img src="../pics//f6be22cb-d64f-4ee5-87b7-cbc4e6255c0e.jpg"/> </div><br>
<div align="center"> <img src="../pics//c3ca36b2-8459-4cf1-98b0-cc95a0e94f20.jpg"/> </div><br>
<div align="center"> <img src="../pics//5b832bde-d05e-42db-b648-42e274571ad9.jpg"/> </div><br>
**2. 模式定义**
@ -857,11 +857,11 @@ if (uniqueInstance == null) {
- RemoteLoader 是客户端,注意它与 RemoteControl 的区别。因为 RemoteControl 不能主动地调用自身的方法,因此也就不能当成是客户端。客户端好比人,只有人才能去真正去使用遥控器。
<div align="center"> <img src="../pics//5ef94f62-98ce-464d-a646-842d9c72c8b8.jpg"/> </div><br>
<div align="center"> <img src="../pics//b7b1f5c6-ff8a-4353-8060-44bbc4b9e02e.jpg"/> </div><br>
**4. 模式类图**
<div align="center"> <img src="../pics//1e09d75f-6268-4425-acf8-8ecd1b4a0ef3.jpg"/> </div><br>
<div align="center"> <img src="../pics//26ccd069-55ec-4a28-aeb3-025e39e5810f.jpg"/> </div><br>
**5. 代码实现**
@ -948,7 +948,7 @@ Light is on!
将一个类的接口,转换为客户期望的另一个接口。适配器让原本不兼容的类可以合作无间。
<div align="center"> <img src="../pics//8e8ba824-7a9e-4934-a212-e6a41dcc1602.jpg"/> </div><br>
<div align="center"> <img src="../pics//c484b07d-be3d-4699-9e28-f035de8a274c.jpg"/> </div><br>
**2. 模式类图**
@ -962,11 +962,11 @@ Light is on!
鸭子Duck和火鸡Turkey拥有不同的叫声Duck 调用的是 quack() 方法,而 Turkey 调用 gobble() 方法。
要求将 Turkey 的 gobble() 方法适配成 Duck 的 quack() 方法。
要求将 Turkey 的 gobble() 方法适配成 Duck 的 quack() 方法,从而让火鸡冒充鸭子
**4. 解决方案类图**
<div align="center"> <img src="../pics//1a511c76-bb6b-40ab-b8aa-39eeb619d673.jpg"/> </div><br>
<div align="center"> <img src="../pics//b8ceb9db-180e-4d01-932c-593fa2a6f515.jpg"/> </div><br>
**5. 代码实现**
@ -1022,6 +1022,10 @@ public class DuckTestDrive {
gobble!
```
**6. Enumration 适配成 Iterator**
<div align="center"> <img src="../pics//aa340e1a-f366-436b-a5a5-29a90425c10d.png"/> </div><br>
# 外观模式
**1. 模式定义**
@ -1040,7 +1044,7 @@ gobble!
**4. 解决方案类图**
<div align="center"> <img src="../pics//25387681-89f8-4365-a2fa-83b86449ee84.jpg"/> </div><br>
<div align="center"> <img src="../pics//a0339a9f-f44f-4e37-a37f-169bc735536d.jpg"/> </div><br>
**5. 设计原则**
@ -1062,7 +1066,7 @@ gobble!
模板方法 templateMethod() 定义了算法的骨架,确定了 primitiveOperation1() 和 primitiveOperation2() 方法执行的顺序,而 primitiveOperation1() 和 primitiveOperation2() 让子类去实现。
<div align="center"> <img src="../pics//ed62f400-192c-4185-899b-187958201f0c.jpg"/> </div><br>
<div align="center"> <img src="../pics//87ffaf7f-4aa5-4da0-af84-994de62fa440.jpg"/> </div><br>
**3. 问题描述**
@ -1082,7 +1086,7 @@ gobble!
**6. 钩子**
钩子hock某些步骤在不同实现中可有可无,可以先定义一个什么都不做的方法,把它加到模板方法中,如果子类需要它就覆盖默认实现并加上自己的实现。
某些步骤在不同实现中可有可无,可以先定义一个什么都不做的方法,把它加到模板方法中,如果子类需要它就覆盖默认实现并加上自己的实现。
**7. 代码实现**
@ -1334,7 +1338,7 @@ public class Client {
组合类拥有一个组件对象,因此组合类的操作可以委托给组件对象去处理,而组件对象可以是另一个组合类或者叶子类。
<div align="center"> <img src="../pics//f99c019e-7e91-4c2e-b94d-b031c402dcb5.jpg"/> </div><br>
<div align="center"> <img src="../pics//cf08a51d-14c0-4bfc-863b-c8672d9c2b02.jpg"/> </div><br>
**4. 代码实现**
@ -1455,9 +1459,9 @@ Context 的 request() 方法委托给 State 对象去处理。当 Context 组合
但是状态模式是通过状态转移来改变 Context 所组合的 State 对象,而策略模式是通过 Context 本身的决策来改变组合的 Strategy 对象。
所谓的状态转移,是指 Context 在运行过程中由于一些条件发生改变而使得 State 对象发生改变,主要必须要是在运行过程中。
所谓的状态转移,是指 Context 在运行过程中由于一些条件发生改变而使得 State 对象发生改变,注意必须要是在运行过程中。
状态模式主要是用来解决状态转移的问题,当状态发生庄毅了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 需要使用哪个算法。
状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 需要使用哪个算法。
**4. 问题描述**
@ -1477,7 +1481,7 @@ Context 的 request() 方法委托给 State 对象去处理。当 Context 组合
糖果销售机即 Context。
下面的实现中每个 State 都组合了 Context 对象,这是因为状态转移的操作在 State 对象中,而状态转移过程又必须改变 Context 对象的 state 对象,因此 State 必须拥有 Context 对象。
下面的实现中每个 State 都组合了 Context 对象,这是因为状态转移的操作在 State 对象中,而状态转移过程又必须改变 Context 对象的 state 对象,因此 State 必须组合 Context 对象。
```java
public interface State {
@ -1504,6 +1508,7 @@ public interface State {
```
```java
public class HasQuarterState implements State{
private GumballMachine gumballMachine;
public HasQuarterState(GumballMachine gumballMachine){
@ -1535,6 +1540,7 @@ public class HasQuarterState implements State{
```
```java
public class NoQuarterState implements State {
GumballMachine gumballMachine;
public NoQuarterState(GumballMachine gumballMachine) {
@ -1595,6 +1601,7 @@ public class SoldOutState implements State {
```
```java
public class SoldState implements State {
GumballMachine gumballMachine;
public SoldState(GumballMachine gumballMachine) {
@ -1630,6 +1637,7 @@ public class SoldState implements State {
```
```java
public class GumballMachine {
private State soldOutState;
private State noQuarterState;
private State hasQuarterState;
@ -1699,6 +1707,7 @@ public class GumballMachine {
```
```java
public class GumballMachineTestDrive {
public static void main(String[] args) {
GumballMachine gumballMachine = new GumballMachine(5);
@ -1778,7 +1787,7 @@ No gumball dispensed
过度使用设计模式可能导致代码被过度工程化,应该总是用最简单的解决方案完成工作,并在真正需要模式的地方才使用它。
反模式:不好的解决方案来解决一个问题。主要作用是为了警告不要使用这些解决方案。
反模式:不好的解决方案来解决一个问题。主要作用是为了警告人们不要使用这些解决方案。
模式分类:

View File

@ -1,62 +1,94 @@
<!-- GFM-TOC -->
* [S.O.L.I.D](#solid)
* [1. 单一责任原则](#1-单一责任原则)
* [2. 开放封闭原则](#2-开放封闭原则)
* [3. 里氏替换原则](#3-里氏替换原则)
* [4. 接口分离原则](#4-接口分离原则)
* [5. 依赖倒置原则](#5-依赖倒置原则)
* [其他常见原则](#其他常见原则)
* [1. 迪米特法则](#1-迪米特法则)
* [2. 合成复用原则](#2-合成复用原则)
* [3. 共同封闭原则](#3-共同封闭原则)
* [4. 稳定抽象原则](#4-稳定抽象原则)
* [5. 稳定依赖原则](#5-稳定依赖原则)
* [封装、继承、多态](#封装继承多态)
* [设计原则](#设计原则)
* [1. S.O.L.I.D](#1-solid)
* [1.1 单一责任原则](#11-单一责任原则)
* [1.2 开放封闭原则](#12-开放封闭原则)
* [1.3 里氏替换原则](#13-里氏替换原则)
* [1.4 接口分离原则](#14-接口分离原则)
* [1.5 依赖倒置原则](#15-依赖倒置原则)
* [2. 其他常见原则](#2-其他常见原则)
* [2.1 迪米特法则](#21-迪米特法则)
* [2.2 合成复用原则](#22-合成复用原则)
* [2.3 共同封闭原则](#23-共同封闭原则)
* [2.4 稳定抽象原则](#24-稳定抽象原则)
* [2.5 稳定依赖原则](#25-稳定依赖原则)
* [三大特性](#三大特性)
* [1. 封装](#1-封装)
* [2. 继承](#2-继承)
* [3. 多态](#3-多态)
* [UML](#uml)
* [1. 类图](#1-类图)
* [1.1 继承相关](#11-继承相关)
* [1.2 整体和部分](#12-整体和部分)
* [1.3 相互联系](#13-相互联系)
* [2. 时序图](#2-时序图)
* [2.1 定义](#21-定义)
* [2.2 赤壁之战时序图](#22-赤壁之战时序图)
* [2.3 活动图、时序图之间的关系](#23-活动图时序图之间的关系)
* [2.4 类图与时序图的关系](#24-类图与时序图的关系)
* [2.5 时序图的组成](#25-时序图的组成)
* [参考资料](#参考资料)
<!-- GFM-TOC -->
# S.O.L.I.D
# 设计原则
S.O.L.I.D 是面向对象设计和编程 (OOD&OOP) 中几个重要编码原则 (Programming Priciple) 的首字母缩写。
设计原则可以帮助我们避免那些糟糕的设计,这些原则被归纳在《敏捷软件开发:原则、模式与实践》这本书中。
## 1. S.O.L.I.D
| 简写 | 全拼 | 中文翻译 |
| -- | -- | -- |
|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 | 依赖倒置原则 |
| 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 | 依赖倒置原则 |
### 1.1 单一责任原则
## 1. 单一责任原则
**修改一个类的原因应该只有一个。**
当需要修改某个类的时候原因有且只有一个。换句话说就是让一个类只做一种类型责任,当这个类需要承当其他类型的责任的时候,就需要分解这个类。
换句话说就是让一个类只负责一件事,当这个类需要做过多事情的时候,就需要分解这个类。
## 2. 开放封闭原则
如果一个类承担的职责过多,就等于把这些职责耦合在了一起,一个职责的变化可能会削弱这个类完成其它职责的能力。
软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。
### 1.2 开放封闭原则
## 3. 里氏替换原则
**类应该对扩展开放,对修改关闭。**
当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有 is-a 关系
扩展就是添加新功能的意思,因此该原则要求在添加新功能时不需要修改代码
## 4. 接口分离原则
符合开闭原则最典型的设计模式是装饰者模式,它可以动态地将责任附加到对象上,而不用去修改类的代码。
不能强迫用户去依赖那些他们不使用的接口。换句话说,使用多个专门的接口比使用单一的总接口总要好。
### 1.3 里氏替换原则
## 5. 依赖倒置原则
**子类对象必须能够替换掉所有父类对象。**
1. 高层模块不应该依赖于低层模块,二者都应该依赖于抽象
2. 抽象不应该依赖于细节,细节应该依赖于抽象
继承是一种 IS-A 关系,子类需要能够当成父类来使用,并且需要比父类更特殊。
# 其他常见原则
如果不满足这个原则,那么各个子类的行为上就会有很大差异,增加继承体系的复杂度。
### 1.4 接口分离原则
**不应该强迫客户依赖于它们不用的方法。**
因此使用多个专门的接口比使用单一的总接口总要好。
### 1.5 依赖倒置原则
- **高层模块不应该依赖于低层模块,二者都应该依赖于抽象**
- **抽象不应该依赖于细节,细节应该依赖于抽象**
高层模块包含一个应用程序中重要的策略选择和业务模块,如果高层模块依赖于底层模块,那么底层模块的改动就会直接影响到高层模块,从而迫使高层模块也需要改动。
依赖于抽象意味着:
- 任何变量都不应该持有一个指向具体类的指针或者引用;
- 任何类都不应该从具体类派生;
- 任何方法都不应该覆写它的任何基类中的已经实现的方法。
## 2. 其他常见原则
除了上述的经典原则,在实际开发中还有下面这些常见的设计原则。
@ -68,44 +100,41 @@ S.O.L.I.D 是面向对象设计和编程 (OOD&OOP) 中几个重要编码原则 (
|SAP| The Stable Abstractions Principle | 稳定抽象原则 |
|SDP| The Stable Dependencies Principle | 稳定依赖原则 |
## 1. 迪米特法则
### 2.1 迪米特法则
迪米特法则又叫作最少知道原则Least Knowledge Principle 简写LKP就是说一个对象应当对其他对象有尽可能少的了解不和陌生人说话。
## 2. 合成复用原则
### 2.2 合成复用原则
尽量使用对象组合,而不是继承来达到复用的目的。
## 3. 共同封闭原则
### 2.3 共同封闭原则
一起修改的类,应该组合在一起(同一个包里)。如果必须修改应用程序里的代码,我们希望所有的修改都发生在一个包里(修改关闭),而不是遍布在很多包里。
## 4. 稳定抽象原则
### 2.4 稳定抽象原则
最稳定的包应该是最抽象的包,不稳定的包应该是具体的包,即包的抽象程度跟它的稳定性成正比。
## 5. 稳定依赖原则
### 2.5 稳定依赖原则
包之间的依赖关系都应该是稳定方向依赖的,包要依赖的包要比自己更具有稳定性。
# 封装、继承、多态
封装、继承、多态是面向对象的三大特性。
# 三大特性
## 1. 封装
利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。用户无需知道对象内部的细节,但可以通过对象对外提供的接口来访问该对象。
利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。用户无需知道对象内部的细节,但可以通过对象对外提供的接口来访问该对象。
封装有三大好处:
1. 良好的封装能够减少耦合。
2. 类内部的结构可以自由修改。
3. 可以对成员进行更精确的控制。
4. 隐藏信息,实现细节。
1. 减少耦合
2. 隐藏内部细节,因此内部结构可以自由修改
3. 可以对成员进行更精确的控制
以下 Person 类封装 name、gender、age 等属性,外界只能通过 get() 方法获取一个 Person 对象的 name 属性和 gender 属性,而无法获取 age 属性,但是 age 属性可以供 work() 方法使用。
注意到 gender 属性使用 int 数据类型进行存储,封装使得用户注意不到这种实现细节。并且在需要修改使用的数据类型时,也可以在不影响客户端代码的情况下进行。
注意到 gender 属性使用 int 数据类型进行存储,封装使得用户注意不到这种实现细节。并且在需要修改 gender 属性使用的数据类型时,也可以在不影响客户端代码的情况下进行。
```java
public class Person {
@ -125,7 +154,7 @@ public class Person {
if(18 <= age && age <= 50) {
System.out.println(name + " is working very hard!");
} else {
System.out.println(name + " can't work!");
System.out.println(name + " can't work any more!");
}
}
}
@ -133,23 +162,27 @@ public class Person {
## 2. 继承
继承实现了 **is-a** 关系,例如 Cat 和 Animal 就是一种 is-a 关系,因此可以将 Cat 继承自 Animal从而获得 Animal 非 private 的属性和方法。
继承实现了 **IS-A** 关系,例如 Cat 和 Animal 就是一种 IS-A 关系,因此 Cat 可以继承自 Animal从而获得 Animal 非 private 的属性和方法。
Cat 可以当做 Animal 来使用,也就是可以使用 Animal 引用 Cat 对象,这种子类转换为父类称为 **向上转型**
继承应该遵循里氏替换原则:当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有 is-a 关系。
Cat 可以当做 Animal 来使用,也就是说可以使用 Animal 引用 Cat 对象。父类引用指向子类对象称为 **向上转型**
```java
Animal animal = new Cat();
```
继承应该遵循里氏替换原则,子类对象必须能够替换掉所有父类对象。
## 3. 多态
多态分为编译时多态和运行时多态。编译时多态主要指方法的重,运行时多态指程序中定义的对象引用所指向的具体类型在运行期间才确定。
多态分为编译时多态和运行时多态。编译时多态主要指方法的重,运行时多态指程序中定义的对象引用所指向的具体类型在运行期间才确定。
多态有三个条件:1. 继承2. 覆盖父类方法3. 向上转型。
运行时多态有三个条件:
下面的代码中乐器类Instrument有两个子类Wind 和 Percussion它们都覆盖了 play() 方法,并且在 main() 方法中使用父类 Instrument 来引用 Wind 和 Percussion 对象。在 Instrument 引用调用 play() 方法时,会执行实际引用对象所在类的 play() 方法,而不是 Instrument 类的方法。
1. 继承
2. 覆盖
3. 向上转型
下面的代码中乐器类Instrument有两个子类Wind 和 Percussion它们都覆盖了父类的 play() 方法,并且在 main() 方法中使用父类 Instrument 来引用 Wind 和 Percussion 对象。在 Instrument 引用调用 play() 方法时,会执行实际引用对象所在类的 play() 方法,而不是 Instrument 类的方法。
```java
public class Instrument {
@ -180,54 +213,51 @@ public class Music {
}
}
}
```
# UML
## 1. 类图
**1.1 继承相关**
### 1.1 继承相关
继承有两种形式 : 泛化(generalize和实现realize表现为 is-a 关系。
继承有两种形式 : 泛化(Generalize和实现Realize表现为 IS-A 关系。
① 泛化关系 (generalization)
**泛化关系 (Generalize)**
从具体类中继承
从具体类中继承
<div align="center"> <img src="../pics//29badd92-109f-4f29-abb9-9857f5973928.png"/> </div><br>
② 实现关系 (realize)
**实现关系 (Realize)**
从抽象类或者接口中继承
从抽象类或者接口中继承
<div align="center"> <img src="../pics//4b16e1d3-3a60-472c-9756-2f31b1c48abe.png"/> </div><br>
**1.2 整体和部分**
### 1.2 整体和部分
① 聚合关系 (aggregation)
**聚合关系 (Aggregation)**
表示整体由部分组成,但是整体和部分不是强依赖的,整体不存在了部分还是会存在。以下表示 B 由 A 组成:
<div align="center"> <img src="../pics//34259bb8-ca3a-4872-8771-9e946782d9c3.png"/> </div><br>
② 组合关系 (composition)
**组合关系 (Composition)**
和聚合不同,组合中整体和部分是强依赖的,整体不存在了部分也不存在了。比如公司和部门,公司没了部门就不存在了。但是公司和员工就属于聚合关系了,因为公司没了员工还在。
<div align="center"> <img src="../pics//7dda050d-ac35-4f47-9f51-18f18ed6fa9a.png"/> </div><br>
**1.3 相互联系**
### 1.3 相互联系
① 关联关系 (association)
**关联关系 (Association)**
表示不同类对象之间有关联,这是一种静态关系,与运行过程的状态无关,在最开始就可以确定。因此也可以用 1 对 1、多对 1、多对多这种关联关系来表示。比如学生和学校就是一种关联关系一个学校可以有很多学生但是一个学生只属于一个学校因此这是一种多对一的关系在运行开始之前就可以确定。
<div align="center"> <img src="../pics//4ccd294c-d6b2-421b-839e-d88336ff5fb7.png"/> </div><br>
② 依赖关系 (dependency)
**依赖关系 (Dependency)**
和关联关系不同的是 , 依赖关系是在运行过程中起作用的。一般依赖作为类的构造器或者方法的参数传入。双向依赖时一种不好的设计。
@ -235,13 +265,11 @@ public class Music {
## 2. 时序图
http://www.cnblogs.com/wolf-sun/p/UML-Sequence-diagram.html
**2.1 定义**
### 2.1 定义
时序图描述了对象之间传递消息的时间顺序,它用来表示用例的行为顺序。它的主要作用是通过对象间的交互来描述用例(注意是对象),从而寻找类的操作。
**2.2 赤壁之战时序图**
### 2.2 赤壁之战时序图
从虚线从上往下表示时间的推进。
@ -273,19 +301,19 @@ public class 孙权 {
}
```
**2.3 活动图、时序图之间的关系**
### 2.3 活动图、时序图之间的关系
活动图示从用户的角度来描述用例;
时序图是从计算机的角度(对象间的交互)描述用例。
**2.4 类图与时序图的关系**
### 2.4 类图与时序图的关系
类图描述系统的静态结构,时序图描述系统的动态行为。
**2.5 时序图的组成**
### 2.5 时序图的组成
① 对象
**对象**
有三种表现形式
@ -297,13 +325,13 @@ public class 孙权 {
2. 把初始化整个交互活动的对象(有时是一个参与者)放置在最左边。
② 生命线
**生命线**
生命线从对象的创建开始到对象销毁时终止
<div align="center"> <img src="../pics//b7b0eac6-e7ea-4fb6-8bfb-95fec6f235e2.png"/> </div><br>
③ 消息
**消息**
对象之间的交互式通过发送消息来实现的。
@ -323,17 +351,17 @@ public class 孙权 {
4\. 返回消息,可选。
④ 激活
**激活**
生命线上的方框表示激活状态,其它时间处于休眠状态。
<div align="center"> <img src="../pics//6ab5de9b-1c1e-4118-b2c3-fb6c7ed7de6f.png"/> </div><br>
# 参考资料
- 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)
- 敏捷软件开发:原则、模式与实践
- [面向对象设计的 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)
- [面向对象编程三大特性 ------ 封装、继承、多态](http://blog.csdn.net/jianyuerensheng/article/details/51602015)

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB