1、設計模式的本質
? 面向物件設計原則的實際運用,是對類的封裝性、繼承性和多型性以及類的關聯關系和組合關系的充分理解,
2、設計模式的目的
? 提高代碼可讀性、重用性、可靠性、可擴展性,實作“高內聚,低耦合”,
名詞解釋
- 可讀性:按照規范編程,便于其他程式員閱讀和理解
- 重用性:相同功能的代碼,可以重復使用,無需多次撰寫
- 可靠性:增加功能時,對原有功能沒有影響
- 可擴展性:增加功能時方便,可維護性強
3、設計模式的依據
? 經典的設計模式有23種,但是發展到今天還有很多叫不上名字來的設計模式,無一例外都遵循著“軟體設計七大原則”,
3.1 單一職責原則(Single Responsibility Principle, SRP)
3.1.1 解釋
? 單一職責就是一個類或者一個方法只負責一項職責,
3.1.2 舉例
? 假設有一個IT部門,一個開發,一個測驗,一個運維,我們把三個人的作業抽象為一個類
3.1.2.1 Demo1
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實作了三種不同職責,違背了單一職責原則
3.1.2.2 Demo2
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也做了修改,這樣改動太大
3.1.2.3 Demo3
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中改動量也很小
3.1.3 總結
? 1. 單一職責可以細化為類級別和方法級別,最低限度是在方法級別保證單一職責原則;但是如果一個類中有幾十個方法,那么就需要衡量下是否需要進行類分解
? 2. 提高代碼可讀性,可維護性,可擴展性
? 3. 降低類的復雜度
3.2 介面隔離原則(Interface Segregation Principle,ISP)
3.2.1 解釋
? 一個類對另一個類的依賴應該建立在最小的介面上,即一個類不應該依賴它不需要的介面
3.2.2 舉例
? 就拿涼拌黃瓜和炒黃瓜的步驟來舉例
涼拌黃瓜:洗菜 -> 切菜 -> 涼拌
炒 黃 瓜:洗菜 -> 切菜 -> 炒菜
3.2.2.1 Demo1
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介面的設計不合理,不符合介面隔離原則
3.2.2.2 Demo2
? 我們注意到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);
}
}
3.2.3 總結
? 1.提高代碼可讀性,可重用性,可維護性
? 3.降低類的復雜度,降低耦合性
3.3 依賴倒置 / 倒轉原則(Dependency Inversion Principle,DIP)
3.3.1 解釋
? 1. 依賴倒置/倒轉的核心是面向介面編程
? 2. 相對于細節的多變性,抽象的東西相對穩定得多;以抽象為基礎的架構比以細節為基礎的架構穩定得多;而在Java中抽象是指抽象類和介面,細節是指抽象類和介面的具體實作
? 3. 抽象類和介面可以理解為制定規范,但不涉及任何具體操作,把展現細節的作業交給他們具體的實作類完成,以此來提高系統的可靠性和可維護性
3.3.2 舉例
? 以付款場景為例
3.3.2.1 Demo1
public static void main(String[] args) {
Person person = new Person();
person.payment( new Ali() );
person.payment( new WeChat() );
person.payment( new BankCard() );
}
static class Person {
// 使用支付寶付款
public void payment(Ali type) {
System.out.println( type.pay() );
}
// 使用微信付款
public void payment(WeChat type) {
System.out.println( type.pay() );
}
// 使用銀行卡付款
public void payment(BankCard type) {
System.out.println( type.pay() );
}
}
static class Ali {
public String pay() {
return "通過 -支付寶- 付款";
}
}
static class WeChat {
public String pay() {
return "通過 -微信- 付款";
}
}
static class BankCard {
public String pay() {
return "通過 -銀行卡- 付款";
}
}
3.3.2.2 Demo2
3.3.3 總結
? 依賴關系傳遞的三種方式:介面傳遞、構造方法傳遞、Setter方法傳遞
3.3.3.1 介面傳遞
// 依賴關系傳遞-通過介面傳遞
public static void main(String[] args) {
IComputer computer = new ShineLon();
ISwitch s = new Switch();
s.turnOn(computer);
}
// 電腦介面
interface IComputer {
// 運行
void play();
}
// 神舟(炫龍)筆記本
static class ShineLon implements IComputer {
@Override
public void play() {
System.out.println("神舟(炫龍)筆記本,正在運行...");
}
}
// 開關介面
interface ISwitch {
// 打開【通過介面傳遞】
void turnOn(IComputer computer);
}
// 開關實作類
static class Switch implements ISwitch {
@Override
public void turnOn(IComputer computer) {
computer.play();
}
}
3.3.3.2 構造方法傳遞
// 依賴關系傳遞-通過構造方法傳遞
public static void main(String[] args) {
IComputer computer = new ShineLon();
ISwitch s = new Switch(computer);
s.turnOn();
}
// 電腦介面
interface IComputer {
// 運行
void play();
}
// 神舟(炫龍)筆記本
static class ShineLon implements IComputer {
@Override
public void play() {
System.out.println("神舟(炫龍)筆記本,正在運行...");
}
}
// 開關介面
interface ISwitch {
// 打開
void turnOn();
}
// 開關實作類
static class Switch implements ISwitch {
private final IComputer computer;
// 【通過構造方法傳遞】
Switch(IComputer computer) {
this.computer = computer;
}
@Override
public void turnOn() {
this.computer.play();
}
}
3.3.3.3 Setter方法傳遞
// 依賴關系傳遞-通過Setter方法傳遞
public static void main(String[] args) {
IComputer computer = new ShineLon();
Switch s = new Switch();
s.setComputer(computer);
s.turnOn();
}
// 電腦介面
interface IComputer {
// 運行
void play();
}
// 神舟(炫龍)筆記本
static class ShineLon implements IComputer {
@Override
public void play() {
System.out.println("神舟(炫龍)筆記本,正在運行...");
}
}
// 開關介面
interface ISwitch {
// 打開
void turnOn();
}
// 開關實作類
static class Switch implements ISwitch {
private IComputer computer;
@Override
public void turnOn() {
this.computer.play();
}
// 【通過Setter方法傳遞】
public void setComputer(IComputer computer) {
this.computer = computer;
}
}
3.4 里式替換原則(Liskov Substitution Principle,LSP)
3.4.1 解釋
? 里式替換原則規范了繼承的使用:在子類中盡量不要覆寫父類的方法,繼承會使兩個類的耦合性增強,可以改用組合,聚合,依賴的方式
3.4.2 舉例
3.4.2.1 Demo1
public static void main(String[] args) {
Parent parent1 = new Parent();
parent1.add(1, 2);
parent1.subtract(1, 2);
Son son = new Son();
son.add(1, 2);
son.subtract(1, 2);
}
static class Parent {
// 加法運算
public void add(int a, int b) {
System.out.println("a + b = " + (a + b));
}
// 減法運算
public void subtract(int a, int b) {
System.out.println("a - b = " + (a - b));
}
}
static class Son extends Parent {
// 減法運算
@Override
public void subtract(int a, int b) {
System.out.println("a * b = " + (a * b));
}
}
? 以上代碼中子類Son覆寫了父類Parent的subtract變為乘法運算了,但是對于呼叫者main來說,這一程序是透明的,這就導致了程式的錯誤,要解決這個問題,我們可以把有沖突的方法再封裝到另一個父類Base中,讓Son和Parent繼承這個更基礎的父類Base,原先的繼承關系去掉,使用依賴,聚合,組合等關系代替,
3.4.2.2 Demo2
public static void main(String[] args) {
Parent parent1 = new Parent();
parent1.add(1, 2);
parent1.subtract(1, 2);
Son son = new Son();
son.add(1, 2);
// 減法運算:Parent
son.subtract1(1, 2);
// 減法運算:Son
son.subtract2(1, 2);
son.multiply(1, 2);
}
// 更基礎的類
static class Base {
// 減法運算
public void subtract(int a, int b) {
System.out.println("a - b = " + (a - b));
}
}
static class Parent extends Base {
// 加法運算
public void add(int a, int b) {
System.out.println("a + b = " + (a + b));
}
}
static class Son extends Base {
private final Parent parent = new Parent();
// 減法運算:Parent
public void subtract1(int a, int b) {
// 呼叫Parent類的subtract方法
parent.subtract(a, b);
}
// 減法運算:Son
public void subtract2(int a, int b) {
System.out.println("a - b - 1 = " + (a - b - 1));
}
// 加法運算
public void add(int a, int b) {
// 呼叫Parent類的add方法
parent.subtract(a, b);
}
// 乘法運算
public void multiply(int a, int b) {
System.out.println("a * b = " + (a * b));
}
}
3.4.3 總結
- 克服子類覆寫父類方法導致重用性降低的問題
- 保證程式的正確性,對類的擴展不會給變動已有系統,降低了代碼出錯的可能
- 加強程式的健壯性,變更的同時也可以兼顧到兼容性
- 提高程式的可維護性、可擴展性
3.5 迪米特法則(Law of Demeter,LOD)
3.5.1 解釋
? 迪米特法則又叫最少知識原則(Least Knowledge Principle,LKP),即一個類對自己依賴的類知道的越少越好,比如A類中有個B類的成員變數,那么我們就說A類依賴了B類,對B類來說,不管邏輯有多復雜,都應該盡量將邏輯封裝在B類的內部(除了允許對外訪問的方法);還有另外一種更為直接的定義方式:只與直接的朋友通信,
? 直接的朋友:出現在類的成員變數,方法引數,方法回傳值中的類為直接的朋友,出現在區域變數中的類不是直接的朋友,也就是說,陌生類盡量不要以區域變數的形式出現在類的內部,
3.5.2 舉例
3.5.2.1 Demo1
public static void main(String[] args) {
School school = new School();
school.selectAllStudent();
System.out.println("---------------");
school.selectAllTeacher();
}
// 學校類
static class School {
// 查詢所有學生
public void selectAllStudent() {
//TODO Student以區域變數形式出現,不是直接的朋友
List<Student> students = new ArrayList<>();
for (int i = 0; i < 10; i++) {
students.add( new Student(String.valueOf(i)) );
}
for (Student student : students) {
System.out.println(student);
}
}
// 查詢所有老師
public void selectAllTeacher() {
//TODO Teacher以區域變數形式出現,不是直接的朋友
List<Teacher> teachers = new ArrayList<>();
for (int i = 0; i < 10; i++) {
teachers.add( new Teacher(String.valueOf(i)) );
}
for (Teacher teacher : teachers) {
System.out.println(teacher);
}
}
}
3.5.2.2 Demo2
public static void main(String[] args) {
School school = new School();
school.printAllStudent( school.selectAllStudent() );
System.out.println("---------------");
school.printAllTeacher( school.selectAllTeacher() );
}
// 學校類
static class School {
// 查詢所有學生
public List<Student> selectAllStudent() {
// Student以方法回傳值形式出現,是直接的朋友
List<Student> students = new ArrayList<>();
for (int i = 0; i < 10; i++) {
students.add( new Student(String.valueOf(i)) );
}
return students;
}
// 列印所有學生
public void printAllStudent(List<Student> students) {
// Student方法引數形式出現,是直接的朋友
for (Student student : students) {
System.out.println(student);
}
}
// 查詢所有老師
public List<Teacher> selectAllTeacher() {
// Teacher以方法回傳值形式出現,是直接的朋友
List<Teacher> teachers = new ArrayList<>();
for (int i = 0; i < 10; i++) {
teachers.add( new Teacher(String.valueOf(i)) );
}
return teachers;
}
// 列印所有老師
public void printAllTeacher(List<Teacher> teachers) {
// Teacher方法引數形式出現,是直接的朋友
for (Teacher teacher : teachers) {
System.out.println(teacher);
}
}
}
? 我們把Demo1中的Student,Teacher區域變數修改為方法回傳值的形式,并且作為printAllStudent,printAllTeacher的方法引數進行后續的列印操作,使得Student,Teacher成為School的直接的朋友,降低了類之間耦合性,
3.5.3 總結
? 迪米特法則核心其實就是降低類之間的耦合
3.6 合成 / 聚合復用原則(Composite / Aggregate Reuse Principle,C / ARP)
3.6.1 解釋
? 合成復用原則是說盡量使用合成 / 聚合的方式,避免使用繼承,因為繼承相對于合成或者聚合是一種強依賴(如果父類需要修改,那么就需要考慮是否會對其所有的子類產生影響)
3.6.2 舉例
? 我們以B類對A類中的test方法做增強為例,分別以繼承、組合、聚合、依賴的方式實作
3.6.2.1 Demo1
// 繼承關系
public static void main(String[] args) {
B b = new B();
b.test();
}
static class A {
public void test() {
System.out.println("A 類的 test 方法...");
}
}
static class B extends A {
@Override
public void test() {
// A類方法
super.test();
// B類對A類方法進行增強
System.out.println("B 類增強后的 test 方法...");
}
}
3.6.2.2 Demo2
// 組合關系
public static void main(String[] args) {
B b = new B();
b.test();
}
static class A {
public void test() {
System.out.println("A 類的 test 方法...");
}
}
static class B {
private A a = new A();
public void test() {
// A類方法
a.test();
// B類對A類方法進行增強
System.out.println("B 類增強后的 test 方法...");
}
}
3.6.2.3 Demo3
// 聚合關系
public static void main(String[] args) {
B b = new B();
b.setA( new A() );
b.test();
}
static class A {
public void test() {
System.out.println("A 類的 test 方法...");
}
}
static class B {
private A a;
public void test() {
// A類方法
a.test();
// B類對A類方法進行增強
System.out.println("B 類增強后的 test 方法...");
}
public void setA(A a) {
this.a = a;
}
}
3.6.2.4 Demo4
// 依賴關系
public static void main(String[] args) {
B b = new B();
b.test( new A() );
}
static class A {
public void test() {
System.out.println("A 類的 test 方法...");
}
}
static class B {
public void test(A a) {
// A類方法
a.test();
// B類對A類方法進行增強
System.out.println("B 類增強后的 test 方法...");
}
}
3.6.3 總結
- 維持類的封裝性
- 降低類之間耦合度
- 提高可復用性
3.7 開閉原則(Open Closed Principle,OCP)
3.7.1 解釋
? 把開閉原則放到最后講,是因為開閉原則是七大原則中最基礎,也是最重要的設計原則,其他六種設計原則均是為了實作開閉原則,
? 一個軟體物體(類,模塊,函式)應該保證:對擴展開放(提供方),對修改關閉(使用方),用抽象構建框架,用實作擴展細節,當軟體需要變化時,盡量通過擴展軟體物體的方式來實作變化,而不是通過修改已有的代碼來實作變化,
3.7.2 舉例
3.7.2.1 Demo1
public static void main(String[] args) {
PaintBrush brush = new PaintBrush();
brush.draw( new Triangle() );
brush.draw( new Rectangle() );
}
static class PaintBrush {
// 使用方(對修改關閉)
public void draw(Shape shape) {
switch (shape.type) {
case 1:
drawTriangle();
break;
case 2:
drawRectangle();
break;
//TODO 擴展分支
}
}
private void drawTriangle() {
System.out.println("畫三角形");
}
private void drawRectangle() {
System.out.println("畫長方形");
}
//TODO 擴展方法
}
// 提供方(對擴展開放)
static class Shape {
public int type;
}
static class Triangle extends Shape {
public Triangle() {
super.type = 1;
}
}
static class Rectangle extends Shape {
public Rectangle() {
super.type = 2;
}
}
//TODO 擴展類
? 以上代碼為例,如果我們想要再新加一個型別,就需要在注釋//TODO的地方做擴展,PaintBrush作為使用方也需要做修改,違反了開閉原則
3.7.2.2 Demo2
public static void main(String[] args) {
PaintBrush brush = new PaintBrush();
brush.draw( new Triangle() );
brush.draw( new Rectangle() );
}
static class PaintBrush {
// 使用方(對修改關閉)
public void draw(Shape shape) {
shape.draw();
}
}
// 提供方(對擴展開放)
static abstract class Shape {
// 抽象方法,由子類實作細節
public abstract void draw();
}
static class Triangle extends Shape {
@Override
public void draw() {
System.out.println("畫三角形");
}
}
static class Rectangle extends Shape {
@Override
public void draw() {
System.out.println("畫長方形");
}
}
//TODO 擴展類
? 代碼根據開閉原則優化后,在新增型別時,只需要在提供方Shape新增子類即可,使用方PaintBrush代碼無需修改
3.7.3 總結
- 對軟體測驗來說,如果遵守開閉原則,那么只需要測驗擴展的部分代碼,原有代碼無需測驗
- 提高代碼可維護性、可復用性
4、設計模式的工具
? 提起設計模式,很多人感覺無從下手,因為類與類、類與介面之間的繼承、實作等關系特別復雜,看著看著就被繞進去了,我們借助一種具象化的工具:UML圖,來幫助我們理解復雜的關系
統一建模語言(Unified Modeling Language,UML)是一種為面向物件系統的產品進行說明、可視化和編制檔案的一種標準語言,是非專利的第三代建模和規約語言,UML是面向物件設計的建模工具,獨立于任何具體程式設計語言,
? ---- 百度百科
? UML圖本質就是用一系列的符號來表示軟體模型中各個元素及其之間的關系(比如Java中的類,介面,依賴,實作,泛化,組合,聚合等元素或關系),UML圖型別眾多,這里只介紹UML類圖:
4.1 UML類圖表示方法
? 下面使用Java代碼與UML類圖對照的方式展示
4.1.1 具體類
// 人
public class Person {
// 性別
public String sex;
// 生日
protected String birth;
// 作業
String work;
// 年齡
private short age;
public String getSex() {
return sex;
}
protected String getBirth() {
return birth;
}
String getWork() {
return work;
}
private short getAge() {
return age;
}
public void setSex(String sex) {
this.sex = sex;
}
protected void setBirth(String birth) {
this.birth = birth;
}
void setWork(String work) {
this.work = work;
}
private void setAge(short age) {
this.age = age;
}
}
? 具體類在UML類圖中用矩形表示,矩形的第一層是類名,第二層是成員變數,第三層是成員方法,成員變數和成員方法前的訪問修飾符都是由符號表示:
- ? “+” 表示 “public”
- ? “#” 表示 “protected”
- ? “-” 表示 “private”
- 不加符號 表示 “default”
? 方法引數表示為:
? method(變數名1:變數型別1, 變數名2:變數型別2, ... , 變數名n:變數型別n):回傳值型別

