UML建模
統一建模語言(UML)是用來設計軟件的可視化建模語言。它的語言特點是簡單 統一 圖形化 能表達軟件設計中的動態與靜態信息。
UML的分類
動態結構圖: 類圖 對象圖 組件圖 部署圖
動態行為圖: 狀態圖 活動圖 時序圖 協作圖 構件圖等
在UML類圖中表示具體的類
在類的UML圖中,使用長方形描述一個類的主要構成,長方形垂直地分為三層,以此放置類的名稱、屬性和方法。
在UML類圖中表示抽象類
抽象類在UML類圖中同樣用矩形框表示,但是抽象類的類名以及抽象方法的名字都用斜體字表示,如圖所示。
在UML類中表示接口
接口在類圖中也是用矩形框表示,但是與類的表示法不同的是,接口在類圖中的第一層頂端用構造型<<interface>>表示,下面是接口的名字,第二層是方法。
在UML類圖中表示關系
類和類、類和接口、接口和接口之間存在一定關系,UML類圖中一般會有連線指明它們之間的關系。
軟件設計原則
1.開閉原則
定義:一個軟件實體類 模塊和函數應該對擴展開放,對修改關閉。
用抽象構建框架,用實現擴展細節
優點: 提高軟件系統的可復用性及維護性
開閉原則是所有的設計模式的最核心的目標,頂層設計思維:
- 抽象意識
- 封裝意識
- 擴展的意思
2.依賴倒置原則
定義: 高層模塊不應該依賴底層模塊,二者都應該依賴其抽象
抽象不應該依賴細節;細節應該依賴抽象
針對接口編程,不要針對實現編程
優點: 可以減少類間的耦合度 提高系統穩定性 提高代碼可讀性和可維護性, 可降低修改程序所造成的風險。
3.單一職責原則
定義: 不要存在多余一個導致類變更的原因
一個類/接口/方法只負責一項職責
優點: 降低類的復雜度 提高類的可讀性,提高系統的可維護性,降低變更引起的風險
如何判斷一個類的職責是否單一?
- 類中的代碼行數 函數 或者屬性過多
- 類依賴的其他的類過多
- 私有方法過多
- 類中的大量方法總是操作類中的幾個屬性
4.接口隔離原則
定義: 用多個專門的接口,而不使用單一的總接口,客戶端不應該依賴它不需要的接口
一個類對一個類的依賴應該建立在最小的接口上
建立單一接口,不要建立龐大臃腫的接口
盡量細化接口,接口中的方法盡量少
注意適度原則,一定要適度
優點:符合我們常說的高內聚低耦合的設計思想
從而使得類具有很好的可讀性,可擴展性和可維護性。
遵循接口隔離原則的優勢
- 將臃腫接口分解成多個粒度曉得接口,可以提高系統的靈活性喝可維護性
- 使用多個專門的接口,還能體現出對象的層次
- 能顧減少項目工程中的冗余代碼
5.迪米特法則
定義: 一個對象應該對其他對象保持最少的了解。又叫最少知道原則
盡量降低類與類之間的耦合
優點: 降低類之間的耦合
6.里氏替換原則
什么是替換
替換的前提是面向對象語言所支持的多態特性,同一個行為具有多個不同表現形式或形態的能力
簡單說就是當我的一個方法的參數是一個接口類型時,這個方法可以接收所有實現過這個接口的實現類
什么是期望行為一致的替換
在不了解派生類的情況下,僅通過接口或基類的方法,即可清楚的知道方法的行為,而不管哪種派生類的實現,都與接口或基類方法的期望行為一致。
7.總結
單一職責(SRP)
- 概念:一個類只負責完成一個職責或功能
- 作用:1.提高類的內聚性 2.實現代碼的高內聚 低耦合
- 不滿足的4種情況: 類中的代碼行數 函數 或者屬性過多
類依賴的其他類過多
私有方法過多
類種大量的方法都是集中操作類中的幾個屬性
開閉原則(OCP):
概念: 對擴展開放,對修改關閉
開閉原則并不是說完全的杜絕修改,而是以最小的修改代碼的代價來完成新功能的開發
作用:新邏輯解耦,需要發生改變不會影響老業務的邏輯
改動成本最小,只需要追加新邏輯,不需要改的老邏輯
提高代碼的穩定性喝可擴展性
如何做到開閉原則:
鍛煉頂層思維:擴展意識 抽象意識 封裝意識
提高代碼擴展性的方式:多態 依賴注入 面向接口編程 合理使用設計模式
里氏替換原則(LSP):子類對象能夠替換程序中父類對象出現的任何地方,并且保證原來程序的邏輯行為不變及正確性不被破壞
作用:為良好的繼承定義了一個規范
提高代碼的健壯性,降低程序出錯的可能性
里氏替換原則與多態的區別:
多態是面向對象的一大特性,也是面向對象編程語言的一種語法。它是一種代碼實現的思路。
里氏替換是一種設計原則,用來指導繼承關系中子類該如何設計,在替換父類的時候,不改變原有程序的邏輯及不破壞原有程序的正確性。
接口隔離原則(ISP)
一個類對另一個類的依賴應該建立在最小的接口上,要為各個類建立它們需要的專用接口,而不要試圖建立一個很龐大的接口供所有依賴它的類去調用。
作用:提高系統的靈活性喝可維護性
減少項目工程中的代碼冗余
接口隔離原則與單一職責原則的區別:
單一職責原則注重的是職責,而接口隔離原則注重的是對接口依賴的隔離。
單一職責原則主要是約束類,它針對的是程序中實現和細節;
接口隔離原則主要是約束接口,主要爭對抽象和程序整體框架的構建。
依賴倒置原則(DIP)
概念: 高層模塊不應該依賴于底層模塊,二者都應該依賴于抽象。抽象不應該依賴于細節,細節應該依賴于抽象。
作用:
減少類間的耦合性,提高系統的穩定性
降低并行開發引起的風險
提高代碼的可讀性和可維護性
依賴倒置 控制反轉 依賴注入 :
依賴倒置: 是一種通用的軟件設計原則,主要用來指導框架層面的設計
控制反轉: 與依賴倒置有一點相似,它也是一種框架設計常用的模式,但并不是具體的方法。
依賴注入: 是實現控制反轉的一個手段,它是一種具體的編碼技巧。
迪米特法則(LKP):
概念: 不該有直接依賴關系的類之間,不要有依賴,有依賴的類之間,盡量只依賴必要的接口。
作用:如果兩個軟件實體無須直接通信,那么就不應當發生直接的相互調用,可以通過第三方轉發調用。其目的是降低類之間的耦合度,提高模塊的相對獨立性。
使用注意:過渡使用迪米特法則回使系統大量的中介類,從而增加系統的復雜性,使模塊之間的通信效率降低。所以,在采用迪米特法則時需要反復權衡,確保高內聚和低耦合的同時,保證系統的結構清晰。
創建型
簡單工廠
適用場景: 工廠類負責創建的對象比較少
客戶端(應用層)只知道傳入工廠類的參數,對于如何創建對象(邏輯)不關心
優點: 只需要傳入一個正確的參數,就可以獲取你所需要的對象,而無須知道其創建細節
缺點:工廠類的職責相對過重,增加新的產品 需要修改工廠類的判斷邏輯,違背了開閉原則。
/*** 簡單工廠Demo*/
public class VideoFactor {//利用反射public Video getVideo(Class c){Video video = null;try{video=(Video) Class.forName(c.getName()).newInstance();} catch (ClassNotFoundException e) {throw new RuntimeException(e);} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);}return video;}// public Video getVideo(String type) {// if ("java".equals(type)) {// return new JavaVideo();// }else if ("python".equals(type)) {// return new PythonVideo(); // }// return null;// }
}
工廠模式
定義:定義一個創建對象的接口,但讓實現這個接口的類來決定實例化那個類,工廠方法讓類的實例化推遲到子類中進行。
適用場景: 創建對象需要大量重復的代碼
客戶端(應用層)不依賴于產品類實例如何被創建 實現等細節
一個類通過其子類來創建那個對象
優點:用戶只需要關心所需產品對應的工廠,無須關心創建細節
加入新產品符合開閉原則,提高可擴展性
缺點: 類的個數容易過多,增加復雜度
增加了系統的抽象性和理解難度
抽象工廠模式
定義:抽象工廠模式提供一個創建一些列相關或相互依賴對象的接口
無須指定它們具體的類
適用場景:客戶端(應用層)不依賴于產品類實例如何被創建 實現等細節
強調一些列相關的產品對象(屬于同一產品族)一起適用創建對象需要大量重復的代碼
提供一個產品類的庫,所有的產品以同樣的接口出現,從而使客戶端不依賴于具體實現。
優點: 具體產品在應用層代碼隔離,無須關心創建細節
將一個系列的產品族統一到一起創建
缺點:規定了所有可能被創建的產品集合,產品族中擴展新的產品困難,需要修改抽象工廠的接口。
增加了系統的抽象性和理解難度
建造者模式
定義: 將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示
用戶只需指定需要建造的類型就可以得到它們,建造過程及細節不需要知道
適用場景: 如果一個對象有非常復雜的內部結構(很多屬性)
想把復雜對象的創建和使用分離
優點: 封裝性好,創建和使用分離
擴展性好 建造類之間獨立,一定程度上解耦
缺點:
產生多余的Builder對象
產品內部發生變化,建造者都要修改,成本較大
package com.keyi.design.parrttern.builder.v2;public class Course {private String CourseName; private String CoursePPT;private String CourseVideo;private String CourseArticle;public Course(CourseBuilder courseBuilder) {this.CourseName = courseBuilder.courseName;this.CoursePPT = courseBuilder.coursePPT;this.CourseVideo = courseBuilder.courseVideo;this.CourseArticle = courseBuilder.courseArticle;}@Overridepublic String toString() {return "Course{" +"CourseName='" + CourseName + '\'' +", CoursePPT='" + CoursePPT + '\'' +", CourseVideo='" + CourseVideo + '\'' +", CourseArticle='" + CourseArticle + '\'' +'}';}public static class CourseBuilder {private String courseName;private String coursePPT;private String courseVideo;private String courseArticle;public CourseBuilder builderCourseName(String CourseName) {this.courseName = CourseName;return this;}public CourseBuilder builderCoursePPT(String coursePPT) {this.coursePPT = coursePPT;return this;}public CourseBuilder builderCourseVideo(String courseVideo) {this.courseVideo = courseVideo;return this;}public CourseBuilder builderCourseArticle(String courseArticle) {this.courseArticle = courseArticle;return this;}public Course build() {return new Course(this);}}
}
單例模式
保證一個類僅有一個實例,并提供一個全局訪問點
適用場景:想確保任何情況下都絕對只有一個實例
優點: 在內存中只有一個實例,減少了內存開銷
可以避免對資源的多重占用
設置全局訪問點,嚴格控制訪問
缺點: 沒有接口,擴展困難
重點: 私有構造器 線程安全 延遲加載 序列化和反序列化安全 反射
單例模式和工廠模式
單例模式和享元模式
懶漢式
特點: 線程安全 內存消耗低 資源開銷大 性能有影響
public class LazySingleton {private static LazySingleton lazySingleton=null;private LazySingleton() {}public synchronized static LazySingleton getInstance(){if(lazySingleton==null){lazySingleton=new LazySingleton();}return lazySingleton;}
}
Double Check 雙重檢查鎖的機制
public class LazySingletonDoubleCheck {//volatile 防止多線程中指令重排序 CPU共享內存 可見性private volatile static LazySingletonDoubleCheck lazySingletonDoubleCheck=null;private LazySingletonDoubleCheck() {}public synchronized static LazySingletonDoubleCheck getInstance(){if(lazySingletonDoubleCheck==null){synchronized (LazySingletonDoubleCheck.class){if(lazySingletonDoubleCheck==null){lazySingletonDoubleCheck=new LazySingletonDoubleCheck();//1.分配內存//2.初始化對象//3.設置LazySingletonDoubleCheck 指向剛分配的內存地址}}}return lazySingletonDoubleCheck;}
}
存在問題: 容易被反射破壞(反射可以修改構造器的權限)
public class StaticInnerClassSingleton {public static class InnerClass {private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();}private StaticInnerClassSingleton() {if (InnerClass.staticInnerClassSingleton != null) {throw new RuntimeException("單例模式禁止被初始化");}}public static StaticInnerClassSingleton getInstance() {return InnerClass.staticInnerClassSingleton;}
}
餓漢式
優點:類加載的時候就開始初始化 避免了線程同步的問題
缺點: 類加載的時候就開始初始化 沒有延遲加載的效果 從來沒有過 導致內存開銷浪費
/*** 餓漢式 類加載的時候就開始了初始化*/
public class HungrySingLeton {private final static HungrySingLeton hungrySingLeton = new HungrySingLeton();private HungrySingLeton() {if (hungrySingLeton!=null){throw new RuntimeException("單例模式禁止被初始化");}}public static HungrySingLeton getInstance(){return hungrySingLeton;}/*** 防止反射獲取對象* @return*/public Object readResolve() {return hungrySingLeton;
}
}return hungrySingLeton;}
}
可以防止反射攻擊(因為初始化的工作在類加載的時候就已經初始化好了,可以通過私有化構造器判斷這個對象是否初始化的狀態 來判定對象是否初始化)
枚舉單例模式
public enum EnumInstance {INSTANCE;private Object data;public Object getData() {return data;}public void setData(Object data) {this.data = data;}public static EnumInstance getInstance(){return INSTANCE;}
}
解決了反射和序列化和反序列化的問題 (反編譯看枚舉的構造方法是有參和私有的) 比較像餓漢式 (反編譯看初始化操作是靜態代碼塊執行的)
容器單例模式
/*** 容器單例模式*/
public class ContainerSingleton {private static Map<String, Object> singletonMap = new HashMap<>();private ContainerSingleton() {}public static void putInstance(String key, Object instance) {if (StringUtils.isNotBlank(key) && instance != null) {singletonMap.put(key, instance);}}public static Object getInstance(String key) { return singletonMap.get(key);}
}
原型模式
定義: 指原型實例指定創建對象的種類,并且通過拷貝這些原型創建新的對象
不需要指定任何創建的細節,不調用構造函數
適用場景
類初始化消耗較多資源
new產生的一個對象需要非常繁瑣的過程(數據準備 訪問權限等)
構造函數比較復雜
循環體中生產大量對象時
優點: 原型模式性能比直接new一個對象性能高
簡化創建過程
缺點: 必須配備克隆方法
對克隆復雜對象或對克隆出的對象進行復雜改造時,容易引入風險
深拷貝 淺拷貝要運用得當
package com.keyi.design.parrttern.prototype;
//淺克隆
public class Mail implements Cloneable {private String name;private String subject;private String body;public Mail() {System.out.println("Mail object created");}public String getSubject() {return subject;}public void setSubject(String subject) {this.subject = subject;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getBody() {return body;}public void setBody(String body) {this.body = body;}@Overridepublic String toString() {return "Mail{" +"name='" + name + '\'' +", subject='" + subject + '\'' +", body='" + body + '\'' +'}';}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
public class A implements Cloneable{@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
public class B extends A{public static void main(String[] args) throws CloneNotSupportedException {B b = new B();B clone = (B) b.clone();}
}
public class Pig implements Cloneable{public String name;private Date age;public Pig(String name, Date age) {this.name = name;this.age = age;}public Date getAge() {return age;}public void setAge(Date age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Pig{" +"name='" + name + '\'' +", age=" + age +'}'+super.toString();}//深克隆@Overrideprotected Object clone() throws CloneNotSupportedException {Pig pig = (Pig) super.clone();pig.age = (Date) age.clone();return pig;}}
結構型
外觀模式
定義: 又叫門面模式,提供了一個統一的接口,用來訪問子系統中的一群接口
外觀模式定義了一個高層接口,讓子系統更容易使用
適用場景: 子系統越來越復雜,增加外觀模式提供簡單調用接口
構建多層系統結構,利用外觀對象作為每層的入口,簡化層間調用
優點: 簡化了調用過程,無需了解深入子系統,防止帶來風險。
減少系統依賴 松散耦合
更好的劃分訪問層次
符合迪米特法則,即最少知道原則
缺點: 增加子系統,擴展子系統行為容易引入風險
不符合開閉原則
外觀模式和中介者模式
外觀模式和單例模式
外觀模式和抽象工作模式
裝飾著模式
定義:在不改變原有對象的基礎上,將功能附加到對象上
提供了比繼承更有彈性的替代方案(擴展原有對象功能)
適用場景: 擴展一個類的功能或給一個類添加附加職責
動態的給一個對象添加功能,這些功能可以再動態的撤銷
優點: 繼承的有力補充,比繼承靈活,不改變原有對象的情況下給一個對象擴展功能
通過使用不同裝飾類以及這些 裝飾類的排列組合,可以實現不同的效果。
符合開閉原則
缺點: 會出現更多的代碼,更多的類,增加程序復雜性
動態裝飾時,多層裝飾時會更復雜
裝飾著模式和代理模式
裝飾者模式和適配器模式
適配器模式
定義: 將一個類的接口轉換成客戶期望的另一個接口
使原本接口不兼容的類可以一起工作
應用場景: 已經存在的類,它的方法和需求不匹配的(方法結果相同或相似)
不是軟件設計階段考慮的模式,是隨著軟件維護,由于不同產品,不同廠家造成功能類似而接口不同情況下的解決方案。
優點: 能提高類的透明性和復用,現有的類復用但不需要改變
目標類和適配器類解耦,提高程序擴展性
符合開閉原則
缺點: 適配器編寫過程需要全面考慮,可能回增加系統的復雜性
增加系統代碼可讀的難度
擴展: 對象適配器 類適配器
適配器模式和外觀模式
類適配器
public interface Target {void request();
}
public class ConcreteTarget implements Target{@Overridepublic void request() {System.out.println("ConcreteTarget request");}
}
/*** 適配器模式*/
public class Adapter extends Adaptee implements Target {@Overridepublic void request() {super.adapteeRequest();}
}
public class Adaptee {public void adapteeRequest(){System.out.println("被適配者的方法");}
}
對象適配器
/*** 適配器對象模式*/
public class Adapter implements Target{private Adaptee adaptee=new Adaptee();@Overridepublic void request() {adaptee.adapteeRequest();}
}
享元模式
定義: 提供了減少對象數量從而改善應用所需的對象結構的方式
運用共享技術有效地支持大量細粒度的對象
使用場景: 常常應用于系統底層的開發,以便解決系統的性能問題。
系統有大量相似對象 需要緩沖池的場景
優點: 減少對象的創建,降低內存中對象的數量,降低系統的內存,提高效率
減少內存之外的其他資源占用
缺點: 內/外部狀態 關注線程安全問題
使系統 程序的邏輯復雜化
擴展: 內部狀態 外部狀態
享元模式和代理模式
享元模式和單例模式
public interface Employee {void report();
}
public class EmployeeFactory {private static final Map<String, Employee> EMPLOYEE_MAP = new HashMap<String, Employee>();private EmployeeFactory() {}public static Employee getManger(String department) {Manger manger = (Manger) EMPLOYEE_MAP.get(department);if (manger == null) {manger = new Manger(department);System.out.println("創建部門經理:" + department);manger.setReportContent(department + "部門匯報:......");EMPLOYEE_MAP.put(department, manger);}return manger;}}
public class Manger implements Employee{@Overridepublic void report() {System.out.println(reportContent);}private String department;private String reportContent;public Manger(String department) {this.department = department;}public void setReportContent(String reportContent) {this.reportContent = reportContent;}
}
組合模式
定義:將對象組合成樹形結構以表示“部分-整體”的層次結構
組合模式使客戶端對單個對象和組合對象保持一致的方式處理
適用場景: 希望客戶端可以忽略組合對象與單個對象的差異時
處理一個樹形結構時
優點: 清楚地定義分層次的復雜對象,表示對象的全部或部分層次
讓客戶端忽略了層次的差異,方便對整個層次結構進行控制
簡化客戶端代碼
符合開閉原則
缺點: 限制類型時會較為復雜
使設計變得更加抽象
組合模式和訪問者模式
橋接模式
定義: 將抽象部分與它的具體實現部分分離,使它們都可以獨立地變化
通過組合的方式建立兩個類之間聯系,而不是繼承
適用場景: 抽象和具體實現之間增加更多的靈活性
一個類存在兩個(或多個)獨立變化的維度,且這兩個(或多個)維度都需要獨立進行擴展。
不希望適用繼承,或因為多層繼承導致類的個數劇增
優點:分離抽象部分及其具體實現部分
提高了系統的可擴展性
符合開閉原則
缺點: 增加了系統的理解與設計難度
需要正確地識別出系統中兩個獨立變化的維度
橋接模式和組合模式
橋接模式和適配器模式
代理模式
定義: 為其他對象提供一種代理,以控制對這個對象的訪問
代理對象在客戶端和目標對象之間起到中介的作用
優點: 代理模式能將代理對象與真實被調用的目標對象分離
一定程序上降低了系統的耦合度,擴展性好
保護目標對象
增強目標對象
缺點:代理模式會造成系統設計中類的數目增加
在客戶端和目標對象增加一個代理對象,會造成請求處理速度變慢
增加系統的復雜度
代理模式和裝飾著模式
擴展:
靜態代理:
動態代理:
CGLib代理: