物件導向設計原則的實際運用,是對類的封裝性、繼承性和多型性以及類的關聯關係和組合關係的充分理解。
提高程式碼可讀性、重用性、可靠性、可延伸性,實現「高內聚,低耦合」。
常見的設計模式有23種,但是發展到今天還有很多叫不上名字來的設計模式,無一例外都遵循著「軟體設計七大原則」。
單一職責就是一個類或者一個方法只負責一項職責。
假設有一個IT部門,一個開發,一個測試,一個運維。我們把三個人的工作抽象為一個類
public static void main(String[] args) {
Employee.work("Developer");
Employee.work("Tester");
Employee.work("Operator");
}
// 員工類
static class Employee {
public static void work(String name) {
System.out.println(name + "正在寫程式碼...");
}
}
執行結果
Developer正在寫程式碼...
Tester正在寫程式碼...
Operator正在寫程式碼...
很明顯,開發、測試、運維都在寫程式碼,顯然不合理;正常來說,開發寫程式碼、測試寫用例、運維寫指令碼,而Demo1
中的work
實現了三種不同職責,違背了單一職責原則
public static void main(String[] args) {
Developer.work("Developer");
Tester.work("Tester");
Operator.work("Operator");
}
// 員工類:開發
static class Developer {
public static void work(String name) {
System.out.println(name + "正在寫程式碼...");
}
}
// 員工類:測試
static class Tester {
public static void work(String name) {
System.out.println(name + "正在寫用例...");
}
}
// 員工類:運維
static class Operator {
public static void work(String name) {
System.out.println(name + "正在寫指令碼...");
}
}
執行結果
Developer正在寫程式碼...
Tester正在寫用例...
Operator正在寫指令碼...
看執行結果,已經符合了單一職責原則,但是看Demo2
程式碼會發現,三種職責我們建立了三個類,把Employee
分解為Developer
,Tester
,Operator
,並且呼叫方main
也做了修改,這樣改動太大
public static void main(String[] args) {
Employee.workCode("Developer");
Employee.workUseCase("Tester");
Employee.workScript("Operator");
}
// 員工類
static class Employee {
public static void workCode(String name) {
System.out.println(name + "正在寫程式碼...");
}
public static void workUseCase(String name) {
System.out.println(name + "正在寫用例...");
}
public static void workScript(String name) {
System.out.println(name + "正在寫指令碼...");
}
}
執行結果
Developer正在寫程式碼...
Tester正在寫用例...
Operator正在寫指令碼...
在Demo3
中把work
一分為三,沒有分解類,而是在方法級別上進行了拆分,也達到了預期的效果,並且呼叫者main
中改動量也很小
1. 單一職責可以細化為類級別和方法級別,最低限度是在方法級別保證單一職責原則;但是如果一個類中有幾十個方法,那麼就需要衡量下是否需要進行類分解
2. 提高程式碼可讀性,可維護性,可延伸性
3. 降低類的複雜度
一個類對另一個類的依賴應該建立在最小的介面上,即一個類不應該依賴它不需要的介面
就拿涼拌黃瓜和炒黃瓜的步驟來舉例
涼拌黃瓜:洗菜 -> 切菜 -> 涼拌
炒 黃 瓜:洗菜 -> 切菜 -> 炒菜
private static final String name = "黃瓜";
public static void main(String[] args) {
CookingCold cookingCold = new CookingCold();
ColdMixCucumber coldMixCucumber = new ColdMixCucumber();
coldMixCucumber.wash(cookingCold);
coldMixCucumber.cut(cookingCold);
coldMixCucumber.coldMix(cookingCold);
System.out.println();
CookingHot cookingHot = new CookingHot();
FryCucumber fryCucumber = new FryCucumber();
fryCucumber.wash(cookingHot);
fryCucumber.cut(cookingHot);
fryCucumber.fry(cookingHot);;
}
// 做菜介面
interface Cooking {
// 洗菜
void wash(String name);
// 切菜
void cut(String name);
// 涼拌
void coldMix(String name);
// 炒菜
void fry(String name);
}
// 做冷盤
static class CookingCold implements Cooking {
@Override
public void wash(String name) {
System.out.println("洗" + name);
}
@Override
public void cut(String name) {
System.out.println("切" + name);
}
@Override
public void coldMix(String name) {
System.out.println("涼拌" + name);
}
@Override
public void fry(String name) {}
}
// 做熱菜
static class CookingHot implements Cooking {
@Override
public void wash(String name) {
System.out.println("洗" + name);
}
@Override
public void cut(String name) {
System.out.println("切" + name);
}
@Override
public void coldMix(String name) {}
@Override
public void fry(String name) {
System.out.println("炒" + name);
}
}
// 涼拌黃瓜
static class ColdMixCucumber {
// 洗黃瓜
public void wash(Cooking cooking) {
cooking.wash(name);
}
// 切黃瓜
public void cut(Cooking cooking) {
cooking.cut(name);
}
// 涼拌黃瓜
public void coldMix(Cooking cooking) {
cooking.coldMix(name);
}
}
// 炒黃瓜
static class FryCucumber {
// 洗黃瓜
public void wash(Cooking cooking) {
cooking.wash(name);
}
// 切黃瓜
public void cut(Cooking cooking) {
cooking.cut(name);
}
// 炒黃瓜
public void fry(Cooking cooking) {
cooking.fry(name);
}
}
下圖為Demo1
的UML類圖,據此分析CookingCold
和CookingHot
均實現了Cooking
介面並實現其所有方法,但在ColdMixCucumber
和FryCucumber
中僅使用了其中的三個方法;雖然執行結果沒有問題,但是明顯Cooking
介面的設計不合理,不符合介面隔離原則
我們注意到Cooking
介面中,wash
和cut
是都會用到的,而coldMix
和fry
並不會全部用到;根據介面隔離原則,我們把Cooking
介面分解為三個介面,UML類圖如下所示:
private static final String name = "黃瓜";
public static void main(String[] args) {
CookingCold cookingCold = new CookingCold();
ColdMixCucumber coldMixCucumber = new ColdMixCucumber();
coldMixCucumber.wash(cookingCold);
coldMixCucumber.cut(cookingCold);
coldMixCucumber.coldMix(cookingCold);
System.out.println();
CookingHot cookingHot = new CookingHot();
FryCucumber fryCucumber = new FryCucumber();
fryCucumber.wash(cookingHot);
fryCucumber.cut(cookingHot);
fryCucumber.fry(cookingHot);;
}
// 做菜介面_01
interface Cooking_01 {
// 洗菜
void wash(String name);
// 切菜
void cut(String name);
}
// 做菜介面_02
interface Cooking_02 {
// 涼拌
void coldMix(String name);
}
// 做菜介面_03
interface Cooking_03 {
// 炒菜
void fry(String name);
}
// 做冷盤
static class CookingCold implements Cooking_01, Cooking_02 {
@Override
public void wash(String name) {
System.out.println("洗" + name);
}
@Override
public void cut(String name) {
System.out.println("切" + name);
}
@Override
public void coldMix(String name) {
System.out.println("涼拌" + name);
}
}
// 做熱菜
static class CookingHot implements Cooking_01, Cooking_03 {
@Override
public void wash(String name) {
System.out.println("洗" + name);
}
@Override
public void cut(String name) {
System.out.println("切" + name);
}
@Override
public void fry(String name) {
System.out.println("炒" + name);
}
}
// 涼拌黃瓜
static class ColdMixCucumber {
// 洗黃瓜
public void wash(Cooking_01 cooking) {
cooking.wash(name);
}
// 切黃瓜
public void cut(Cooking_01 cooking) {
cooking.cut(name);
}
// 涼拌黃瓜
public void coldMix(Cooking_02 cooking) {
cooking.coldMix(name);
}
}
// 炒黃瓜
static class FryCucumber {
// 洗黃瓜
public void wash(Cooking_01 cooking) {
cooking.wash(name);
}
// 切黃瓜
public void cut(Cooking_01 cooking) {
cooking.cut(name);
}
// 炒黃瓜
public void fry(Cooking_03 cooking) {
cooking.fry(name);
}
}
1.提高程式碼可讀性,可重用性,可維護性
3.降低類的複雜度,降低耦合性
1. 依賴倒置/倒轉的核心是面向介面程式設計
2. 相對於細節的多變性,抽象的東西相對穩定得多;以抽象為基礎的架構比以細節為基礎的架構穩定得多;而在Java中抽象是指抽象類和介面,細節是指抽象類和介面的具體實現
3. 抽象類和介面可以理解為制定規範,但不涉及任何具體操作,把展現細節的工作交給他們具體的實現類完成,以此來提高系統的可靠性和可維護性
以付款場景為例
// 接收方
public abstract class Receiver {
// 訊息
private String message;
// 獲取接收型別
protected abstract String getType();
public String getMessage() {
return message;
}
}
抽象類與具體類基本相同,只是矩形第一層的抽象類名是斜體的,成員方法中的抽象方法也是斜體的
// 形狀
public interface Shape {
// 獲取尺寸
int getSize();
}
介面在矩形的第一層中第一行為 <<Interface>>
*做介面標識,第二行為介面名
簡單來說,只要類中用到了另一個類,他們之間就存在了依賴關係(UML類圖中依賴關係用帶虛線的箭頭表示)
public class UserService {
// 成員變數
private UserDao userDao;
// 方法引數
public void insert(User user) {}
// 方法返回值
public Role getRole(Long id) {
return null;
}
// 方法區域性變數
public void update() {
Dept dept = new Dept();
}
}
類中用到了另一個類包括以上幾種情況:
泛化關係實際上就是繼承關係,是依賴關係的特例(在UML類圖中用帶空心三角的實線表示)
public class User extends Person {}
實現關係是指類A實現了介面B,是依賴關係的特例(在UML類圖中用帶空心三角的虛線表示)
public class UserServiceImpl implements IUserService{}
關聯關係是類與類之間存在的聯絡,是依賴關係的特例(在UML類圖中用帶雙箭頭的實線或者不帶箭頭的雙實線表示雙向關聯,用帶單箭頭的實線表示單向關聯)
關聯具有導航性:單向關聯 和 雙向關聯
關聯具有多重性:一對一,多對一,多對多
*
或者0..*
:表示0到多個0..1
:表示0或者1個1..*
:表示1到多個// 單向一對一關聯
public class Association_01 {
// 人
static class Person {
private IDCard idCard;
}
// 身份證
static class IDCard {
}
}
// 雙向一對一關聯
public class Association_02 {
// 人
static class Person {
private IDCard idCard;
}
// 身份證
static class IDCard {
private Person person;
}
}
// 單向多對一關聯
public class Association_03 {
// 人
static class Person {
private List<BankCard> bankCardList;
}
// 銀行卡
static class BankCard {
}
}
// 雙向多對一關聯
public class Association_04 {
// 人
static class Person {
private List<BankCard> bankCardList;
}
// 銀行卡
static class BankCard {
private Person person;
}
}
// 多對多關聯
public class Association_05 {
// 使用者
static class User {
private List<Role> roleList;
}
// 角色
static class Role {
private List<User> userList;
}
}
聚合關係是指整體與部分的關係,整體和部分可以分開(比如電腦和滑鼠,滑鼠是電腦的一部分,可以分開),是關聯關係的特例,所以同樣具有導航性和多重性(在UML類圖中用空心菱形加實線箭頭表示,空心菱形在整體一方,箭頭指向部分一方,表示把部分聚合到整體中來)
// 聚合關係
public class Aggregation_01 {
// 人
static class Person {
private IDCard idCard;
}
// 身份證
static class IDCard {
}
}
組合關係是指整體與部分的關係,整體和部分不可以分開(比如人的身體和頭,頭是身體的一部分,不可以分開),是關聯關係的特例,所以同樣具有導航性和多重性(在UML類圖中用實心菱形加實線箭頭表示,實心菱形在整體一方,箭頭指向部分一方,表示把部分組合到整體中來)
// 組合關係
public class Composition_01 {
// 人
static class Person {
private IDCard idCard = new IDCard();
}
// 身份證
static class IDCard {
}
}
我使用的是draw.io
(線上版本、PC版都有),支援多種語言
下載連結:https://github.com/jgraph/drawio-desktop/releases
主介面如下:
以下羅列了常見的23中設計模式:
建立型模式:
- 單例模式
- 工廠模式
- 抽象工廠模式
- 原型模式
- 建造者模式
結構型模式:
- 介面卡模式
- 橋接模式
- 裝飾模式
- 組合模式
- 外觀模式
- 享元模式
- 代理模式
行為型模式:
- 模板方法模式
- 命令模式
- 存取者模式
- 迭代器模式
- 觀察者模式
- 中介者模式
- 備忘錄模式
- 直譯器模式(Interpreter模式)
- 狀態模式
- 策略模式
- 職責鏈模式(責任鏈模式)
本篇章完整程式碼:https://github.com/yushixin-1024/DesignPattern
PS:程式碼不涉及具體業務邏輯,僅僅是為了舉例,方便理解。
本文來自部落格園,作者:颯沓流星,轉載請註明原文連結:https://www.cnblogs.com/yushixin1024/p/16427453.html