4.1.2 抽象類
// 接收方
public abstract class Receiver {
// 訊息
private String message;
// 獲取接收型別
protected abstract String getType();
public String getMessage() {
return message;
}
}
? 抽象類與具體類基本相同,只是矩形第一層的抽象類名是斜體的,成員方法中的抽象方法也是斜體的

4.1.3 介面
// 形狀
public interface Shape {
// 獲取尺寸
int getSize();
}
? 介面在矩形的第一層中第一行為 <<Interface>>*做介面標識,第二行為介面名

4.2 UML類圖表示關系
4.2.1 依賴關系(Dependency)
? 簡單來說,只要類中用到了另一個類,他們之間就存在了依賴關系(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();
}
}
? 類中用到了另一個類包括以上幾種情況:
- 類的成員變數
- 類中方法的引數
- 類中方法的回傳值
- 類的方法中使用到(區域變數)
4.2.2 泛化關系(Generalization)
? 泛化關系實際上就是繼承關系,是依賴關系的特例(在UML類圖中用帶空心三角的實線表示)

public class User extends Person {}
4.2.3 實作關系(Realization / Implementation)
? 實作關系是指類A實作了介面B,是依賴關系的特例(在UML類圖中用帶空心三角的虛線表示)

public class UserServiceImpl implements IUserService{}
4.2.4 關聯關系(Association)
? 關聯關系是類與類之間存在的聯系,是依賴關系的特例(在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;
}
}
4.2.5 聚合關系(Aggregation)
? 聚合關系是指整體與部分的關系,整體和部分可以分開(比如電腦和滑鼠,滑鼠是電腦的一部分,可以分開),是關聯關系的特例,所以同樣具有導航性和多重性(在UML類圖中用空心菱形加實線箭頭表示,空心菱形在整體一方,箭頭指向部分一方,表示把部分聚合到整體中來)

