天天看點

設計模式-調停者模式

  調停者模式是對象的行為模式。調停者模式包裝了一系列對象互相作用的方式,使得這些對象不必互相明顯引用。進而便它們可以較松散地耦合。當這些對象中的某些對象之間的互相作用發生改變時,不會立即影響到其它的一些對象之間的互相作用。進而保證這些互相作用可以彼此獨立地變化。 (學習)

  為什麼需要調停者

  如下圖所示,這個示意圖中有大量的對象,這些對既會影響别的對象,又會被别的對象所影響,是以常常叫做同僚(Colleague)對象。這些同僚對象通過彼此的互相作用形成系統的行為。從圖中可以看出,幾乎每一個對象都需要與其它對象發生互相作用,而這種互相作用表現為一個對象與另一個對象的直接耦合。這就是過度耦合的系統。

  通過引入調停者對象(Mediator),可以将系統的網狀結構變成以中介者為中心的星形結構,如下圖所示。在這個星形結構中,同僚對象不再通過直接的聯系與另一個對象發生互相作用;相反的,它通過調停者對象與另一個對象發生互相作用。調停者對象的存在保證了對象結構上的穩定,也就是說,系統的結構不會因為新對象的引入造成大量的修改工作。

  一個好的面向對象的設計可以使對象之間增加協作性(Collaboration),減少耦合度(Couping)。一個深思熟慮的設計會把一個系統分解為一群互相協作的同僚對象,然後給每一個同僚對象以獨特的責任,恰當的配置它們之間的協作關系,使它們可以在一起工作。

如果沒有主機闆  

  大家都知道,電腦裡面各個配件之間的互動,主要是通過主機闆來完成的。如果電腦裡面沒有了主機闆,那麼各個配件之間就必順自行互相互動,以互相傳送資料。而且由于各個配件的接口不同,互相之間互動時,還必順把資料接口進行轉換才能配對得上。

  所幸是有了主機闆,各個配件的互動完全通過主機闆來完成,每個配件都隻需要和主機闆互動,而主機闆知道如何跟所有的配件打交道,這樣就簡單多了。

調停者模式的結構

  調停者模式的示意性類圖如下所示:

                    

調停者模式包含以下角色:

  • 抽象調停者(Mediator)角色:

  定義出同僚對象到調停者對象的接口,其中主要方法是一個(或多個)事件方法。

  • 具體調停者(ConcreteMediator)角色:

  實作了抽象調停者所聲明的事件方法。具體調停者知曉所有的具體同僚類,并負責具體的協調各同僚對象的互動關系。

  • 抽象同僚(Colleague)類角色:

  定義出調停者到同僚對象的接品。同僚對象隻知道調停者而不知道其餘的同僚對象。

  • 具體同僚(ConcreteColleague)類角色:

  所有的具體同僚類均從抽象同僚類繼承而來,實作自已的業務,在需要與其它同僚通信的時候,就與持有的調停者通信,調停者會負責與其他的同僚互動。

源代碼

package Mediator

/**
 * 抽象調停者類
 * */
interface Mediator {
    /**
     * 同僚對象在自身改變的時候來通知調停者方法
     * 讓調停者去負責相應的與其他同僚對象的互動
     * */
    fun changed(colleague: Colleague)
}      
package Mediator

/**
 * 抽象同僚類
 * */
abstract class Colleague constructor(mediator: Mediator) {
    
    //持有一個調停者對象
    private var mediator: Mediator? = null

    init {
        this.mediator = mediator
    }

    /**
     * 擷取目前同僚類對應的調停者對象
     * */
    fun getMediator(): Mediator? {
        return this.mediator
    }
}      
package Mediator

/**
 * 具體調停者類
 * */
class ConcreteMediator : Mediator {

    //持有并維護同僚A
    var concreteColleagueA: ConcreteColleagueA? = null
    //持有并維護同僚B
    var concreteColleagueB: ConcreteColleagueB? = null

    override fun changed(colleague: Colleague) {
        /**
         * 某一同僚類發生了變化,通常需要與其它同僚互動,
         * 具體協調相應的同僚對象來實作協作的行為。
         * */
    }
}      
package Mediator

/**
 * 具體同僚類
 * */
class ConcreteColleagueA constructor(mediator: Mediator) : Colleague(mediator) {
    /**
     * 示意方法,執行某些操作
     * */
    fun opreation(){
        //在需要跟其它同僚通信的時候,通适調停者對象
        getMediator()?.changed(this)
    }
}      
package Mediator

class ConcreteColleagueB constructor(mediator: Mediator) : Colleague(mediator) {
    /**
     * 示意方法,執行某些操作
     * */
    fun opreation(){
        //在需要跟其它同僚通信的時候,通适調停者對象
        getMediator()?.changed(this)
    }
}      

使用電腦來看電影

  在日常生活中,我們經常使用電腦來看電影,把這個過程描述出來,簡化後假定會有如下的互動過程:

  1. 首先是光驅要讀取CD光牒上的資料,然後告訴主機闆,它的狀态改變了。
  2. 主機闆去得到光驅的資料,把這些資料交給cpu進行分析處理。
  3. cpu處理完後,把資料分成了視訊資料和音頻資料,通知主機闆,它處理完了。
  4. 主機闆去得到cpu處理過後的資料,分别把資料交給顯示卡和聲霸卡,去顯示視訊和發出聲音。

  要使用調停者模式來實作示例,那就要區分出同僚對象和調停者對象。很明顯,主機闆是調停者,而光驅、聲霸卡、cpu、顯示等配件,都是作為同僚對象。

