一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Java設計模式之java中介者模式詳解

Java設計模式之java中介者模式詳解

2022-01-04 00:46大忽悠愛忽悠 Java教程

這篇文章主要為大家詳細介紹了23種設計模式之java中介者模式,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助

 

引言

一般來說,同事類之間的關系是比較復雜的,多個同事類之間互相關聯時,他們之間的關系會呈現為復雜的網狀結構,這是一種過度耦合的架構,即不利于類的復用,也不穩定。例如在下圖中,有六個同事類對象,假如對象1發生變化,那么將會有4個對象受到影響。如果對象2發生變化,那么將會有5個對象受到影響。也就是說,同事類之間直接關聯的設計是不好的。

Java設計模式之java中介者模式詳解

如果引入中介者模式,那么同事類之間的關系將變為星型結構,從圖中可以看到,任何一個類的變動,只會影響的類本身,以及中介者,這樣就減小了系統的耦合。一個好的設計,必定不會把所有的對象關系處理邏輯封裝在本類中,而是使用一個專門的類來管理那些不屬于自己的行為。

Java設計模式之java中介者模式詳解

 

介紹

中介者模式(Mediator Pattern):用一個中介對象(中介者)來封裝一系列的對象交互,中介者使各對象不需要顯式地相互引用,從而使其耦合松散,而且可以獨立地改變它們之間的交互。中介者模式又稱為調停者模式,它是一種對象行為型模式。

 

角色

  • Mediator(抽象中介者):它定義一個接口,該接口用于與各同事對象之間進行通信。
  • ConcreteMediator(具體中介者):它是抽象中介者的子類,通過協調各個同事對象來實現協作行為,它維持了對各個同事對象的引用。
  • Colleague(抽象同事類):它定義各個同事類公有的方法,并聲明了一些抽象方法來供子類實現,同時它維持了一個對抽象中介者類的引用,其子類可以通過該引用來與中介者通信。
  • ConcreteColleague(具體同事類):它是抽象同事類的子類;每一個同事對象在需要和其他同事對象通信時,先與中介者通信,通過中介者來間接完成與其他同事類的通信;在具體同事類中實現了在抽象同事類中聲明的抽象方法。

中介者模式的核心在于中介者類的引入,在中介者模式中,中介者類承擔了兩方面的職責:

  • 中轉作用(結構性):通過中介者提供的中轉作用,各個同事對象就不再需要顯式引用其他同事,當需要和其他同事進行通信時,可通過中介者來實現間接調用。該中轉作用屬于中介者在結構上的支持。
  • 協調作用(行為性):中介者可以更進一步的對同事之間的關系進行封裝,同事可以一致的和中介者進行交互,而不需要指明中介者需要具體怎么做,中介者根據封裝在自身內部的協調邏輯,對同事的請求進行進一步處理,將同事成員之間的關系行為進行分離和封裝。

開發中常見的場景

  • MVC模式(Controller 是中介者,根據 View 層的請求來操作 Model 層)
  • 窗口游戲程序,窗口軟件開發中窗口對象也是一個中介者對象
  • 圖形界面開發GUI中,多個組件之間的交互,可以通過引入一個中介者對象來解決,可以是整體的窗口對象或者DOM對象
  • Java.lang.reflect.Method#invoke()

 

數據庫同步數據案例

我們來實現一個簡化版的數據同步方案,有三種數據庫 MysqlRedisElasticsearch

  • 其中的 Mysql 作為主數據庫,當增加一條數據時需要同步到另外兩個數據庫中;
  • Redis 作為緩存數據庫,當增加一條數據時不需要同步到另外另個數據庫;
  • 而 Elasticsearch 作為大數據查詢數據庫,有一個統計功能,當增加一條數據時只需要同步到 Mysql,

所以它們之間的關系圖如下所示。

Java設計模式之java中介者模式詳解

 

不使用中介者模式的數據同步方案,各數據源維護各自的同步作業

抽象數據庫

public abstract class AbstractDatabase
{
    //存儲數據
    protected LinkedList<String> datas=new LinkedList<>();
    //向自己的數據庫中增加數據的方法
    public abstract void addData(String data);
    //同步數據的方法--默認空實現
    public void DataStore(String data){}
    //展示當前數據庫所有數據
    public void display()
    {
        datas.forEach(x->System.out.println(x));
    }
}

具體數據庫 Mysql,維護同步到 Redis和Elasticsearch 的同步作業

