设计模式(待更新)
OOP七大原則
開閉原則:對擴展開發,對修改關閉。
里氏替換原則: 繼承必須確保超類所擁有的性質在子類中仍然成立
依賴倒置原則: 要面向接口編程,不要面向實現編程。
單一職責原則: 控制類的粒度大小、將對象解耦、提高其內聚性。
接口隔離原則: 要為各個類建立它們需要的專用接口
迪米特法則: 只與你的直接朋友交談,不跟"陌生人”說話。
合成復用原則: 盡量先使用組合或者聚合等關聯關系來實現,其次才考慮使用繼承關系來實現。
單例模式
餓漢式
package com.sanjin.single;//餓漢式單例//餓漢,一上來就把實例加載了 public class Hungry {//可能會浪費空間private byte[] data1=new byte[1024*1024];private byte[] data2=new byte[1024*1024];private byte[] data3=new byte[1024*1024];private byte[] data4=new byte[1024*1024];//構造器私有private Hungry(){}//保證唯一private final static Hungry HUBGRY= new Hungry();public static Hungry getInstance(){return HUBGRY;} }DCL懶漢式
package com.sanjin.single;import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException;//懶漢式單例 public class LazyMan {private static boolean sanjin =false;private LazyMan(){synchronized (LazyMan.class){if (sanjin==false){sanjin=true;}else {throw new RuntimeException("不要試圖使用反射破壞異常");}}System.out.println(Thread.currentThread().getName());}private volatile static LazyMan lazyMan;//雙重檢測鎖模式的 懶漢式單例 DCL 懶漢式public static LazyMan getInstance(){//加鎖if (lazyMan==null){synchronized (LazyMan.class){if (lazyMan==null){lazyMan=new LazyMan();//不是原子性操作/*1. 分配內存空間2. 執行構造方法初始化對象3. 把這個對象指向這個空間123132*/}}}if (lazyMan==null){lazyMan=new LazyMan();}return lazyMan;}//單線程下ok//多線程并發//反射!public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {//LazyMan instance = LazyMan.getInstance();Field sanjin = LazyMan.class.getDeclaredField("sanjin");sanjin.setAccessible(true);Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor();declaredConstructor.setAccessible(true);LazyMan lazyMan = declaredConstructor.newInstance();sanjin.set(lazyMan,false);LazyMan lazyMan1 = declaredConstructor.newInstance();System.out.println(lazyMan==lazyMan1);} }靜態內部類
package com.sanjin.single;//靜態內部類 public class Holder {private Holder(){}public static Holder getInstance(){return innerClass.HOLDER;}public static class innerClass{private static final Holder HOLDER=new Holder();} }單例不安全,反射
枚舉
package com.sanjin.single;import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException;//enum 是一個什么? 本身也是一個class類 public enum EnumSingle {INSTANCE;public EnumSingle getInstance(){return INSTANCE;} } class Test{public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {EnumSingle instance = EnumSingle.INSTANCE;Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);declaredConstructor.setAccessible(true);EnumSingle enumSingle = declaredConstructor.newInstance();System.out.println(instance==enumSingle);} }枚舉類型的最終反編譯
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov. // Jad home page: http://www.kpdus.com/jad.html // Decompiler options: packimports(3) // Source File Name: EnumSingle.javapackage com.sanjin.single;public final class EnumSingle extends Enum {public static EnumSingle[] values(){return (EnumSingle[])$VALUES.clone();}public static EnumSingle valueOf(String name){return (EnumSingle)Enum.valueOf(com/sanjin/single/EnumSingle, name);}private EnumSingle(String s, int i){super(s, i);}public EnumSingle getInstance(){return INSTANCE;}public static final EnumSingle INSTANCE;private static final EnumSingle $VALUES[];static {INSTANCE = new EnumSingle("INSTANCE", 0);$VALUES = (new EnumSingle[] {INSTANCE});} }簡單(靜態)工廠模式
滿足 :開閉原則,依賴倒置原則,迪米特法則。
核心本質:
實例化對象不使用new,用工廠方法代替。
將選擇實現類,創建對象統一管理和控制。從而將調用者跟我們的實現類解耦。
簡單工廠模式:
創建接口:
兩個實現類
package model.factory.simple;public class Tesla implements Car{@Overridepublic void name() {System.out.println("Tesla");} } package model.factory.simple;public class WuLing implements Car{@Overridepublic void name() {System.out.println("五菱神車");} }創建工廠
package model.factory.simple;// 靜態工廠模式 // 增加新的產品,必須修改代碼,這是弊端,這違背了開閉原則public class CarFactory {public static Car getCar(String car){if (car==null||car.length()<=0){return null;}if (car.equals("五菱")){return new WuLing();}else if (car.equals("Tesla")){return new Tesla();}return null;} }調用實現
package model.factory.simple;public class Consumer {public static void main(String[] args) {// 1. 了解這個接口和實現類才能new出來 // Car car1=new WuLing(); // Car car2=new Tesla(); // car1.name(); // car2.name();//2. 使用工廠創建Car car = CarFactory.getCar("五菱");Car tesla = CarFactory.getCar("Tesla");car.name();tesla.name();} }簡單工廠模式簡單易懂,但是如果要增加產品,就要修改源代碼,破壞開閉規則。
工廠方法模式
在簡單工廠模式的前提下,我們增加以下類
工廠方法模式類
實現工廠方法
package model.factory.method;public class TeslaFactory implements CarFactory{@Overridepublic Car getCar() {return new Tesla();} } package model.factory.method;public class WuLingFactory implements CarFactory{@Overridepublic Car getCar() {return new WuLing();} }測試
package model.factory.method;public class Consumer {public static void main(String[] args) {Car car = new WuLingFactory().getCar();Car car1 = new TeslaFactory().getCar();car.name();car1.name();} }此時如果想再新增一個產品,只需實現工廠方法
package model.factory.method;public class Mobai implements Car{@Overridepublic void name() {System.out.println("摩拜單車");} } package model.factory.method;public class MobaiFactory implements CarFactory{@Overridepublic Car getCar() {return new Mobai();} }由此發現,這樣可以不破壞最初代碼,但是繁瑣了很多。
抽象工廠模式
抽象工廠模式簡單來說就是工廠的工廠,具體的話,我們先看個例子
首先我們先寫一個手機的接口
package model.factory.abstract1;//手機產品接口 public interface PhoneProduct {void start();void shutdown();void callup();void sendSMS(); }我們在來寫一個路由器的接口
package model.factory.abstract1;//路由器產品接口 public interface RouterProduct {void start();void shutdown();void openWifi();void setting(); }現在有華為和小米兩個產品
package model.factory.abstract1;public class HuaWeiPhone implements PhoneProduct{@Overridepublic void start() {System.out.println("開啟華為手機");}@Overridepublic void shutdown() {System.out.println("關閉華為手機");}@Overridepublic void callup() {System.out.println("華為打電話");}@Overridepublic void sendSMS() {System.out.println("華為發短信");} } package model.factory.abstract1;public class HuaWeiRouter implements RouterProduct{@Overridepublic void start() {System.out.println("啟動華為路由器");}@Overridepublic void shutdown() {System.out.println("關閉華為路由器");}@Overridepublic void openWifi() {System.out.println("啟動華為路由器wifi");}@Overridepublic void setting() {System.out.println("華為路由器設置");} } package model.factory.abstract1;public class XiaomiPhone implements PhoneProduct{@Overridepublic void start() {System.out.println("開啟小米手機");}@Overridepublic void shutdown() {System.out.println("關閉小米手機");}@Overridepublic void callup() {System.out.println("小米打電話");}@Overridepublic void sendSMS() {System.out.println("小米發短信");} } package model.factory.abstract1;//小米路由器 public class XiaomiRouter implements RouterProduct{@Overridepublic void start() {System.out.println("啟動小米路由器");}@Overridepublic void shutdown() {System.out.println("關閉小米路由器");}@Overridepublic void openWifi() {System.out.println("啟動小米路由器wifi");}@Overridepublic void setting() {System.out.println("小米路由器設置");} }這樣我們有了兩個接口和對應各自的實現類
但是這樣還沒有體現本節內容
所以我們加入抽象工廠
package model.factory.abstract1;//抽象產品工廠 public interface ProductFactory {//生產手機PhoneProduct phoneProduct();//生產路由器RouterProduct routerProduct();}有了抽象工廠,我們還不能指定到底是生產華為的手機還是小米的路由器,所以
package model.factory.abstract1;public class HuaWeiFactory implements ProductFactory{@Overridepublic PhoneProduct phoneProduct() {return new HuaWeiPhone();}@Overridepublic RouterProduct routerProduct() {return new HuaWeiRouter();} } package model.factory.abstract1;public class XIaomiFactory implements ProductFactory{@Overridepublic PhoneProduct phoneProduct() {return new XiaomiPhone();}@Overridepublic RouterProduct routerProduct() {return new XiaomiRouter();} }增加測試類:
package model.factory.abstract1;public class Client{public static void main(String[] args) {System.out.println("===========小米");//小米工廠XIaomiFactory xIaomiFactory = new XIaomiFactory();PhoneProduct phoneProduct = xIaomiFactory.phoneProduct();phoneProduct.callup();phoneProduct.sendSMS();RouterProduct routerProduct = xIaomiFactory.routerProduct();routerProduct.openWifi();System.out.println("============華為");HuaWeiFactory huaWeiFactory = new HuaWeiFactory();PhoneProduct phoneProduct1 = huaWeiFactory.phoneProduct();phoneProduct1.callup();RouterProduct routerProduct1 = huaWeiFactory.routerProduct();routerProduct1.openWifi();}}最后的關系如下:
調理一遍:
用戶選擇工廠,工廠實現了抽象工廠的內容,然后用戶根據需求調用所需要的產品。
建造者模式
由上面的工廠模式,我們可以理解為創建了一個族的產品,然建造者就是把這些東西組裝起來變成一個類。
比如,造房子的過程。
我們先要有地基,然后鋼筋工廠,然后鋪電線,粉刷。最后形成一個房子。
建造者,模式,就是抽象成一個指揮者,你去控制工人們怎么做。藍圖在你這里,你只管決定然后做出最后的產品。
如下:
但是這樣,只是定死的內容,我們大多數場景,是由用戶為指揮者。
我們舉例為麥當勞,里面有套餐還可以用戶自定義 就是單點。
初始值就為套餐
package model.builder.demo2;//建造者 public abstract class Bulider {public abstract Bulider buliderA(String msg);//漢堡public abstract Bulider buliderB(String msg);//可樂public abstract Bulider buliderC(String msg);//薯條public abstract Bulider buliderD(String msg);//甜點abstract Product getProduct(); } package model.builder.demo2;//產品、套餐 public class Product {private String BuildA="漢堡";private String BuildB="可樂";private String BuildC="薯條";private String BuildD="甜點";public void setBuildA(String buildA) {BuildA = buildA;}public void setBuildB(String buildB) {BuildB = buildB;}public void setBuildC(String buildC) {BuildC = buildC;}public void setBuildD(String buildD) {BuildD = buildD;}@Overridepublic String toString() {return "Product{" +"BuildA='" + BuildA + '\'' +", BuildB='" + BuildB + '\'' +", BuildC='" + BuildC + '\'' +", BuildD='" + BuildD + '\'' +'}';} } package model.builder.demo2;//具體建造者 public class Worker extends Bulider{private Product product;public Worker() {this.product = new Product();}@Overridepublic Bulider buliderA(String msg) {product.setBuildA(msg);return this;}@Overridepublic Bulider buliderB(String msg) {product.setBuildB(msg);return this;}@Overridepublic Bulider buliderC(String msg) {product.setBuildC(msg);return this;}@Overridepublic Bulider buliderD(String msg) {product.setBuildD(msg);return this;}@OverrideProduct getProduct() {return product;} } package model.builder.demo2;public class Test {public static void main(String[] args) {//服務員Worker worker=new Worker();Product product = worker.buliderA("全家桶").getProduct();System.out.println(product.toString());} }這樣我們就可以自定義建造什么了。
原型模式
原型模式就是當我們new出來一個對象,如果想克隆這個對象,不用new 而是把這個對象當作一個原型所引用。
我們舉個例子,視頻搬運。
結果是
Video{name='三金', createTime=Wed Apr 21 19:04:56 GMT+08:00 2021} hash=1735600054 Video{name='三金', createTime=Wed Apr 21 19:04:56 GMT+08:00 2021} hash=21685669 Video{name='克隆三金', createTime=Wed Apr 21 19:04:56 GMT+08:00 2021}但是如果我們改變的了date的值,發現v1 v2都會改變。這就是淺克隆。
基本類型值會相互克隆,引用類型會指向相同的地址。
一般我們都希望互不干擾,就是深克隆。
所以我們重寫clone方法。
這樣就可以變成一對一的了
適配器模式
適配器模式我們可以想象成電腦網線轉換器
適配器就是讓兩個東西相互兼容,相互影響。
舉例如下:
網線-適配器-usb
網線:
package model.adapter;//要被適配的類 : 網線 public class Adaptee {public void request(){System.out.println("連接網線上網。");} }適配器:
package model.adapter;//真正的適配器 需要連接usb 網線 public class Adapter extends Adaptee implements NetToUSB{@Overridepublic void handleRequest() {super.request();//可以上網了} }usb:
package model.adapter;//接口轉換器的抽象實現 public interface NetToUSB {//處理請求 把網線插到usbpublic void handleRequest(); }測試:
package model.adapter;//客戶端類 : 想上網,插不上網線 public class Computer {//我們電腦需要連接到轉接器才能上網public void net(NetToUSB adapter){//上網的具體實現 找一個轉接頭adapter.handleRequest();}public static void main(String[] args) {//電腦 適配器 網線Computer computer = new Computer();//電腦Adapter adapter = new Adapter();//轉接器Adaptee adaptee = new Adaptee();//網線computer.net(adapter);} }這樣就簡單的實現了適配器模式的基本用途。
但是我們不想只要這一根網線進這一個適配器(因為用了繼承)
所以對適配器進行改進
這樣每次調用的時候可以指定網線了。
package model.adapter;//客戶端類 : 想上網,插不上網線 public class Computer {//我們電腦需要連接到轉接器才能上網public void net(NetToUSB adapter){//上網的具體實現 找一個轉接頭adapter.handleRequest();}public static void main(String[] args) {//電腦 適配器 網線Computer computer = new Computer();//電腦Adaptee adaptee = new Adaptee();//網線Adapter2 adapter2 = new Adapter2(adaptee);//轉接線2computer.net(adapter2);} }要體會這里的思想。
橋接模式
我們看下面的圖
如果想獲得蘋果筆記本電腦 或者 聯想臺式電腦 該如何呢。
我們應該在這兩個直接搭一個橋梁(可能有點像適配器模式,不過是不一樣的,我們需要仔細考慮考慮)
我們根據上述建立代碼
package model.bridge;public interface Brand {void info(); } package model.bridge;//蘋果品牌 public class Apple implements Brand{@Overridepublic void info() {System.out.println("蘋果");} } package model.bridge;//聯想品牌 public class Lenovo implements Brand{@Overridepublic void info() {System.out.println("聯想");} }一個分支結束。
package model.bridge;//抽象的電腦類型類 public abstract class Computer {//組合,品牌~protected Brand brand;public Computer(Brand brand) {this.brand = brand;}public void info(){//自帶品牌brand.info();} } class Desktop extends Computer{public Desktop(Brand brand) {super(brand);}@Overridepublic void info() {super.info();System.out.println("臺式機");} } class Laptop extends Computer{public Laptop(Brand brand) {super(brand);}@Overridepublic void info() {super.info();System.out.println("筆記本");} }這里我們使用了組合 ,通過構造器讓兩個分支有了聯系。
這里如果體會不到好處的話 ,我再畫一個圖
這樣就會變成多繼承的關系。
所以就體現出了橋接的好處。
代理模式
為什么要學習代理模式?因為這就是SpringAOP的底層!【SpringAOP 和 Spring MVC】面試必問
代理模式的分類:
- 靜態代理
- 動態代理
靜態代理
角色分析:
- 抽象角色:一般會使用接口或者抽象類來解決
- 真實角色:被代理的角色
- 代理角色:代理真實角色,代理真實角色后,我們一般會做一些附屬操作
- 客戶:訪問代理對象的人
代碼步驟:
代理模式的好處:
- 可以使真實角色的操作更加純粹,不用去關注一些公共業務
- 公共業務就交給代理角色,實現了業務的分工
- 公共業務發生拓展的時候,方便集中管理!
缺點:
- 一個真實角色,就會又一個代理角色,代碼量會翻倍,開發效率會變低
加深理解
動態代理
- 動態代理和靜態代理角色一樣
- 動態代理的類是動態生成的,不是我們直接寫好的
- 動態代理分為兩大類:基于接口的動態代理;基于類的動態代理
- 基于接口— JDK 動態代理【我們再這里使用】
- 基于類:cglib
- java字節碼實現:javassist
需要了解兩個類:Proxy: ?代理, InvocationHandler:調用處理程序
InvocationHandler
動態代理類
package com.sanjin.domo04;import com.sanjin.demo03.Rent;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;//我們會用這個類 自動生成代理類 public class Proxy2 implements InvocationHandler {//被代理的接口private Object target;public void setTarget(Object target) {this.target = target;}public Object getProxy(){return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);}//處理代理實例,并返回結果public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//動態代理的本質就是使用反射log(method.getName());Object result = method.invoke(target, args);return result;}public void log(String mes){System.out.println("執行了"+mes+"日志方法");}}測試類
package com.sanjin.domo04;import com.sanjin.demo02.UserService; import com.sanjin.demo02.UserServiceImpl;public class Client {public static void main(String[] args) {//真實角色UserServiceImpl userService = new UserServiceImpl();//代理角色Proxy2 proxy2 = new Proxy2();proxy2.setTarget(userService);//設置要代理的對象//動態生成代理類UserService proxy = (UserService) proxy2.getProxy();proxy.add();proxy.delete();} }動態代理的好處:
- 可以使真實角色的操作更加純粹,不用去關注一些公共業務
- 公共業務就交給代理角色,實現了業務的分工
- 公共業務發生拓展的時候,方便集中管理!
- 一個動態代理類代理的是一個接口,一般就是對應的一類業務
- 一個動態代理類可以代理多個類,只要是實現了同一個接口即可
總結
- 上一篇: 华为mate50os鸿蒙,华为Mate5
- 下一篇: 《设计模式入门》 19.命令模式