儲備知識
設計模式的本質是面向對象設計原則的實際運用,是對類的封裝性,繼承性和多态性以及類的關聯關系群組合關系的充分了解
正确使用設計模式的優點:
設計代碼的可重用性高,可讀性強,可靠性高,靈活性好,可維護性強。
設計模式的基本要素
- 模式名稱
- 問題
- 解決方案
- 效果
23種設計模式
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIyVGduV2YfNWawNCM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0zZU5UeBp2YxR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLwkDNyATNwcTM2ITMwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
- 單例(Singleton)模式:某個類隻能生成一個執行個體,該類提供了一個全局通路點供外部擷取該執行個體,其拓展是有限多例模式。
- 原型(Prototype)模式:将一個對象作為原型,通過對其進行複制而克隆出多個和原型類似的新執行個體。
- 工廠方法(Factory Method)模式:定義一個用于建立産品的接口,由子類決定生産什麼産品。
- 抽象工廠(AbstractFactory)模式:提供一個建立産品族的接口,其每個子類可以生産一系列相關的産品。
- 建造者(Builder)模式:将一個複雜對象分解成多個相對簡單的部分,然後根據不同需要分别建立它們,最後建構成該複雜對象。
- 代理(Proxy)模式:為某對象提供一種代理以控制對該對象的通路。即用戶端通過代理間接地通路該對象,進而限制、增強或修改該對象的一些特性。
- 擴充卡(Adapter)模式:将一個類的接口轉換成客戶希望的另外一個接口,使得原本由于接口不相容而不能一起工作的那些類能一起工作。
- 橋接(Bridge)模式:将抽象與實作分離,使它們可以獨立變化。它是用組合關系代替繼承關系來實作,進而降低了抽象和實作這兩個可變次元的耦合度。
- 裝飾(Decorator)模式:動态的給對象增加一些職責,即增加其額外的功能。
- 外觀(Facade)模式:為多個複雜的子系統提供一個一緻的接口,使這些子系統更加容易被通路。
- 享元(Flyweight)模式:運用共享技術來有效地支援大量細粒度對象的複用。
- 組合(Composite)模式:将對象組合成樹狀層次結構,使使用者對單個對象群組合對象具有一緻的通路性。
- 模闆方法(TemplateMethod)模式:定義一個操作中的算法骨架,而将算法的一些步驟延遲到子類中,使得子類可以不改變該算法結構的情況下重定義該算法的某些特定步驟。
- 政策(Strategy)模式:定義了一系列算法,并将每個算法封裝起來,使它們可以互相替換,且算法的改變不會影響使用算法的客戶。
- 指令(Command)模式:将一個請求封裝為一個對象,使送出請求的責任和執行請求的責任分割開。
- 職責鍊(Chain of Responsibility)模式:把請求從鍊中的一個對象傳到下一個對象,直到請求被響應為止。通過這種方式去除對象之間的耦合。
- 狀态(State)模式:允許一個對象在其内部狀态發生改變時改變其行為能力。
- 觀察者(Observer)模式:多個對象間存在一對多關系,當一個對象發生改變時,把這種改變通知給其他多個對象,進而影響其他對象的行為。
- 中介者(Mediator)模式:定義一個中介對象來簡化原有對象之間的互動關系,降低系統中對象間的耦合度,使原有對象之間不必互相了解。
- 疊代器(Iterator)模式:提供一種方法來順序通路聚合對象中的一系列資料,而不暴露聚合對象的内部表示。
- 通路者(Visitor)模式:在不改變集合元素的前提下,為一個集合中的每個元素提供多種通路方式,即每個元素有多個通路者對象通路。
- 備忘錄(Memento)模式:在不破壞封裝性的前提下,擷取并儲存一個對象的内部狀态,以便以後恢複它。
- 解釋器(Interpreter)模式:提供如何定義語言的文法,以及對語言句子的解釋方法,即解釋器。
OOP七大原則
1.開閉原則
對擴充開放,對修改關閉。
當需求發生變化時,在不修改軟體實體源代碼的情況下,可以進行功能擴充,使其滿足新需求。
2.裡氏替換原則
繼承必須確定超類所擁有的性質在子類中仍然成立。
通俗來講就是,子類可以擴充父類的功能,但不能改變父類原有的功能,即不可以重寫父類的方法。
如果通過重寫父類方法擴充新功能,寫起來可能會很簡單,但這會使整個繼承體系的可複用性變得很差,特别是運用多态比較頻繁時,程式很有可能會運作錯誤。
典型案例,“正方形不是長方形”,“鴕鳥不是鳥”。
3.依賴倒置原則
要面向接口程式設計,不要面向實作程式設計。
原始定義為:高層子產品不應該依賴低層子產品,兩者都應該依賴其抽象;抽象不應該依賴細節,細節應該依賴抽象。
依賴倒置原則是實作開閉原則重要途徑之一。
4.單一職責原則
控制類的粒度大小,将對象解耦,提高其内聚性。
職責指類變化的原因,一個類應當有且隻有一個引起它變化的原因,若有多個則該類需要拆分。如果一個對象承擔了太多職責,至少會存在以下兩個缺點:
一、當一個職責變化時,可能會削弱或抑制該類實作其他職責的能力。
二、當用戶端需要該對象的某一個職責時,不得不将其他不需要的職責全都包含進來,進而造成備援代碼或代碼的浪費。
單一職責同樣也适用于方法。一個方法應該盡可能做好一件事情,如果一個方法處理的事情太多,其顆粒度會變得很粗,不利于重用。
5.接口隔離原則
要為各個類建立它們需要的專用接口,而不要試圖去建立一個很龐大的接口供所有依賴它的類去調用。
接口隔離原則和單一職責都是為了提高類的内聚性、降低它們之間的耦合性,展現了封裝的思想,但兩者是不同的:
單一職責原則注重的是職責,而接口隔離原則注重的是對接口依賴的隔離。
單一職責原則主要是限制類,它針對的是程式中的實作和細節;接口隔離原則主要限制接口,主要針對抽象和程式整體架構的建構。
6.迪米特法則
隻與你的直接朋友交談,不跟“陌生人”說話。
簡單來說就是,如果兩個軟體實體無須直接通信,那麼就不應當發生直接的互相調用,可以通過第三方轉發該調用。其目的是降低類之間的耦合度,提高子產品的相對獨立性。
但過度使用迪米特法則會使系統産生大量的中介類,進而增加系統的複雜性,使子產品之間的通信效率降低。是以,在釆用迪米特法則時需要反複權衡,確定高内聚和低耦合的同時,保證系統的結構清晰。
7.合成複用原則
軟體複用時,要盡量先使用組合或者聚合等關聯關系來實作,其次才考慮使用繼承關系來實作。組合是“has a”關系,繼承是“is a”關系。
組合就是A類的對象是B類的成員變量,相當于 A類是B類對象的一個屬性。
例如,Dog類和Animal類應該使用繼承關系,因為狗是動物;Person類和Head類應該使用組合關系,因為頭是組成人體的一部分,即人有頭。
如果要使用繼承關系,則必須嚴格遵循裡氏替換原則。合成複用原則同裡氏替換原則相輔相成的,兩者都是開閉原則的具體實作規範。
建立型模式
1.單例模式(不允許new對象)
構造器私有,别人無法new對象,以保證構造器内隻有一個對象
1.1餓漢式
直接加載所有對象——可能造成資源浪費(沒用到卻已經申請了記憶體)
//餓漢式單例
public class Hungry {
//浪費記憶體
private byte[] data1=new byte[1024*1024];
private byte[] data2=new byte[1024*1024];
private Hungry(){
}
private final static Hungry HUNGRY=new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
1.2枚舉和懶漢式——DCL懶漢式
雙重檢測鎖模式 ——DCL懶漢式
代碼:
public class LazyMan {
private LazyMan(){
System.out.println(Thread.currentThread().getName()+"ok");
}
private static LazyMan Lazyman;
//雙重檢測鎖模式
public static LazyMan getLazyman(){
//加鎖來解決并發問題
//雙重if檢測
if(Lazyman==null){
synchronized (LazyMan.class){
if(Lazyman==null){
Lazyman=new LazyMan();//不是一個原子操作
//1.配置設定記憶體空間
//2.執行構造方法,初始化對象
//3.把這個對象指向這個空間
}
}
}
return Lazyman;
}
//單線程下單例OK
//多線程并發測試代碼
public static void main(String[] args){
for (int i=0;i<10;i++){
new Thread(()->{
LazyMan.getLazyman();
}).start();
}
}
**産生問題及解決:
問題1: Lazyman=new LazyMan();
不是一個原子操作:
Lazyman=new LazyMan();
- 1.配置設定記憶體空間
- 2.執行構造方法,初始化對象
-
3.把這個對象指向這個空間
多線程時可能發生指令重排問題,執行順序可能不是123而是312等
解決方案:加volatile
問題2:暴力反射可以破壞私有性而建立對象
測試代碼:輸出兩個不同的值,說明破壞了單例模式
//反射!!
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
LazyMan instance=LazyMan.getInstance();
Constructor<single.LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
single.LazyMan instance1 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance1); //輸出兩個不同的值,說明破壞了單例模式
}
問題3:使用兩次反射來破壞私有性而建立對象
測試代碼:
single.LazyMan instance = declaredConstructor.newInstance();
single.LazyMan instance1 = declaredConstructor.newInstance();
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// LazyMan instance=LazyMan.getInstance();
Constructor<single.LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
single.LazyMan instance = declaredConstructor.newInstance();
single.LazyMan instance1 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance1);
}
解決方案:紅綠燈(設定标志位)
- 對wodebianliang保密即可
- 若被反編譯得到變量名就不安全了
private static boolean wodebianliang=false;
private LazyMan(){
synchronized(LazyMan.class){
if(wodebianliang==false){
wodebianliang=true;
}else
throw new RuntimeException("不要試圖用反射破壞異常");
}
}
問題4:标志位被破解時,需要利用源碼的枚舉特性來防止反射
枚舉的源碼中隻有有參構造,沒有無參構造
NoSuchMethodException: single.EnumSingle.()
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
EnumSingle instance1= EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
// NoSuchMethodException: single.EnumSingle.<init>()
//枚舉的源碼中隻有有參構造,沒有無參構造
System.out.println(instance1);
System.out.println(instance2);
使用有參構造方法後,抛出正确的異常
IllegalArgumentException: Cannot reflectively create enum objects
得出結論:反射不能破壞枚舉!!
1.3 靜态内部類
public class Holder {
private Holder(){};
public static Holder getInstance(){
return InnerClass.HOLDER;
}
private static class InnerClass{
private static final Holder HOLDER=new Holder();
}
}
2.工廠模式(建立者和調用者分離)
滿足:開閉原則,依賴倒轉原則,迪米特法則
- 工廠模式:
- 核心本質:執行個體化對象不使用new,用工廠方法代替
- 将選擇實作類,建立對象通過管理和控制,進而實作調用者和實作類解耦。
- 三種模式
- 簡單工廠模式
- 用來生産同一等級結構中的任意産品(對于增加新的産品,需要修改已有代碼)
- 工廠方法模式
- 用來生産同一等級結構中的固定産品(支援增加任意産品)
- 抽象工廠模式
- 圍繞一個超級工廠建立其他工廠,該超級工廠又稱為其他工廠的工廠。不能增加産品,可以增加産品族。
- 簡單工廠模式
2.1簡單工廠模式
需要修改factory才能實作新增對象 “dazhong”
工廠檔案代碼:
//靜态工廠模式--需要修改factory才能實作新增對象 “dazhong”
//開閉原則無法滿足
public class CarFactory {
//方法一
public static Car getCar(String car){
if(car.equals("wuling")){
return new WuLing();
}else if(car.equals("tesila")){
return new Tesila();
}else return null;
}
//方法二
public static Car getWuling(){return new WuLing();}
public static Car getTesila(){return new Tesila();}
調用工廠檔案的代碼:
public class Consumer {
public static void main(String[] args) {
//接口,實作類
WuLing car1 = new WuLing();
Tesila car2 = new Tesila();
//使用工廠建立
Car car = CarFactory.getCar("wuling");
car.name();
car1.name();
car2.name();
}
2.2工廠方法模式
多了一個CarFactory接口和多個繼承該接口的Factory類
CarFactory接口代碼:
//工廠方法模式
public interface CarFactory {
Car getCar();
}
TesilaFactory類實作CarFactory接口:
public class TesilaFactory implements CarFactory{
@Override
public Car getCar() {
return new Tesila();
}
}
調用工廠檔案的代碼:
public class Consumer {
public static void main(String[] args) {
Car car=new WuLingFactory().getCar();
Car car1=new TesilaFactory().getCar();
car.name();
car1.name();
}
}
對比:
結構複雜度 simple
代碼複雜度 simple
程式設計複雜度 simple
管理上的複雜度 simple
根據設計原則 工廠方法模式
根據實際業務 簡單工廠模式
3抽象工廠模式
3.1定義:抽象工廠模式提供了一個建立一系列相關或互相依賴對象的接口,無需指定他們具體的類
3.2結構變化
- 多了抽象接口IProductFactory(超級工廠),HuaWeiFactory和XiaoMiFactory都繼承該接口
//抽象産品工廠
public interface IProductFactory {
//生産手機
IphoneProduct iphoneProduct();
//生産路由器
IRouterProduct irouterProduct();
}
public class HuaWeiFactory implements IProductFactory{
@Override
public IphoneProduct iphoneProduct() {
return new HuaWeiPhone();
}
@Override
public IRouterProduct irouterProduct() {
return new HuaWeiRouter();
}
}
- 測試檔案:先new小米工廠,再調用手機工廠
23種設計模式建立型模式結構型模式
4.建造者模式(用于複雜對象的建造和表示分離)
提供建立對象的最佳模式
4.1結構:
worker是抽象類builder的實作類
director是builder的指導
4.2代碼:
Builder:抽象類,是抽象的建造者。具體實作有對應實體類負責
//抽象的建造者:方法
public abstract class Builder {
abstract void buildA();//地基
abstract void buildB();//鋼筋
abstract void buildC();//電線
abstract void buildD();//粉刷
//完工:得到産品
abstract Product getProduct();
}
Director:指揮者,指揮抽象建造者進行建造。傳回産品。決定建構順序。
//指揮:核心,負責指揮建構一個工程
public class Director {
//指揮勞工安裝順序建房子
public Product build(Builder builder){
builder.buildA();
builder.buildB();
builder.buildC();
builder.buildD();
return builder.getProduct();
}
}
Worker(ConCreteBuilder): 抽象類Builder的實體類,new Product();是實際建造者。重寫抽象類中的get和set方法。調用product中方法實作建構。
//具體的建造者:勞工
public class Worker extends Builder{
private Product product;
public Worker(){
product=new Product(); //注意:勞工負責建立産品
}
@Override
void buildA() {
product.setBuildA("地基");
System.out.println("地基");
}
@Override
void buildB() {
product.setBuildB("鋼筋");
System.out.println("鋼筋");
}
@Override
void buildC() {
product.setBuildC("電線");
System.out.println("電線");
}
@Override
void buildD() {
product.setBuildD("粉刷");
System.out.println("粉刷");
}
@Override
Product getProduct() {
return product;
}
}
Product:具體的産品。為Worder提供建構産品的get和set等方法。
//具體的産品:房子
public class Product {
private String buildA;
private String buildB;
private String buildC;
private String buildD;
public String getBuildA() {
return buildA;
}
public void setBuildA(String buildA) {
this.buildA = buildA;
}
public String getBuildB() {
return buildB;
}
public void setBuildB(String buildB) {
this.buildB = buildB;
}
public String getBuildC() {
return buildC;
}
public void setBuildC(String buildC) {
this.buildC = buildC;
}
public String getBuildD() {
return buildD;
}
public void setBuildD(String buildD) {
this.buildD = buildD;
}
@Override
public String toString() {
return "Product{" +
"buildA='" + buildA + '\'' +
", buildB='" + buildB + '\'' +
", buildC='" + buildC + '\'' +
", buildD='" + buildD + '\'' +
'}';
}
}
4.3 測試
public class Test {
public static void main(String[] args) {
//指揮
Director director = new Director();
//指揮具體勞工完成産品
Product build = director.build(new Worker());
System.out.println(build.toString());
}
}
4.4靜态内部類方式(不需要Director)
靜态内部類使用場景一般是當外部類需要使用内部類,而内部類無需外部類資源,并且内部類可以單獨建立的時候會考慮采用靜态内部類的設計
靜态内部類調用外部類的構造函數,來構造外部類,靜态内部類可以被單獨初始化
4.4.1Builder
//抽象的建造者:方法
public abstract class Builder {
//傳遞參數String msg,實作使用者自定義
abstract Builder buildA(String msg);//漢堡
abstract Builder buildB(String msg);//可樂
abstract Builder buildC(String msg);//薯條
abstract Builder buildD(String msg);//炸雞
//完工:得到産品
abstract Product getProduct();
}
4.4.2Product
//具體的産品:套餐
public class Product {
private String buildA="漢堡";
private String buildB="可樂";
private String buildC="薯條";
private String buildD="炸雞";
//既有預設值,又設定set方法,以實作使用者點套餐和自定義兩種功能需求
public String getBuildA() {
return buildA;
}
public void setBuildA(String buildA) {
this.buildA = buildA;
}
public String getBuildB() {
return buildB;
}
public void setBuildB(String buildB) {
this.buildB = buildB;
}
public String getBuildC() {
return buildC;
}
public void setBuildC(String buildC) {
this.buildC = buildC;
}
public String getBuildD() {
return buildD;
}
public void setBuildD(String buildD) {
this.buildD = buildD;
}
@Override
public String toString() {
return "Product{" +
"buildA='" + buildA + '\'' +
", buildB='" + buildB + '\'' +
", buildC='" + buildC + '\'' +
", buildD='" + buildD + '\'' +
'}';
}
}
4.4.3Worker
//具體的建造者:勞工
public class Worker extends Builder {
private Product product;
public Worker(){
product=new Product(); //注意:勞工負責建立産品
}
// Builder是worker的靜态内部類
@Override
Builder buildA(String msg) {
product.setBuildA(msg);
return this;
}
@Override
Builder buildB(String msg) {
product.setBuildB(msg);
return this;
}
@Override
Builder buildC(String msg) {
product.setBuildC(msg);
return this;
}
@Override
Builder buildD(String msg) {
product.setBuildD(msg);
return this;
}
@Override
Product getProduct() {
return product;
}
}
4.5應用場景及與抽象工廠模式的比較
5.原型模式
應用:
Spring中的bean 可以用單例模式或者原型模式建立
原型模式通常和工廠模式合作使用 : new工廠的操作用原型模式來替換
淺拷貝和深拷貝
淺拷貝
拷貝設定
/*
1.實作一個接口 Cloneable
2.重寫一個方法 clone
*/
//Video
public class Video implements Cloneable{ //克隆别人的視訊的up
private String name;
private Date createTime;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Video(){
}
//其他省略
測試:
public class Bilibili {
public static void main(String[] args) throws CloneNotSupportedException {
//原型對象 v1
Date date=new Date();
Video v1=new Video("狂神說JAVA",date);
System.out.println("v1"+v1);
System.out.println("v1=>hash:"+v1.hashCode());
//v1 克隆出 v2
//克隆出的對象和原對象一模一樣
Video v2= (Video) v1.clone();
System.out.println("v2"+v1);
System.out.println("v2=>hash:"+v1.hashCode());
v2.setName("Clone 狂神說JAVA");
System.out.println(v2);
}
深拷貝
- 序列化和反序列化
- 改造克隆方法
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
//實作深克隆
Video v=(Video) obj;
//将對象的屬性也進行克隆
v.createTime=(Date) this.createTime.clone();
return obj;
}
結構型模式
幫助程式結構上實作松耦合,進而擴大整體的類結構,用來解決更大的問題。
6.擴充卡模式
1.需要适配的類:Adaptee 網線
//要被适配的類: 網線
public class Adaptee {
//實作它的功能 : 上網
public void request(){
System.out.println("連接配接網線上網了");
}
}
2.目标接口(客戶期待的接口):Computer 電腦
//用戶端:想上網,插不上網線
public class Computer {
//我們的電腦需要連接配接上轉接器才能上網
public void net(NetToUSB adapter){
//上網的具體實作 找一個轉接頭
adapter.handleRequest();
}
public static void main(String[] args) {
//電腦,擴充卡,網線
Computer computer = new Computer();
Adaptee adaptee = new Adaptee();
Adapter adapter = new Adapter();
Adapter2 adapter2 = new Adapter2(adaptee);
computer.net(adapter);
computer.net(adapter2);
}
}
3.擴充卡Adapter 網線—電腦轉換連接配接接口
3.1擴充卡接口:NetToUSB
//接口轉換器的抽象實作~
public interface NetToUSB {
//作用:處理請求, 網線=>USB
public void handleRequest();
}
3.2真正的擴充卡
3.2.1 繼承(類擴充卡,單繼承)
//真正的擴充卡, 需要連接配接USB, 需要連接配接網線
public class Adapter extends Adaptee implements NetToUSB{
@Override
public void handleRequest() {
super.request(); //可以上網了
}
}
3.3.2 組合(對象擴充卡:常用)
//不再繼承Adaptee,因為轉接口需要能接任意網線
public class Adapter2 implements NetToUSB{
private Adaptee adaptee; //把網線組合進來
public Adapter2(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void handleRequest() {
adaptee.request();
}
}
7.橋接模式
7.1 品牌
接口
//品牌
public interface Brand {
void info();
}
具體品牌(實作類)
public class Apple implements Brand {
@Override
public void info() {
System.out.println("蘋果");
}
}
public class Lenovo implements Brand{
@Override
public void info() {
System.out.println("聯想");
}
}
7.2 電腦類型(自帶品牌屬性) 組合
抽象類
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);
}
@Override
public void info() {
super.info();
System.out.println("桌上型電腦");
}
}
class Laptop extends Computer{
public Laptop(Brand brand) {
super(brand);
}
@Override
public void info() {
super.info();
System.out.println("筆記本");
}
}
8.代理模式
- 代理模式是SpringAOP的底層
- 代理模式分為靜态代理和動态代理
8.1靜态代理模式
角色分析:
- 抽象角色:一般用抽象類或接口來解決
- 真實角色:被代理的角色
- 代理角色:代理真實角色,代理真實角色後我們一般會進行一些附屬操作
- 客戶:通路代理對象的人
代碼步驟:
1.接口
public interface Rent {
public void rent();
}
2.真實角色
//房東
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房東要出租房子");
}
}
3.代理角色
public class Proxy implements Rent{
private Host host;
public Proxy(){}
public Proxy(Host host){
this.host=host;
}
@Override
public void rent() {
host.rent();
}
//看房
public void seeHouse(){
System.out.println("中介帶你看房子");
}
//簽合同
public void hetong(){
System.out.println("中介和你簽合同");
}
}
4.用戶端通路代理角色
public static void main(String[] args) {
//房東要租房子
Host host=new Host();
//中介(代理角色)代理房東租房子, 同時也有一些附屬操作
Proxy proxy = new Proxy(host);
//你不用找房東,直接找中介租房
proxy.rent();
proxy.seeHouse();
proxy.hetong();
}
}
靜态代理模式的優缺點:
- 優點:
- 可以使真實角色的操作更純粹,不用關注一些公共業務
- 公共業務就交給代理角色,實作了業務分工
- 公共業務發生擴充時,友善集中管理
- 缺點:
- 一個真實角色對應一個代理角色,代碼量翻倍,開發效率低
加深了解
接口
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
真實角色實作接口
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("進行了增加");
}
@Override
public void delete() {
System.out.println("進行了删除");
}
@Override
public void update() {
System.out.println("進行了修改");
}
@Override
public void query() {
System.out.println("進行了查詢");
}
}
代理角色實作接口,聚合真實角色,并增加附屬功能
public class UserServiceProxy implements UserService{
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
@Override
public void add() {
log("add");
userService.add();
}
@Override
public void delete() {
log("delete");
userService.delete();
}
@Override
public void update() {
log("update");
userService.update();
}
@Override
public void query() {
log("query");
userService.query();
}
public void log(String msg){
System.out.println("調用了"+msg+"方法");
}
}
用戶端調用代理角色
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.delete();
}
}
8.2動态代理模式
- 動态代理的底層是反射
- 動态代理和靜态代理的角色一樣
- 動态代理的代理類是動态生成的,不是我們直接寫好的
- 動态代理分為兩大類:基于接口的動态代理,基于類的動态代理
- 基于接口–JDK動态代理【我們在這裡使用】
- 基于類:cglib
- java位元組碼實作:javasist
需要了解兩個類:
Proxy:代理
InvocationHandler:調用處理程式
真實角色和接口同上
代理角色
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//用這個類,自動生産代理類
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent){
this.rent=rent;
}
//生成得到代理類
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
}
//處理代理執行個體,并傳回結果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//動态代理的本質就是使用反射機制實作
seeHouse();
Object result=method.invoke(rent,args);
fare();
return null;
}
public void seeHouse(){
System.out.println("中介帶你看房子");
}
public void fare(){
System.out.println("中介收費");}
}
用戶端
public static void main(String[] args) {
//真實角色
Host host = new Host();
//代理角色:現在沒有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通過調用程式處理角色來處理我們要調用的接口對象
pih.setRent(host);
Rent proxy = (Rent)pih.getProxy(); //這裡的proxy就是動态生成的,我們沒有寫它
proxy.rent();
}
進一步抽象
代理角色(固定代碼,以後可以直接用)
//用這個類,自動生産代理類
public class ProxyInvocationHandler 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);
}
//處理代理執行個體,并傳回結果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName()); //method反射
Object result=method.invoke(target,args);
return null;
}
public void log(String msg){
System.out.println("執行了"+msg+"方法");
}
}
用戶端
public static void main(String[] args) {
//真實角色
UserServiceImpl userService = new UserServiceImpl();
//代理角色,不存在,要生成
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//設定要代理的對象
pih.setTarget(userService);
//動态生成代理類
UserService proxy =(UserService) pih.getProxy();
//執行操作
proxy.delete();
}
優點:
- 可以使真實角色的操作更純粹,不用關注一些公共業務
- 公共業務就交給代理角色,實作了業務分工
- 公共業務發生擴充時,友善集中管理
- 一個動态代理類代理的是一個接口,一般就是對應的一類業務
- 一個動态代理可以代理多個類,隻要是實作了同一個接口