public class MySql extends AbstractDatabase
{
    @Setter
     private Elasticsearch elasticsearch;
    @Setter
    private Redis redis;
     //向自己的數據庫增加數據
    @Override
    public void addData(String data)
    {
        System.out.println("====向Mysql數據庫增加一條數據====");
        System.out.println("增加的數據為:"+data);
        System.out.println("=====================================");
        datas.add(data);
    }
     //重寫父類數據同步的方法
    @Override
    public void DataStore(String data)
    {
        addData(data);
        elasticsearch.addData(data);
        redis.addData(data);
    }
}

Elasticsearch ,只需要同步到Mysql

public class Elasticsearch  extends AbstractDatabase
{
    @Setter
    private  MySql mySql;
    //給自己增加數據的方法
    @Override
    public void addData(String data)
    {
        System.out.println("====向Elasticsearch數據庫增加一條數據====");
        System.out.println("增加的數據為:"+data);
        System.out.println("=====================================");
        datas.add(data);
    }
    //重寫父類數據同步的方法
    @Override
    public void DataStore(String data) {
        addData(data);
        //數據同步
        mySql.addData(data);
    }
}

具體數據庫 Redis,不需要同步到其它數據庫

public class Redis extends AbstractDatabase
{
 //給自己增加數據的方法
    @Override
    public void addData(String data)
    {
        System.out.println("====向Redis數據庫增加一條數據====");
        System.out.println("增加的數據為:"+data);
        System.out.println("=====================================");
        datas.add(data);
    }
}

客戶端測試:

public class Client
{
    public static void main(String[] args) {
        Elasticsearch elasticsearch=new Elasticsearch();
        MySql mySql=new MySql();
        Redis redis=new Redis();
        elasticsearch.setMySql(mySql);
        mySql.setElasticsearch(elasticsearch);
        mySql.setRedis(redis);
        //增加數據
        mySql.DataStore("大忽悠");
        elasticsearch.DataStore("李窈");
        redis.addData("小朋友");
        System.out.println("mysql數據庫中的數據如下:");
        mySql.display();
        System.out.println("elasticsearch數據庫中的數據如下:");
        elasticsearch.display();
        System.out.println("redis數據庫中數據如下:");
        redis.display();
    }
}

Java設計模式之java中介者模式詳解

 

其實這樣已經實現了我們的需求,但是存在一些問題

  • 系統結構復雜且耦合度高。數據源需要維護目標端數據庫的引用,以便完成數據同步
  • 組件的可重用性差。由于每一個數據源和目標端之間具有很強的關聯,若沒有目標端的支持,這個組件很難被另一個系統或模塊重用
  • 系統的可擴展性差:如果需要增加、修改或刪除其中一個數據庫、將導致多個類的源代碼需要修改,這違反了“開閉原則”,可擴展性和靈活性欠佳。

 

中介者模式來重構,將數據同步的功能遷移到中介者中,由中介者來管理數據同步作業

抽象中介者:

//抽象中介者
@Data
public abstract class AbstractMediator {
    protected MySql mySql;
    protected Elasticsearch elasticsearch;
    protected Redis redis;
    public abstract void sync(String databaseName, String data);
}

首先還是抽象數據庫類(抽象同事類),維護了一個中介者

public abstract class AbstractDatabase
{
    public static final String MYSQL="mysql";
    public static final String Elasticsearch="elasticsearch";
    public static final String REDIS="redis";
    //保存一個中介者對象
    @Setter
    protected AbstractMediator Mediator;
    //存儲數據
    protected LinkedList<String> datas=new LinkedList<>();
    //向自己的數據庫中增加數據的方法
    public abstract void addData(String data);
    //同步數據的方法--默認空實現
    public void DataStore(String data){}
    //展示當前數據庫所有數據
    public void display()
    {
        datas.forEach(x->System.out.println(x));
    }
}

Mysql 數據庫(具體同事類)

public class MySql extends AbstractDatabase
{
     //向自己的數據庫增加數據
    @Override
    public void addData(String data)
    {
        System.out.println("====向Mysql數據庫增加一條數據====");
        System.out.println("增加的數據為:"+data);
        System.out.println("=====================================");
        datas.add(data);
    }
     //重寫父類數據同步的方法
    @Override
    public void DataStore(String data)
    {
        addData(data);
       //將數據同步到redis和elasticsearch的工作由中介完成
        mediator.sync(AbstractDatabase.MYSQL,data);
    }
}

Redis 數據庫(具體同事類)

public class Redis extends AbstractDatabase
{
 //給自己增加數據的方法
    @Override
    public void addData(String data)
    {
        System.out.println("====向Redis數據庫增加一條數據====");
        System.out.println("增加的數據為:"+data);
        System.out.println("=====================================");
        datas.add(data);
    }
 //重新父類同步數據的方法
    @Override
    public void DataStore(String data) {
        addData(data);
        //同步數據的工作交給中介
        mediator.sync(AbstractDatabase.REDIS,data);
    }
}

