設計模式就是針對某類特定問題的程式碼設計經驗。
複用成功的設計模式,可以減低開發成本和週期、提高複用、程式碼適用擴充和可維護性。
設計原則
-
單一職責原則
一個類只有一個引起他的變化。如果一個類承擔太多責任,耦合度太高,一個職責變化可能會影響到其他職責。
-
開閉原則
對修改關閉,對擴充開放。
每次發生變化,通過新增程式碼增強實現,不是修改原始碼
-
里氏替換原則
任何出現父類的地方都可以用他的子類替代
-
迪米特原則
也叫最少知道原則。只與你的朋友通訊,不要和陌生人說話。外觀模式和中介模式都是用了迪米特法則。
-
介面隔離原則
一個介面儘量實現單一的功能。不要把把多個功能放入一個總介面中,而應該把每個職責分離到多個專門的介面中,進行介面分離
-
依賴倒置原則
面向介面程式設計而不是面向實現程式設計,可以降低耦合性。
細節依賴於抽象,抽象依賴於細節
-
合成複用原則
在一個新物件中使用已有的物件,讓他成為新物件的部分。儘量使用合成、聚合,儘量不要使用繼承。
建立型
單例模式
實現一個類只有一個例項,並提供全域性訪問。
複製程式碼
類圖
雙重檢查機制實現
public class Singleton {
private Singleton() {
};
private volatile static Singleton singleton = null;
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
複製程式碼
靜態內部類實現
public class SingletonInnner{
private SingletonInnner(){};
static class SingletonHolder{
private final static SingletonInnner instance = new SingletonInnner();
}
public static SingletonInnner getInstance(){
return SingletonHolder.instance;
}
public static void main(String args[]) {
SingletonInnner s1, s2;
s1 = SingletonInnner.getInstance();
s2 = SingletonInnner.getInstance();
System.out.println(s1 == s2);
}
}
複製程式碼
列舉型別寫法
public enum SingletonEnum {
INSTANCE;
public void test(){
System.out.println("hello world");
}
public static void main(String[] args) {
SingletonEnum.INSTANCE.test();
}
}
複製程式碼
優點
- 提供對唯一例項的受控訪問
- 在系統記憶體中只存在一個物件,因此可以節約系統資源,對於一些需要頻繁建立和銷燬的物件單例模式無疑可以提高系統的效能
- 可擴充套件,如實現雙例項、多例項等
缺點
- 單例類職責過重,一定程度上違背了單一職責原則
簡單工廠模式
簡單工廠又是靜態方法模式,負責生產物件的一個類,稱為工廠類。
類例項化和類使用分開,使用者不必知道具體怎樣產出所需產品類。
類圖
實現
//抽象產品
public abstract class Product {
public abstract void show();
}
複製程式碼
//產品A
public class ProductA extends Product {
@Override
public void show() {
System.out.println("show A");
}
}
複製程式碼
//產品B
public class ProductB extends Product {
@Override
public void show() {
System.out.println("show B");
}
}
複製程式碼
//工廠類
public class Factory {
public Product getProduct(String productName) {
if ("A".equals(productName)) {
return new ProductA();
}
if ("B".equals(productName)) {
return new ProductB();
}
return null;
}
}
複製程式碼
//使用
public class Main {
public static void main(String[] args) {
Factory factory = new Factory();
Product productA = factory.getProduct("A");
productA.show();
Product productB = factory.getProduct("B");
productB.show();
}
}
複製程式碼
優點
- 將建立例項的工作與使用例項的工作分開,使用者不必關心類物件如何建立,實現瞭解耦
- 把初始化例項時的工作放到工廠裡進行,使程式碼更容易維護。
- 符合物件導向的原則、 面向介面程式設計,而不是面向實現程式設計。
缺點
- 工廠類集中了所有例項(產品)的建立邏輯,一旦這個工廠不能正常工作,整個系統都會受到影響;
- 違背“開放 - 關閉原則”,一旦新增新產品就不得不修改工廠類的邏輯,這樣就會造成工廠邏輯過於複雜。
- 簡單工廠模式由於使用了靜態工廠方法,靜態方法不能被繼承和重寫,會造成工廠角色無法形成基於繼承的等級結構
適用場景
- 客戶如果只知道傳入工廠類的引數,對於如何建立物件的邏輯不關心時;
- 當工廠類負責建立的物件(具體產品)比較少時。
工廠方法模式
通過定義工廠父類負責定義建立物件的介面,而子類負責生成具體的物件。
將類的例項化延遲到工廠類的子類中完成,由子類覺得應該例項化哪個類。
類圖
實現
//抽象產品
public abstract class Product {
public abstract void show();
}
複製程式碼
//產品A
public class ProductA extends Product {
@Override
public void show() {
System.out.println("show A");
}
}
複製程式碼
//產品B
public class ProductB extends Product {
@Override
public void show() {
System.out.println("show B");
}
}
複製程式碼
//抽象工廠類
public abstract class AbstarctFactory {
public abstract Product getProduct();
}
複製程式碼
//工廠A
public class FactoryA extends AbstarctFactory {
@Override
public Product getProduct() {
return new ProductA();
}
}
複製程式碼
//工廠B
public class FactoryB extends AbstarctFactory{
@Override
public Product getProduct() {
return new ProductB();
}
}
複製程式碼
//使用
AbstarctFactory factoryA = new FactoryA();
Product productA1 = factoryA.getProduct();
productA1.show();
AbstarctFactory factoryB = new FactoryB();
Product productB1 = factoryB.getProduct();
productB1.show();
複製程式碼
優點
- 符合開閉原則。當新增一種產品時,新增對應的產品和對應的工廠即可
- 單一職責原則,一個工廠只生產對應的產品
- 不使用靜態工廠方法,可以形成基於繼承的等級結構
缺點
- 新增新產品時,要增加新產品類和提供與之對應的具體工廠類,系統類的個數將成對增加,在一定程度上增加了系統的複雜度;同時,有更多的類需要編譯和執行,會給系統帶來一些額外的開銷;
- 由於考慮到系統的可擴充套件性,需要引入抽象層,在客戶端程式碼中均使用抽象層進行定義,增加了系統的抽象性和理解難度,且在實現時可能需要用到DOM、反射等技術,增加了系統的實現難度。
- 雖然保證了工廠方法內的對修改關閉,但對於使用工廠方法的類,如果要更換另外一種產品,仍然需要修改例項化的具體工廠類
- 一個具體工廠只能建立一種具體產品
適用場景
- 當一個類不知道它所需要的物件的類時 在工廠方法模式中,客戶端不需要知道具體產品類的類名,只需要知道所對應的工廠即可;
- 當一個類希望通過其子類來指定建立物件時
- 將建立物件的任務委託給多個工廠子類中的某一個,客戶端在使用時可以無須關心是哪一個工廠子類建立產品子類,需要時再動態指定,可將具體工廠類的類名儲存在配置檔案或資料庫中。
抽象工廠模式
抽象工廠相對於工廠方法模式來說,抽象工廠中每個工廠可以建立多種類的產品;而工廠方法每個工廠只能建立一類。這裡略過...
類圖
建造者模式
將一個複雜物件的構建與他的表示分離,使得用同樣的構建過程可以建立不同的表示
在使用者不知道物件的建造過程和細節的情況下就可以直接建立出複雜的物件。
使用者只需給出物件的型別和記憶體,建造者模式負責按順序建立複雜物件。
類圖
實現
//product
//套餐類
public class Meal {
private String food;
private String drink;
public String getFood() {
return food;
}
public void setFood(String food) {
this.food = food;
}
public String getDrink() {
return drink;
}
public void setDrink(String drink) {
this.drink = drink;
}
}
複製程式碼
//builder
//套餐構造器
public abstract class MealBuilder {
Meal meal = new Meal();
public abstract void buildFood();
public abstract void buildDrink();
public Meal getMeal() {
return meal;
}
}
複製程式碼
//具體產品A
//套餐A
public class MealA extends MealBuilder{
@Override
public void buildFood() {
meal.setFood("一盒薯條");
}
@Override
public void buildDrink() {
meal.setDrink("一杯可樂");
}
}
複製程式碼
//Direct
//肯德基服務員,相當於一個指揮者
public class KFCWaiter {
private MealBuilder mealBuilder;
public void setMealBuilder(MealBuilder mealBuilder) {
this.mealBuilder = mealBuilder;
}
public Meal construct(){
//準備食物
mealBuilder.buildFood();
//準備飲料
mealBuilder.buildDrink();
//準備完畢,返回一個完整的套餐給客戶
return mealBuilder.getMeal();
}
}
複製程式碼
//使用
public class Main {
public static void main(String[] args) {
//服務員
KFCWaiter waiter = new KFCWaiter();
//套餐A
MealA a = new MealA();
//服務員準備套餐A
waiter.setMealBuilder(a);
//獲得套餐
Meal mealA = waiter.construct();
System.out.print("套餐A的組成部分:");
System.out.println(mealA.getFood()+"---"+mealA.getDrink());
}
}
複製程式碼
優點
- 解耦
- 精確控制物件的建立
- 擴充套件,新增具體構建者無需修改原來的程式碼
缺點
- 使用範圍受限,產品間差異大的不適合使用建造者模式
- 內部變化複雜,會有很多的建造類。
使用場景
- 需要生產的物件具有複雜的內部結構,常用的StringBuffer、StringBuilder、以及Swagger(一種介面文件),都是以這種模式構建物件的
- 構建具有共同特性的複雜物件
原型模式
用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件。
工作原理是:通過將一個原型物件傳給那個要發動建立的物件,這個要發動建立的物件通過請求原型物件拷貝它們自己來實施建立。
類圖
淺克隆
深克隆
優點
- 建立新的物件比較複雜時,可以簡化物件建立過程,提高效率
- 使用深克隆保持物件的狀態
缺點
- 深克隆時可能需要很複雜的程式碼:原型模式最大的缺點就在於每個原型的子類都必須實現clone的操作,尤其在包含引用型別的物件時,clone方法會比較麻煩,必須要能夠遞迴的讓所有的相關物件都要正確的實現克隆。
使用場景
- 建立新物件成本較大(如初始化需要佔用較長的時間,佔用太多的CPU資源或網路資源),新的物件可以通過原型模式對已有物件進行復制來獲得