源代碼 

package Mediator.example

/**
 * 抽象同僚類
 * */
abstract class Colleague constructor(mediator: Mediator) {
    
    //持有一個調停者對象,目前同僚類對應的調停者對象
    var mediator: Mediator? = null
    
    init {
        this.mediator = mediator
    }
}      
package Mediator.example

/**
 * 同僚類-光驅
 * */
class CDDriver constructor(mediator: Mediator) : Colleague(mediator) {

    //光驅讀取出來的資料
    var data = ""

    /**
     * 讀取CD光牒
     * */
    fun readCD() {
        //逗号前是視訊顯示的資料,逗号後是聲音
        data = "火影, kaka西"
        //通知主機闆自已的狀态發生了改變
        mediator?.changed(this)
    }
}      
package Mediator.example

/**
 * 同僚類-cpu
 * */
class CPU constructor(mediator: Mediator) : Colleague(mediator) {

    //解釋出來的視訊資料
    var videaData = ""
    //解釋出來的聲音資料
    var soundData = ""

    /**
     * 處理資料,把資料分成視訊和聲音資料
     * */
    fun executeData(data: String) {
        val list = data.split(",")
        videaData = list[0]
        soundData = list[1]
        //通知主機闆,cpu完成工作
        mediator?.changed(this)
    }
}      
package Mediator.example

/**
 * 同僚類-顯示卡
 * */
class VideoCard constructor(mediator: Mediator) : Colleague(mediator) {
    /**
     * 顯示視訊資料
     * */
    fun showData(data: String) {
        println("you are watching: $data")
    }
}      
package Mediator.example

/**
 * 同僚類-聲霸卡
 * */
class SoundCard constructor(mediator: Mediator) : Colleague(mediator) {
    /**
     * 按照視訊資料發出聲音
     * */
    fun soundData(data: String) {
        println("sound in video is: $data")
    }
}      
package Mediator.example

/**
 * 抽象調停者類
 * */
interface Mediator {
    /**
     * 同僚對象在自身改變的時候來通知調停者方法,
     * 讓調停者去負責相應的與其他同僚對象的互動
     * */
    fun changed(colleague: Colleague)
}      
package Mediator.example

/**
 * 具體調停者類,這裡是主機闆
 * */
class MainBoard : Mediator {

    //需要知道要互動的同僚類:光驅類
    var cdDriver: CDDriver? = null
    //需要知道要互動的同僚類:CPU類
    var cpu: CPU? = null
    //需要知道要互動的同僚類:顯示卡類
    var videoCard: VideoCard? = null
    //需要知道要互動的同僚類:聲霸卡類
    var soundCard: SoundCard? = null

    /**
     * 處理光驅讀取資料以後,與其他對象的互動
     * */
    fun opeCDDriverReadData(cd: CDDriver) {
        //先擷取光驅讀取的資料
        val data = cd.data
        //把資料交給cpu處理
        cpu?.executeData(data)
    }

    /**
     * 處理cpu處理資料後與其他對象的互動
     * */
    fun opeCPU(cpu: CPU) {
        //先擷取cpu處理後的資料
        val videoData = cpu.videaData
        val soundData = cpu.soundData
        //把這些資料傳遞給顯示卡和聲霸卡
        videoCard?.showData(videoData)
        soundCard?.soundData(soundData)
    }

    override fun changed(colleague: Colleague) {
        if (colleague is CDDriver) {
            //表示光驅讀取了資料
            opeCDDriverReadData(colleague)
        } else if (colleague is CPU) {
            //表示cpu處理了資料
            opeCPU(colleague)
        }
    }
}      

代碼運作

//建立調停者-主機闆
val mainBoard = MainBoard()
//建立同僚類
val cdDriver = CDDriver(mainBoard)
val cpu = CPU(mainBoard)
val videoCard = VideoCard(mainBoard)
val soundCard = SoundCard(mainBoard)
//讓調停者知道所有同僚
mainBoard.cdDriver = cdDriver
mainBoard.cpu = cpu
mainBoard.videoCard = videoCard
mainBoard.soundCard = soundCard
//開始看電影,把CD光牒放入光驅
cdDriver.readCD()      

 運作結果

you are watching: 火影
sound in video is:  kaka西      

 調停者模式的優化點

  • 松散耦合

  調停者模式通過把多個同僚對象之間的互動封裝到調停者對象裡面,進而使得同僚對象之間松散耦合,基本上可以做到互補依賴。這樣一來,同僚對象就可以獨立地變化和複用,而不再像以前那樣“牽一處而動全身”了。

  • 集中控制互動

  多個同僚對象的互動,被封裝在調停者對象裡面集中管理,使得這些互動行為發生變化的時候,隻需要修改調停者對象就可以了,當然如果是已經做好的系統,那麼就擴充調停者對象,而各個同僚類不需要做修改。

  • 多對多變成一對多

  沒有使用調停者模式的時候,同僚對象之間的關系通常是多對多的,引入調停者對象以後,調停者對象和同僚對象的關系通常變成雙向的一對多,這會讓對象的關系更容易了解和實作。

調停者模式的缺點 

  調停者模式的一個潛在缺點是,過度集中化。如果同僚對象的互動非常多,而且比較複雜,當這些複雜性全部集中到調停者的時候,會導緻調停者對象變得十分複雜,而且難于管理和維護。

繼續閱讀