Elasticsearch(具體同事類)

public class Elasticsearch  extends AbstractDatabase
{
    //給自己增加數據的方法
    @Override
    public void addData(String data)
    {
        System.out.println("====向Elasticsearch數據庫增加一條數據====");
        System.out.println("增加的數據為:"+data);
        System.out.println("=====================================");
        datas.add(data);
    }
    //重寫父類數據同步的方法
    @Override
    public void DataStore(String data) {
        addData(data);
        //數據同步
        mediator.sync(AbstractDatabase.Elasticsearch,data);
    }
}

具體中介者:

public class SyncMediator extends AbstractMediator {
    @Override
    public void sync(String databaseName, String data) {
        if (AbstractDatabase.MYSQL.equals(databaseName)) {
            // mysql 同步到 redis 和 Elasticsearch
            this.redis.addData(data);
            this.elasticsearch.addData(data);
        } else if (AbstractDatabase.REDIS.equals(databaseName)) {
            // redis 緩存同步,不需要同步到其他數據庫
        } else if (AbstractDatabase.Elasticsearch.equals(databaseName)) {
            // Elasticsearch 同步到 Mysql
            this.mySql.addData(data);
        }
    }
}

測試客戶端

public class Client
{
    public static void main(String[] args) {
        AbstractMediator mediator=new SyncMediator();

        Elasticsearch elasticsearch=new Elasticsearch();
        MySql mySql=new MySql();
        Redis redis=new Redis();

        elasticsearch.setMediator(mediator);
        mySql.setMediator(mediator);
        redis.setMediator(mediator);
        mediator.setMySql(mySql);
        mediator.setElasticsearch(elasticsearch);
        mediator.setRedis(redis);

        //增加數據
        mySql.DataStore("大忽悠");
        elasticsearch.DataStore("李窈");
        redis.DataStore("小朋友");
        System.out.println("mysql數據庫中的數據如下:");
        mySql.display();
        System.out.println("elasticsearch數據庫中的數據如下:");
        elasticsearch.display();
        System.out.println("redis數據庫中數據如下:");
        redis.display();
    }
}

Java設計模式之java中介者模式詳解

 

小結

 

主要優點

  • 中介者模式簡化了對象之間的交互,它用中介者和同事的一對多交互代替了原來同事之間的多對多交互,一對多關系更容易理解、維護和擴展,將原本難以理解的網狀結構轉換成相對簡單的星型結構。
  • 中介者模式可將各同事對象解耦。中介者有利于各同事之間的松耦合,我們可以獨立的改變和復用每一個同事和中介者,增加新的中介者和新的同事類都比較方便,更好地符合 “開閉原則”。
  • 可以減少子類生成,中介者將原本分布于多個對象間的行為集中在一起,改變這些行為只需生成新的中介者子類即可,這使各個同事類可被重用,無須對同事類進行擴展。

 

中介者模式的主要缺點

  • 在具體中介者類中包含了大量同事之間的交互細節,可能會導致具體中介者類非常復雜,使得系統難以維護。(也就是把具體同事類之間的交互復雜性集中到了中介者類中,結果中介者成了最復雜的類)

 

適用場景

  • 系統中對象之間存在復雜的引用關系,系統結構混亂且難以理解。
  • 一個對象由于引用了其他很多對象并且直接和這些對象通信,導致難以復用該對象。
  • 想通過一個中間類來封裝多個類中的行為,而又不想生成太多的子類。可以通過引入中介者類來實現,在中介者中定義對象交互的公共行為,如果需要改變行為則可以增加新的具體中介者類

 

具體應用

 

Java Timer 中的中介者模式

敲一個 java.util.Timer 的Demo

兩個任務類

