一 什么是設計模式
設計模式是一套反復使用的代碼設計總結,使用設計模式是為了可重用代碼、保證代碼可靠性、程式的重用性,熟悉設計模式能更容易看懂框架原始碼,更好的設計自己的系統,
二 設計模式分類
設計模式分為創建型、結構型和行為型三種模式,
三 設計模式的六大原則
開放封閉原則:盡量通過擴展軟體物體來解決需求變化,而不是通過修改已有的代碼來完成變化
里氏代換原則:使用的基類可以在任何地方使用繼承的子類,完美的替換基類,子類可以擴展父類的功能,但不能改變父類原有的功能,子類可以實作父類的抽象方法,但不能覆寫父類的非抽象方法【模版模式的鉤子方法不在此列】,子類中可以增加自己特有的方法
依賴倒轉原則:面向介面編程,我們在程式代碼中傳遞引數時或在關聯關系中,盡量參考層次高的抽象層類
介面隔離原則:使用多個隔離的介面,比使用單個介面要好
迪米特法則:一個類盡量減少自己對其他物件的依賴,簡稱類間解耦
單一職責原則:一個方法只負責一件事情
四 常用的設計模式
4.1 單例模式
使用場景:
網站計數器、應用程式日志應用、執行緒池
注意事項:
注意執行緒安全問題
代碼實作:
1 package com.leo.basic.code.design; 2 3 /** 4 * @DESCRIPTION 5 * 單例模式:采用一定的方式保證一個類在一個系統中只有一個實體 6 * 優點:單例模式保證了系統記憶體中該類只存在一個物件,節省了系統資源,對于一些需要頻繁創建銷毀的物件,使用單例模式可以提高系統性能 7 * 缺點:由于單利模式中沒有抽象層,因此單例類的擴展有很大的困難;不適用于變化的物件 8 * @DATE 2022-11-02 9 * @AUTHOR liuzhilong 10 */ 11 public class SingleTest { 12 13 public static void main(String[] args) { 14 // 1、餓漢式單例模式(靜態常量) 15 Singleton1 singleton1 = Singleton1.getInstance(); 16 Singleton1 singleton2 = Singleton1.getInstance(); 17 System.out.println(singleton1 == singleton2); 18 // 2、餓漢式單例模式(靜態代碼塊) 19 Singleton2 singleton21 = Singleton2.getInstance(); 20 Singleton2 singleton22 = Singleton2.getInstance(); 21 System.out.println(singleton21 == singleton22); 22 // 3、靜態內部類 23 Singleton3 singleton31 = Singleton3.getInstance(); 24 Singleton3 singleton32 = Singleton3.getInstance(); 25 System.out.println(singleton31 == singleton32); 26 } 27 28 29 } 30 // 實作方式 31 // 1、餓漢式單例模式(靜態常量) 32 class Singleton1 { 33 34 private static Singleton1 instance = new Singleton1(); 35 36 private Singleton1() { 37 38 } 39 40 public static Singleton1 getInstance() { 41 return instance; 42 } 43 } 44 45 // 2、餓漢式單例模式(靜態代碼塊) 46 class Singleton2 { 47 48 private static Singleton2 instance; 49 50 static { 51 instance = new Singleton2(); 52 } 53 54 private Singleton2() { 55 56 } 57 58 public static Singleton2 getInstance() { 59 return instance; 60 } 61 } 62 63 // 3、靜態內部類 64 class Singleton3 { 65 66 private Singleton3() { 67 68 } 69 // 采用了類裝載的機制來保證初始化實體時只有一個執行緒 70 private static class Singleton3Instance{ 71 private static final Singleton3 instance = new Singleton3(); 72 } 73 74 public static Singleton3 getInstance() { 75 return Singleton3Instance.instance; 76 } 77 } 78 79 // 4、列舉 80 enum Singleton4 { 81 INSTANCE; 82 } 83 84 85 // 5、雙重檢查(Double-check) 86 class Singleton5 { 87 88 private static Singleton5 instance; 89 90 private Singleton5() { 91 92 } 93 // 因為JVM本質重排序的原因,可能會初始化多次,不推薦使用 94 public static Singleton5 getInstance() { 95 if(instance == null){ 96 synchronized (Singleton5.class){ 97 if(instance == null){ 98 instance = new Singleton5(); 99 } 100 } 101 } 102 return instance; 103 } 104 }View Code
4.2 工廠模式
使用場景:
實體化物件、Spring IoC
注意事項:
代碼實作:
1 package com.leo.basic.code.design; 2 3 4 5 /** 6 * @DESCRIPTION 工廠模式: 7 * 簡單工廠:創建物件在工廠類中,工廠類很重【1類產品】 8 * 工廠方法:創建物件在具體工廠實作類中,工廠是個介面【1類產品】 9 * 抽象工廠:工廠中定義了一系列產品創建方法,創建物件在具體的工廠實作類中【多類產品】 10 * 優點:在創建物件時不會對客戶端暴露創建邏輯,實作了創建者和呼叫者分離 11 * 缺點: 12 * @DATE 2022-11-02 13 * @AUTHOR liuzhilong 14 */ 15 public class FactoryTest { 16 17 public static void main(String[] args) { 18 // 1、簡單工廠 19 Car aodi = SimpleCarFactory.createCar("奧迪"); 20 Car bmw = SimpleCarFactory.createCar("寶馬"); 21 aodi.run(); 22 bmw.run(); 23 24 // 2、工廠方法 25 Car aodi1 = new AoDiFactory().createCar(); 26 Car jili1 = new BmwFactory().createCar(); 27 aodi1.run(); 28 jili1.run(); 29 } 30 } 31 32 // Car 產品 33 interface Car { 34 35 void run(); 36 } 37 38 class Bmw implements Car { 39 40 @Override 41 public void run() { 42 System.out.println("Bmw"); 43 } 44 } 45 46 class AoDi implements Car { 47 48 @Override 49 public void run() { 50 System.out.println("AoDi"); 51 } 52 } 53 54 // 1、簡單工廠 55 class SimpleCarFactory { 56 57 public static Car createCar(String name) { 58 if ("".equals(name)) { 59 return null; 60 } 61 if (name.equals("奧迪")) { 62 return new AoDi(); 63 } 64 if (name.equals("寶馬")) { 65 return new Bmw(); 66 } 67 return null; 68 } 69 } 70 71 72 // 2、工廠方法 73 interface MethodCarFactory { 74 75 Car createCar(); 76 } 77 78 class AoDiFactory implements MethodCarFactory { 79 80 public Car createCar() { 81 return new AoDi(); 82 } 83 } 84 85 class BmwFactory implements MethodCarFactory { 86 87 public Car createCar() { 88 return new Bmw(); 89 } 90 } 91 92 // 3、抽象工廠 93 // 發動機產品 94 interface Engine { 95 void run(); 96 } 97 class EngineA implements Engine { 98 public void run() { 99 System.out.println("轉的快!"); 100 } 101 } 102 class EngineB implements Engine { 103 public void run() { 104 System.out.println("轉的慢!"); 105 } 106 } 107 108 interface TotalFactory { 109 // 創建汽車 110 Car createChair(); 111 // 創建發動機 112 Engine createEngine(); 113 } 114 // 上海工廠實作類,由他決定呼叫哪個工廠的那個實體 115 class ShanghaiFactory implements TotalFactory { 116 public Engine createEngine() { 117 return new EngineA(); 118 } 119 public Car createChair() { 120 return new AoDi(); 121 } 122 }View Code
4.3 模版模式
使用場景:
資料庫訪問的封裝、Junit單元測驗、servlet中關于doGet/doPost方法的呼叫
注意事項:
代碼實作:
1 package com.leo.basic.code.design; 2 3 /** 4 * @DESCRIPTION 5 * 模版:定義一個操作中的演算法骨架(父類),而將一些步驟延遲到子類中,是一個流程!!,主要子類不能覆寫父類的模版方法!!! 6 * 優點:公共的邏輯代碼抽取,代碼復用;封裝不變的部分,重寫可變的部分,易擴展 7 * 缺點:每來一個子類就要定義一套子類的規范,專案的體積會越來越大 8 * @DATE 2022-11-02 9 * @AUTHOR liuzhilong 10 */ 11 public class TemplateTest { 12 13 public static void main(String[] args) { 14 RestaurantTemplate restaurantTemplate = new RestaurantLobsterImpl(); 15 restaurantTemplate.process(); 16 } 17 } 18 19 abstract class RestaurantTemplate { 20 21 // 1.看選單 22 public void menu() { 23 System.out.println("看選單"); 24 } 25 26 // 2.點菜業務 27 abstract void spotMenu(); 28 29 // 3.吃飯業務 30 public void havingDinner() { 31 System.out.println("吃飯"); 32 } 33 34 // 4.付款業務 35 abstract void payment(); 36 37 // 5.走人 38 public void goR() { 39 System.out.println("走人"); 40 } 41 42 //模板通用結構 43 public void process() { 44 menu(); 45 spotMenu(); 46 havingDinner(); 47 payment(); 48 goR(); 49 } 50 } 51 52 class RestaurantLobsterImpl extends RestaurantTemplate { 53 54 void spotMenu() { 55 System.out.println("龍蝦"); 56 } 57 58 void payment() { 59 System.out.println("50塊"); 60 } 61 }View Code
4.4 策略模式
使用場景:
策略模式的用意是針對一組演算法或邏輯,將每一個演算法或邏輯封裝到具有共同介面的獨立的類中,從而使得它們之間可以相互替換注意事項:
代碼實作:
1 package com.leo.basic.code.design; 2 3 /** 4 * @DESCRIPTION 5 * 策略:定義了一系列的演算法 或 邏輯 或 相同意義的操作,并將每一個演算法、邏輯、操作封裝起來,而且使它們還可以相互替換, 6 * 注意策略針對的是一個點,是一個演算法,不是流程,針對每個點實作一個具體的實作類,這點和模版方法不同!!! 7 * 優點:1、演算法可以自由切換, 2、避免使用多重條件判斷, 3、擴展性非常良好 8 * 缺點:1、策略類會增多, 2、所有策略類都需要對外暴露, 9 * @DATE 2022-11-02 10 * @AUTHOR liuzhilong 11 */ 12 public class StrategyTest { 13 14 public static void main(String[] args) { 15 Context context; 16 //使用支付邏輯A 17 context = new Context(new PayStrategyA()); 18 context.algorithmInterface(); 19 //使用支付邏輯B 20 context = new Context(new PayStrategyB()); 21 context.algorithmInterface(); 22 } 23 } 24 25 abstract class PayStrategy { 26 27 // 支付邏輯方法 28 abstract void algorithmInterface(); 29 } 30 31 class PayStrategyA extends PayStrategy { 32 33 void algorithmInterface() { 34 System.out.println("微信支付"); 35 } 36 } 37 38 class PayStrategyB extends PayStrategy { 39 40 void algorithmInterface() { 41 System.out.println("支付寶支付"); 42 } 43 } 44 45 //定義下文維護演算法策略 46 class Context { 47 48 PayStrategy strategy; 49 50 public Context(PayStrategy strategy) { 51 this.strategy = strategy; 52 } 53 54 public void algorithmInterface() { 55 strategy.algorithmInterface(); 56 } 57 }View Code
4.5 配接器模式
使用場景:
注意事項:
代碼實作:
1 package com.leo.basic.code.design; 2 3 /** 4 * @DESCRIPTION 5 * 配接器:將一個類的介面轉換成客戶希望的另外一個介面 6 * 優點:讓原本因介面不匹配不能在一起作業的兩個類可以協同作業 7 * 缺點: 8 * @DATE 2022-11-03 9 * @AUTHOR liuzhilong 10 */ 11 public class AdapterTest { 12 13 public static void main(String[] args) { 14 // 1、類配接器 15 //電腦,配接器,網線 16 Computer computer = new Computer();//電腦 17 Adapter adapter = new Adapter();//轉接器 18 computer.net(adapter);//電腦直接連接轉接器就可以 19 20 // 2、物件配接器 21 Adapter2 adapter2 = new Adapter2(new Adaptee());//轉接器 22 computer.net(adapter2); 23 24 } 25 } 26 27 // 1、類配接器 28 //要適配的類:網線 -- Adaptee 29 class Adaptee { 30 31 //功能:上網 32 public void request() { 33 System.out.println("鏈接網線上網"); 34 } 35 } 36 37 //介面轉換器的抽象實作 -- target 38 interface NetToUsb { 39 //作用:處理請求,網線=>usb 40 public void handleRequest(); 41 } 42 43 //真正的配接器,余姚鏈接usb,連接網線 -- Adapter 44 class Adapter extends Adaptee implements NetToUsb{ 45 @Override 46 public void handleRequest() { 47 super.request();//可以上網了 48 } 49 } 50 51 //客戶端類:想上網,插不上網線 52 class Computer { 53 //電腦需要連接上轉接器才可以上網 54 public void net(NetToUsb adapter){ 55 //上網的具體實作:找一個轉接頭 56 adapter.handleRequest(); 57 } 58 } 59 60 // 2、物件配接器 61 class Adapter2 implements NetToUsb { 62 private Adaptee adaptee; 63 public Adapter2(Adaptee adaptee){ 64 this.adaptee = adaptee; 65 } 66 @Override 67 public void handleRequest() { 68 adaptee.request();;//可以上網了 69 } 70 }View Code
4.6 建造者模式
使用場景:
需要生成的物件具有復雜的內部結構;需要生成的物件內部屬性本身相互依賴
注意事項:
代碼實作:
1 package com.leo.basic.code.design; 2 3 /** 4 * @DESCRIPTION 5 * 建造者:是將一個復雜的對象的構建與它的表示分離,使得同樣的構建程序可以創建不同的方式進行創建 6 * 與工廠模式的區別是:建造者模式更加關注零件裝配的順序 7 * 優點: 8 * 缺點: 9 * @DATE 2022-11-02 10 * @AUTHOR liuzhilong 11 */ 12 public class BuilderTest { 13 14 public static void main(String[] args) { 15 PersonDirector pb = new PersonDirector(); 16 Arms arms = pb.constructPerson(new ArmsBuilder()); 17 System.out.println(arms.getHelmet()); 18 System.out.println(arms.getArmor()); 19 System.out.println(arms.getWeapon()); 20 } 21 } 22 23 // 1、Product:要創建的復雜物件 -- 裝備類 24 class Arms { 25 //頭盔 26 public String helmet; 27 //鎧甲 28 public String armor; 29 //武器 30 public String weapon; 31 32 public String getHelmet() { 33 return helmet; 34 } 35 36 public void setHelmet(String helmet) { 37 this.helmet = helmet; 38 } 39 40 public String getArmor() { 41 return armor; 42 } 43 44 public void setArmor(String armor) { 45 this.armor = armor; 46 } 47 48 public String getWeapon() { 49 return weapon; 50 } 51 52 public void setWeapon(String weapon) { 53 this.weapon = weapon; 54 } 55 } 56 57 // 2、Builder:給出一個抽象介面,以規范產品物件的各個組成成分的建造 -- 裝備制造規范 58 interface PersonBuilder { 59 void builderHelmetMurder(); 60 void builderArmorMurder(); 61 void builderWeaponMurder(); 62 void builderHelmetYanLong(); 63 void builderArmorYanLong(); 64 void builderWeaponYanLong(); 65 Arms BuilderArms(); //組裝 66 } 67 68 // 3、ConcreteBuilder:實作Builder介面,針對不同的商業邏輯,具體化復雜物件的各部分的創建,在建造程序完成后,提供產品的實體 -- 裝備制造 69 class ArmsBuilder implements PersonBuilder { 70 private Arms arms; 71 //創建一個Arms實體,用于呼叫set方法 72 public ArmsBuilder() { 73 arms = new Arms(); 74 } 75 public void builderHelmetMurder() { 76 arms.setHelmet("奪命頭盔"); 77 } 78 public void builderArmorMurder() { 79 arms.setArmor("奪命鎧甲"); 80 } 81 public void builderWeaponMurder() { 82 arms.setWeapon("奪命寶刀"); 83 } 84 public void builderHelmetYanLong() { 85 arms.setHelmet("炎龍頭盔"); 86 } 87 public void builderArmorYanLong() { 88 arms.setArmor("炎龍鎧甲"); 89 } 90 public void builderWeaponYanLong() { 91 arms.setWeapon("炎龍寶刀"); 92 } 93 public Arms BuilderArms() { 94 return arms; 95 } 96 97 } 98 99 // 4、Director:呼叫具體建造者來創建復雜物件的各個部分, 100 // 在指導者中不涉及具體產品的資訊,只負責保證物件各部分完整創建或按某種順序創建 101 class PersonDirector { 102 103 //組裝 104 public Arms constructPerson(PersonBuilder pb) { 105 pb.builderHelmetYanLong(); 106 pb.builderArmorMurder(); 107 pb.builderWeaponMurder(); 108 return pb.BuilderArms(); 109 } 110 }View Code
4.7 代理模式
使用場景:
Spring AOP、日志列印、例外處理、事務控制、權限控制
注意事項:
代碼實作:
1 package com.leo.basic.code.design; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 import java.lang.reflect.Proxy; 6 7 /** 8 * @DESCRIPTION 9 * 代理:通過代理控制物件的訪問,可以在這個物件呼叫方法之前、呼叫方法之后去處理/添加新的功能 10 * 靜態代理:由程式員創建或工具生成代理類的原始碼,再編譯代理類 11 * 動態代理:動態代理的物件,是利用JDK的API,動態的在記憶體中構建代理物件 12 * 優點:降低了系統的耦合度,擴展性好,可以起到保護目標物件的作用 13 * 缺點: 14 * @DATE 2022-11-02 15 * @AUTHOR liuzhilong 16 */ 17 public class ProxyTest { 18 19 public static void main(String[] args) { 20 // 1、靜態代理 21 UserDao userDao = new UserDaoImpl(); 22 UserDaoProxy userDaoProxy = new UserDaoProxy(userDao); 23 userDaoProxy.save(); 24 25 // 2、動態代理 26 // 被代理物件 27 UserDao userDaoImpl = new UserDaoImpl(); 28 InvocationHandlerImpl invocationHandlerImpl = new 29 InvocationHandlerImpl(userDaoImpl); 30 //類加載器 31 ClassLoader loader = userDaoImpl.getClass().getClassLoader(); 32 Class<?>[] interfaces = userDaoImpl.getClass().getInterfaces(); 33 // 主要裝載器、一組介面及呼叫處理動態代理實體 34 UserDao newProxyInstance = (UserDao) Proxy.newProxyInstance(loader, 35 interfaces, invocationHandlerImpl); 36 newProxyInstance.save(); 37 } 38 } 39 40 // 1、靜態代理 41 interface UserDao{ 42 void save(); 43 } 44 class UserDaoImpl implements UserDao { 45 public void save() { 46 System.out.println("保存資料方法"); 47 } 48 } 49 50 class UserDaoProxy extends UserDaoImpl { 51 private UserDao userDao; 52 public UserDaoProxy(UserDao userDao) { 53 this.userDao = userDao; 54 } 55 public void save() { 56 System.out.println("開啟事物..."); 57 userDao.save(); 58 System.out.println("關閉事物..."); 59 } 60 } 61 62 // 2、動態代理 63 class InvocationHandlerImpl implements InvocationHandler { 64 // 這其實業務實作類物件,用來呼叫具體的業務方法 65 private Object target; 66 // 通過建構式傳入目標物件 67 public InvocationHandlerImpl(Object target) { 68 this.target = target; 69 } 70 //動態代理實際運行的代理方法 71 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 72 System.out.println("呼叫開始處理"); 73 //下面invoke()方法是以反射的方式來創建物件,第一個引數是要創建的物件,第二個是構成方法的引數,由第二個引數來決定創建物件使用哪個構造方法 74 Object result = method.invoke(target, args); 75 System.out.println("呼叫結束處理"); 76 return result; 77 } 78 }View Code
4.8 觀察者模式
使用場景:
關聯行為場景,需要注意的是,關聯行為是可拆分的,而不是“組合”關系,事件多級觸發場景
注意事項:
代碼實作:
1 package com.leo.basic.code.design; 2 3 import java.util.Vector; 4 5 /** 6 * @DESCRIPTION 7 * 觀察者:是一種行為型設計模式,最重要的作用就是解耦!將觀察者與被觀察者解耦,使得他們之間的依賴性更小 8 * 優點:降低了目標與觀察者之間的耦合關系 9 * 缺點:目標與觀察者之間的依賴關系并沒有完全解除,而且有可能出現回圈參考;當觀察者物件很多時,通知的發布會花費很多時間,影響程式的效率 10 * @DATE 2022-11-02 11 * @AUTHOR liuzhilong 12 */ 13 public class ObserverTest { 14 15 public static void main(String[] args) { 16 // 目標物件 17 ConcreteSubject subject = new ConcreteSubject(); 18 // 創建多個觀察者 19 ObserverImpl obs1 = new ObserverImpl(); 20 ObserverImpl obs2 = new ObserverImpl(); 21 ObserverImpl obs3 = new ObserverImpl(); 22 // 注冊到觀察佇列中 23 subject.registerObserver(obs1); 24 subject.registerObserver(obs2); 25 subject.registerObserver(obs3); 26 // 改變State狀態 27 subject.setState(300); 28 System.out.println("obs1觀察者的MyState狀態值為:"+obs1.getMyState()); 29 System.out.println("obs2觀察者的MyState狀態值為:"+obs2.getMyState()); 30 System.out.println("obs3觀察者的MyState狀態值為:"+obs3.getMyState()); 31 // 改變State狀態 32 subject.setState(400); 33 System.out.println("obs1觀察者的MyState狀態值為:"+obs1.getMyState()); 34 System.out.println("obs2觀察者的MyState狀態值為:"+obs2.getMyState()); 35 System.out.println("obs3觀察者的MyState狀態值為:"+obs3.getMyState()); 36 } 37 } 38 39 // 1、定義抽象觀察者,每一個實作該介面的實作類都是具體觀察者 40 interface Observer { 41 // 觀察者方法 42 void update(int state); 43 } 44 45 // 2、定義具體觀察者 46 class ObserverImpl implements Observer { 47 // 具體觀察者的屬性 48 private int myState; 49 public void update(int state) { 50 myState=state; 51 System.out.println("收到訊息,myState值改為:"+state); 52 } 53 public int getMyState() { 54 return myState; 55 } 56 } 57 58 // 3、定義主題,主題定義觀察者陣列,并實作增、刪及通知操作 59 class Subjecct { 60 //觀察者的存盤集合,不推薦ArrayList,執行緒不安全, 61 private Vector<Observer> list = new Vector<>(); 62 // 注冊觀察者方法 63 public void registerObserver(Observer obs) { 64 list.add(obs); 65 } 66 // 洗掉觀察者方法 67 public void removeObserver(Observer obs) { 68 list.remove(obs); 69 } 70 // 通知所有的觀察者更新 71 public void notifyAllObserver(int state) { 72 for (Observer observer : list) { 73 observer.update(state); 74 } 75 } 76 } 77 78 // 4、定義具體的業務觸發狀態變更的地方,他繼承繼承Subject類,在這里實作具體業務,在具體專案中,該類會有很多 79 class ConcreteSubject extends Subjecct { 80 //被觀察物件的屬性 81 private int state; 82 public int getState(){ 83 return state; 84 } 85 public void setState(int state){ 86 this.state=state; 87 //主題物件(目標物件)值發生改變 88 this.notifyAllObserver(state); 89 } 90 }View Code
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/526917.html
標籤:設計模式
上一篇:【深入淺出 Yarn 架構與實作】1-1 設計理念與基本架構
下一篇:做一個不崩潰的核酸系統有多難?
