设计模式(待更新)
OOP七大原則
開(kāi)閉原則:對(duì)擴(kuò)展開(kāi)發(fā),對(duì)修改關(guān)閉。
里氏替換原則: 繼承必須確保超類(lèi)所擁有的性質(zhì)在子類(lèi)中仍然成立
依賴(lài)倒置原則: 要面向接口編程,不要面向?qū)崿F(xiàn)編程。
單一職責(zé)原則: 控制類(lèi)的粒度大小、將對(duì)象解耦、提高其內(nèi)聚性。
接口隔離原則: 要為各個(gè)類(lèi)建立它們需要的專(zhuān)用接口
迪米特法則: 只與你的直接朋友交談,不跟"陌生人”說(shuō)話。
合成復(fù)用原則: 盡量先使用組合或者聚合等關(guān)聯(lián)關(guān)系來(lái)實(shí)現(xiàn),其次才考慮使用繼承關(guān)系來(lái)實(shí)現(xiàn)。
單例模式
餓漢式
package com.sanjin.single;//餓漢式單例//餓漢,一上來(lái)就把實(shí)例加載了 public class Hungry {//可能會(huì)浪費(fèi)空間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];//構(gòu)造器私有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;//雙重檢測(cè)鎖模式的 懶漢式單例 DCL 懶漢式public static LazyMan getInstance(){//加鎖if (lazyMan==null){synchronized (LazyMan.class){if (lazyMan==null){lazyMan=new LazyMan();//不是原子性操作/*1. 分配內(nèi)存空間2. 執(zhí)行構(gòu)造方法初始化對(duì)象3. 把這個(gè)對(duì)象指向這個(gè)空間123132*/}}}if (lazyMan==null){lazyMan=new LazyMan();}return lazyMan;}//單線程下ok//多線程并發(fā)//反射!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);} }靜態(tài)內(nèi)部類(lèi)
package com.sanjin.single;//靜態(tài)內(nèi)部類(lèi) 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 是一個(gè)什么? 本身也是一個(gè)class類(lèi) 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);} }枚舉類(lèi)型的最終反編譯
// 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});} }簡(jiǎn)單(靜態(tài))工廠模式
滿(mǎn)足 :開(kāi)閉原則,依賴(lài)倒置原則,迪米特法則。
核心本質(zhì):
實(shí)例化對(duì)象不使用new,用工廠方法代替。
將選擇實(shí)現(xiàn)類(lèi),創(chuàng)建對(duì)象統(tǒng)一管理和控制。從而將調(diào)用者跟我們的實(shí)現(xiàn)類(lèi)解耦。
簡(jiǎn)單工廠模式:
創(chuàng)建接口:
兩個(gè)實(shí)現(xiàn)類(lèi)
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("五菱神車(chē)");} }創(chuàng)建工廠
package model.factory.simple;// 靜態(tài)工廠模式 // 增加新的產(chǎn)品,必須修改代碼,這是弊端,這違背了開(kāi)閉原則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;} }調(diào)用實(shí)現(xiàn)
package model.factory.simple;public class Consumer {public static void main(String[] args) {// 1. 了解這個(gè)接口和實(shí)現(xiàn)類(lèi)才能new出來(lái) // Car car1=new WuLing(); // Car car2=new Tesla(); // car1.name(); // car2.name();//2. 使用工廠創(chuàng)建Car car = CarFactory.getCar("五菱");Car tesla = CarFactory.getCar("Tesla");car.name();tesla.name();} }簡(jiǎn)單工廠模式簡(jiǎn)單易懂,但是如果要增加產(chǎn)品,就要修改源代碼,破壞開(kāi)閉規(guī)則。
工廠方法模式
在簡(jiǎn)單工廠模式的前提下,我們?cè)黾右韵骂?lèi)
工廠方法模式類(lèi)
實(shí)現(xiàn)工廠方法
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();} }測(cè)試
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();} }此時(shí)如果想再新增一個(gè)產(chǎn)品,只需實(shí)現(xiàn)工廠方法
package model.factory.method;public class Mobai implements Car{@Overridepublic void name() {System.out.println("摩拜單車(chē)");} } package model.factory.method;public class MobaiFactory implements CarFactory{@Overridepublic Car getCar() {return new Mobai();} }由此發(fā)現(xiàn),這樣可以不破壞最初代碼,但是繁瑣了很多。
抽象工廠模式
抽象工廠模式簡(jiǎn)單來(lái)說(shuō)就是工廠的工廠,具體的話,我們先看個(gè)例子
首先我們先寫(xiě)一個(gè)手機(jī)的接口
package model.factory.abstract1;//手機(jī)產(chǎn)品接口 public interface PhoneProduct {void start();void shutdown();void callup();void sendSMS(); }我們?cè)趤?lái)寫(xiě)一個(gè)路由器的接口
package model.factory.abstract1;//路由器產(chǎn)品接口 public interface RouterProduct {void start();void shutdown();void openWifi();void setting(); }現(xiàn)在有華為和小米兩個(gè)產(chǎn)品
package model.factory.abstract1;public class HuaWeiPhone implements PhoneProduct{@Overridepublic void start() {System.out.println("開(kāi)啟華為手機(jī)");}@Overridepublic void shutdown() {System.out.println("關(guān)閉華為手機(jī)");}@Overridepublic void callup() {System.out.println("華為打電話");}@Overridepublic void sendSMS() {System.out.println("華為發(fā)短信");} } package model.factory.abstract1;public class HuaWeiRouter implements RouterProduct{@Overridepublic void start() {System.out.println("啟動(dòng)華為路由器");}@Overridepublic void shutdown() {System.out.println("關(guān)閉華為路由器");}@Overridepublic void openWifi() {System.out.println("啟動(dòng)華為路由器wifi");}@Overridepublic void setting() {System.out.println("華為路由器設(shè)置");} } package model.factory.abstract1;public class XiaomiPhone implements PhoneProduct{@Overridepublic void start() {System.out.println("開(kāi)啟小米手機(jī)");}@Overridepublic void shutdown() {System.out.println("關(guān)閉小米手機(jī)");}@Overridepublic void callup() {System.out.println("小米打電話");}@Overridepublic void sendSMS() {System.out.println("小米發(fā)短信");} } package model.factory.abstract1;//小米路由器 public class XiaomiRouter implements RouterProduct{@Overridepublic void start() {System.out.println("啟動(dòng)小米路由器");}@Overridepublic void shutdown() {System.out.println("關(guān)閉小米路由器");}@Overridepublic void openWifi() {System.out.println("啟動(dòng)小米路由器wifi");}@Overridepublic void setting() {System.out.println("小米路由器設(shè)置");} }這樣我們有了兩個(gè)接口和對(duì)應(yīng)各自的實(shí)現(xiàn)類(lèi)
但是這樣還沒(méi)有體現(xiàn)本節(jié)內(nèi)容
所以我們加入抽象工廠
package model.factory.abstract1;//抽象產(chǎn)品工廠 public interface ProductFactory {//生產(chǎn)手機(jī)PhoneProduct phoneProduct();//生產(chǎn)路由器RouterProduct routerProduct();}有了抽象工廠,我們還不能指定到底是生產(chǎn)華為的手機(jī)還是小米的路由器,所以
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();} }增加測(cè)試類(lèi):
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();}}最后的關(guān)系如下:
調(diào)理一遍:
用戶(hù)選擇工廠,工廠實(shí)現(xiàn)了抽象工廠的內(nèi)容,然后用戶(hù)根據(jù)需求調(diào)用所需要的產(chǎn)品。
建造者模式
由上面的工廠模式,我們可以理解為創(chuàng)建了一個(gè)族的產(chǎn)品,然建造者就是把這些東西組裝起來(lái)變成一個(gè)類(lèi)。
比如,造房子的過(guò)程。
我們先要有地基,然后鋼筋工廠,然后鋪電線,粉刷。最后形成一個(gè)房子。
建造者,模式,就是抽象成一個(gè)指揮者,你去控制工人們?cè)趺醋觥K{(lán)圖在你這里,你只管決定然后做出最后的產(chǎn)品。
如下:
但是這樣,只是定死的內(nèi)容,我們大多數(shù)場(chǎng)景,是由用戶(hù)為指揮者。
我們舉例為麥當(dāng)勞,里面有套餐還可以用戶(hù)自定義 就是單點(diǎn)。
初始值就為套餐
package model.builder.demo2;//建造者 public abstract class Bulider {public abstract Bulider buliderA(String msg);//漢堡public abstract Bulider buliderB(String msg);//可樂(lè)public abstract Bulider buliderC(String msg);//薯?xiàng)lpublic abstract Bulider buliderD(String msg);//甜點(diǎn)abstract Product getProduct(); } package model.builder.demo2;//產(chǎn)品、套餐 public class Product {private String BuildA="漢堡";private String BuildB="可樂(lè)";private String BuildC="薯?xiàng)l";private String BuildD="甜點(diǎn)";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) {//服務(wù)員Worker worker=new Worker();Product product = worker.buliderA("全家桶").getProduct();System.out.println(product.toString());} }這樣我們就可以自定義建造什么了。
原型模式
原型模式就是當(dāng)我們new出來(lái)一個(gè)對(duì)象,如果想克隆這個(gè)對(duì)象,不用new 而是把這個(gè)對(duì)象當(dāng)作一個(gè)原型所引用。
我們舉個(gè)例子,視頻搬運(yùn)。
結(jié)果是
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的值,發(fā)現(xiàn)v1 v2都會(huì)改變。這就是淺克隆。
基本類(lèi)型值會(huì)相互克隆,引用類(lèi)型會(huì)指向相同的地址。
一般我們都希望互不干擾,就是深克隆。
所以我們重寫(xiě)clone方法。
這樣就可以變成一對(duì)一的了
適配器模式
適配器模式我們可以想象成電腦網(wǎng)線轉(zhuǎn)換器
適配器就是讓兩個(gè)東西相互兼容,相互影響。
舉例如下:
網(wǎng)線-適配器-usb
網(wǎng)線:
package model.adapter;//要被適配的類(lèi) : 網(wǎng)線 public class Adaptee {public void request(){System.out.println("連接網(wǎng)線上網(wǎng)。");} }適配器:
package model.adapter;//真正的適配器 需要連接usb 網(wǎng)線 public class Adapter extends Adaptee implements NetToUSB{@Overridepublic void handleRequest() {super.request();//可以上網(wǎng)了} }usb:
package model.adapter;//接口轉(zhuǎn)換器的抽象實(shí)現(xiàn) public interface NetToUSB {//處理請(qǐng)求 把網(wǎng)線插到usbpublic void handleRequest(); }測(cè)試:
package model.adapter;//客戶(hù)端類(lèi) : 想上網(wǎng),插不上網(wǎng)線 public class Computer {//我們電腦需要連接到轉(zhuǎn)接器才能上網(wǎng)public void net(NetToUSB adapter){//上網(wǎng)的具體實(shí)現(xiàn) 找一個(gè)轉(zhuǎn)接頭adapter.handleRequest();}public static void main(String[] args) {//電腦 適配器 網(wǎng)線Computer computer = new Computer();//電腦Adapter adapter = new Adapter();//轉(zhuǎn)接器Adaptee adaptee = new Adaptee();//網(wǎng)線computer.net(adapter);} }這樣就簡(jiǎn)單的實(shí)現(xiàn)了適配器模式的基本用途。
但是我們不想只要這一根網(wǎng)線進(jìn)這一個(gè)適配器(因?yàn)橛昧死^承)
所以對(duì)適配器進(jìn)行改進(jìn)
這樣每次調(diào)用的時(shí)候可以指定網(wǎng)線了。
package model.adapter;//客戶(hù)端類(lèi) : 想上網(wǎng),插不上網(wǎng)線 public class Computer {//我們電腦需要連接到轉(zhuǎn)接器才能上網(wǎng)public void net(NetToUSB adapter){//上網(wǎng)的具體實(shí)現(xiàn) 找一個(gè)轉(zhuǎn)接頭adapter.handleRequest();}public static void main(String[] args) {//電腦 適配器 網(wǎng)線Computer computer = new Computer();//電腦Adaptee adaptee = new Adaptee();//網(wǎng)線Adapter2 adapter2 = new Adapter2(adaptee);//轉(zhuǎn)接線2computer.net(adapter2);} }要體會(huì)這里的思想。
橋接模式
我們看下面的圖
如果想獲得蘋(píng)果筆記本電腦 或者 聯(lián)想臺(tái)式電腦 該如何呢。
我們應(yīng)該在這兩個(gè)直接搭一個(gè)橋梁(可能有點(diǎn)像適配器模式,不過(guò)是不一樣的,我們需要仔細(xì)考慮考慮)
我們根據(jù)上述建立代碼
package model.bridge;public interface Brand {void info(); } package model.bridge;//蘋(píng)果品牌 public class Apple implements Brand{@Overridepublic void info() {System.out.println("蘋(píng)果");} } package model.bridge;//聯(lián)想品牌 public class Lenovo implements Brand{@Overridepublic void info() {System.out.println("聯(lián)想");} }一個(gè)分支結(jié)束。
package model.bridge;//抽象的電腦類(lèi)型類(lèi) 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("臺(tái)式機(jī)");} } class Laptop extends Computer{public Laptop(Brand brand) {super(brand);}@Overridepublic void info() {super.info();System.out.println("筆記本");} }這里我們使用了組合 ,通過(guò)構(gòu)造器讓兩個(gè)分支有了聯(lián)系。
這里如果體會(huì)不到好處的話 ,我再畫(huà)一個(gè)圖
這樣就會(huì)變成多繼承的關(guān)系。
所以就體現(xiàn)出了橋接的好處。
代理模式
為什么要學(xué)習(xí)代理模式?因?yàn)檫@就是SpringAOP的底層!【SpringAOP 和 Spring MVC】面試必問(wèn)
代理模式的分類(lèi):
- 靜態(tài)代理
- 動(dòng)態(tài)代理
靜態(tài)代理
角色分析:
- 抽象角色:一般會(huì)使用接口或者抽象類(lèi)來(lái)解決
- 真實(shí)角色:被代理的角色
- 代理角色:代理真實(shí)角色,代理真實(shí)角色后,我們一般會(huì)做一些附屬操作
- 客戶(hù):訪問(wèn)代理對(duì)象的人
代碼步驟:
代理模式的好處:
- 可以使真實(shí)角色的操作更加純粹,不用去關(guān)注一些公共業(yè)務(wù)
- 公共業(yè)務(wù)就交給代理角色,實(shí)現(xiàn)了業(yè)務(wù)的分工
- 公共業(yè)務(wù)發(fā)生拓展的時(shí)候,方便集中管理!
缺點(diǎn):
- 一個(gè)真實(shí)角色,就會(huì)又一個(gè)代理角色,代碼量會(huì)翻倍,開(kāi)發(fā)效率會(huì)變低
加深理解
動(dòng)態(tài)代理
- 動(dòng)態(tài)代理和靜態(tài)代理角色一樣
- 動(dòng)態(tài)代理的類(lèi)是動(dòng)態(tài)生成的,不是我們直接寫(xiě)好的
- 動(dòng)態(tài)代理分為兩大類(lèi):基于接口的動(dòng)態(tài)代理;基于類(lèi)的動(dòng)態(tài)代理
- 基于接口— JDK 動(dòng)態(tài)代理【我們?cè)龠@里使用】
- 基于類(lèi):cglib
- java字節(jié)碼實(shí)現(xiàn):javassist
需要了解兩個(gè)類(lèi):Proxy: ?代理, InvocationHandler:調(diào)用處理程序
InvocationHandler
動(dòng)態(tài)代理類(lèi)
package com.sanjin.domo04;import com.sanjin.demo03.Rent;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;//我們會(huì)用這個(gè)類(lèi) 自動(dòng)生成代理類(lèi) 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);}//處理代理實(shí)例,并返回結(jié)果public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//動(dòng)態(tài)代理的本質(zhì)就是使用反射log(method.getName());Object result = method.invoke(target, args);return result;}public void log(String mes){System.out.println("執(zhí)行了"+mes+"日志方法");}}測(cè)試類(lèi)
package com.sanjin.domo04;import com.sanjin.demo02.UserService; import com.sanjin.demo02.UserServiceImpl;public class Client {public static void main(String[] args) {//真實(shí)角色UserServiceImpl userService = new UserServiceImpl();//代理角色Proxy2 proxy2 = new Proxy2();proxy2.setTarget(userService);//設(shè)置要代理的對(duì)象//動(dòng)態(tài)生成代理類(lèi)UserService proxy = (UserService) proxy2.getProxy();proxy.add();proxy.delete();} }動(dòng)態(tài)代理的好處:
- 可以使真實(shí)角色的操作更加純粹,不用去關(guān)注一些公共業(yè)務(wù)
- 公共業(yè)務(wù)就交給代理角色,實(shí)現(xiàn)了業(yè)務(wù)的分工
- 公共業(yè)務(wù)發(fā)生拓展的時(shí)候,方便集中管理!
- 一個(gè)動(dòng)態(tài)代理類(lèi)代理的是一個(gè)接口,一般就是對(duì)應(yīng)的一類(lèi)業(yè)務(wù)
- 一個(gè)動(dòng)態(tài)代理類(lèi)可以代理多個(gè)類(lèi),只要是實(shí)現(xiàn)了同一個(gè)接口即可
總結(jié)
- 上一篇: 华为mate50os鸿蒙,华为Mate5
- 下一篇: 《设计模式入门》 19.命令模式