public class MyOneTask extends TimerTask {
    private static int num = 0;
    @Override
    public void run() {
        System.out.println("I"m MyOneTask " + ++num);
    }
}
public class MyTwoTask extends TimerTask {
    private static int num = 1000;
    @Override
    public void run() {
        System.out.println("I"m MyTwoTask " + num--);
    }
}

客戶端測試,3秒后開始執行,循環周期為 1秒

public class TimerTest {
    public static void main(String[] args) {
        // 注意:多線程并行處理定時任務時,Timer運行多個TimeTask時,只要其中之一沒有捕獲拋出的異常,
        // 其它任務便會自動終止運行,使用ScheduledExecutorService則沒有這個問題
        Timer timer = new Timer();
        timer.schedule(new MyOneTask(), 3000, 1000); // 3秒后開始運行,循環周期為 1秒
        timer.schedule(new MyTwoTask(), 3000, 1000);
    }
}

Timer 的部分關鍵源碼如下

public class Timer {
    private final TaskQueue queue = new TaskQueue();
    private final TimerThread thread = new TimerThread(queue);
    public void schedule(TimerTask task, long delay) {
        if (delay < 0)
            throw new IllegalArgumentException("Negative delay.");
        sched(task, System.currentTimeMillis()+delay, 0);
    }
    public void schedule(TimerTask task, Date time) {
        sched(task, time.getTime(), 0);
    }
    private void sched(TimerTask task, long time, long period) {
        if (time < 0)
            throw new IllegalArgumentException("Illegal execution time.");
        if (Math.abs(period) > (Long.MAX_VALUE >> 1))
            period >>= 1;
        // 獲取任務隊列的鎖(同一個線程多次獲取這個鎖并不會被阻塞,不同線程獲取時才可能被阻塞)
        synchronized(queue) {
            // 如果定時調度線程已經終止了,則拋出異常結束
            if (!thread.newTasksMayBeScheduled)
                throw new IllegalStateException("Timer already cancelled.");
            // 再獲取定時任務對象的鎖(為什么還要再加這個鎖呢?想不清)
            synchronized(task.lock) {
                // 判斷線程的狀態,防止多線程同時調度到一個任務時多次被加入任務隊列
                if (task.state != TimerTask.VIRGIN)
                    throw new IllegalStateException(
                        "Task already scheduled or cancelled");
                // 初始化定時任務的下次執行時間
                task.nextExecutionTime = time;
                // 重復執行的間隔時間
                task.period = period;
                // 將定時任務的狀態由TimerTask.VIRGIN(一個定時任務的初始化狀態)設置為TimerTask.SCHEDULED
                task.state = TimerTask.SCHEDULED;
            }
            // 將任務加入任務隊列
            queue.add(task);
            // 如果當前加入的任務是需要第一個被執行的(也就是他的下一次執行時間離現在最近)
            // 則喚醒等待queue的線程(對應到上面提到的queue.wait())
            if (queue.getMin() == task)
                queue.notify();
        }
    }
    // cancel會等到所有定時任務執行完后立刻終止定時線程
    public void cancel() {
        synchronized(queue) {
            thread.newTasksMayBeScheduled = false;
            queue.clear();
            queue.notify();  // In case queue was already empty.
        }
    }
    // ...
}

Timer 中在 schedulexxx 方法中通過 TaskQueue 協調各種 TimerTask 定時任務,Timer 是中介者,TimerTask 是抽象同事類,而我們自己寫的任務則是具體同事類

TimerThread Timer 中定時調度線程類的定義,這個類會做為一個線程一直運行來執行 Timer 中任務隊列中的任務。

Timer 這個中介者的功能就是定時調度我們寫的各種任務,將任務添加到 TaskQueue 任務隊列中,給 TimerThread 執行,讓任務與執行線程解耦

 

參考文章

設計模式之中介者模式

23種設計模式(7):中介者模式

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注服務器之家的更多內容!

原文鏈接:https://blog.csdn.net/m0_53157173/article/details/120212092

延伸 · 閱讀

精彩推薦
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7482021-02-04
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
主站蜘蛛池模板: 91精品国产91久久久久久 | 男女男精品视频免费观看 | 视频一区二区国产无限在线观看 | 青山葵在线| 99福利在线观看 | 国产播放啪视频免费视频 | 97超pen个人视频公开视频视 | 国产特级 | 乌克兰13一14娇小 | 色多多视频在线 | 成人在线视频国产 | 九九热在线免费观看 | 人人人人人看碰人人免费 | 日本一区视频在线 | 香蕉国产成版人视频在线观看 | 国产精品一区二区三区免费 | 任我淫| 免费特黄一级欧美大片在线看 | 国产成人愉拍免费视频 | 99久久精品99999久久 | 国产成人精品一区二区不卡 | 精品国产自在现线拍400部 | 国产女乱淫真高清免费视频 | 朝鲜女人free性hu | 99年水嫩漂亮粉嫩在线播放 | 香蕉久久一区二区不卡无毒影院 | 8mav福利视频 | 亚洲一卡2卡三卡4卡5卡组 | 干操网| jzjzjz日本在线观看 | 日本高清视频在线观看 | 13日本xxxxxxxxx18| 亚洲国产日韩欧美一区二区三区 | 99热视频| 紧身牛仔裤美女被啪啪久久网 | 日本www午夜色在线视频 | 性插图动态图无遮挡 | 四大美女思春艳史片 | 久久er99热精品一区二区 | 男生同性啪视频在线观看 | 精品9e精品视频在线观看 |