// 聚合關系
public class Aggregation_01 {
// 人
static class Person {
private IDCard idCard;
}
// 身份證
static class IDCard {
}
}
4.2.6 組合關系(Composition)
? 組合關系是指整體與部分的關系,整體和部分不可以分開(比如人的身體和頭,頭是身體的一部分,不可以分開),是關聯關系的特例,所以同樣具有導航性和多重性(在UML類圖中用實心菱形加實線箭頭表示,實心菱形在整體一方,箭頭指向部分一方,表示把部分組合到整體中來)

// 組合關系
public class Composition_01 {
// 人
static class Person {
private IDCard idCard = new IDCard();
}
// 身份證
static class IDCard {
}
}
4.3 UML類圖畫圖軟體
? 我使用的是draw.io(在線版本、PC版都有),支持多種語言
? 下載鏈接:https://github.com/jgraph/drawio-desktop/releases
? 主界面如下:
5、設計模式的型別
? 以下羅列了常見的23中設計模式:
創建型模式:
- 單例模式
- 工廠模式
- 抽象工廠模式
- 原型模式
- 建造者模式
結構型模式:
- 配接器模式
- 橋接模式
- 裝飾模式
- 組合模式
- 外觀模式
- 享元模式
- 代理模式
行為型模式:
- 模板方法模式
- 命令模式
- 訪問者模式
- 迭代器模式
- 觀察者模式
- 中介者模式
- 備忘錄模式
- 解釋器模式(Interpreter模式)
- 狀態模式
- 策略模式
- 職責鏈模式(責任鏈模式)
6、相關原始碼
? 本篇章完整代碼:https://github.com/yushixin-1024/DesignPattern
? PS:代碼不涉及具體業務邏輯,僅僅是為了舉例,方便理解,
本文來自博客園,作者:颯沓流星,轉載請注明原文鏈接:https://www.cnblogs.com/yushixin1024/p/16427453.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/498984.html
標籤:設計模式
上一篇:java觀察者模式的簡介說明
