16.Mediator中介者(行为型模式)

Posted zzyzxb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了16.Mediator中介者(行为型模式)相关的知识,希望对你有一定的参考价值。

一、动机(Motivation)

<1>在软件构建过程中,经常会出现多个对象互相关联交互的情况,对象之间常常会维持一种复杂的引用关系,如果遇到一些需求的更改,这种直接的引用关系将面临不断的变化;
<2>在这种情况下,我们可使用一个“中介对象”来管理对象间的关联关系,避免相互交互的对象之间的紧耦合引用关系,从而更好地抵御变化。

二、意图(Intent)

用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式的相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
——《设计模式》GoF

三、结构(Structure)

四、结构详解

五、生活中的例子

<1>联合国机构下设安理会,专门负责各国之间的调停和协调工作;
<2>不同国家之间通过联合国机构进行事务声明。

六、实现

namespace Test

    //Mediator
    public abstract class 联合国机构
    
        protected System.Collections.Generic.List<国家> 成员国 = new List<国家>();
        public void 接收新成员国(国家 colleague)
        
            if (!this.成员国.Contains(colleague))
            
                this.成员国.Add(colleague);
            
        
        public void 开除成员国(国家 colleague)
        
            if (this.成员国.Contains(colleague))
            
                this.成员国.Remove(colleague);
                Console.WriteLine("<0>:开除成员国:1", this, colleague);
            
        
        public abstract void 发表声明(string 内容, 国家 发表者);
    

    //ConcreteMediator
    public class 安理会 : 联合国机构
    
        public override void 发表声明(string 内容, 国家 发表者)
        
            foreach (国家 colleague in base.成员国)
            
                if (colleague != 发表者)
                
                    colleague.接收声明(内容, 发表者);
                
            
        
    

    //Colleague
    public abstract class 国家
    
        protected 联合国机构 _协调人;
        public 国家(联合国机构 mediator)
        
            this._协调人 = mediator;
            mediator.接收新成员国(this);
            Console.WriteLine("0在1\\t加入联合国", this.ToString(), DateTime.Now);
        
        public virtual void 发表声明(string 内容)
        
            Console.WriteLine("<0>发表声明:\\r\\n\\t1", this, 内容);
            this._协调人.发表声明(内容, this);
        
        public virtual void 接收声明(string 内容, 国家 发表者)
        
            Console.WriteLine("0收到<1>发表的声明:\\r\\n\\t2\\t时间:3", this, 发表者, 内容, DateTime.Now);
        
    

    public class 美国 : 国家
    
        public 美国(联合国机构 meidator) : base(meidator)
        
        
        public override void 接收声明(string 内容, 国家 发表者)
        
            base.接收声明(内容, 发表者);
            if (内容.Contains("核武器"))
            
                Console.WriteLine("!!!!警报:FBI开始对0进行全程监控", 发表者);
            
        
    

    public class 伊拉克 : 国家
    
        public 伊拉克(联合国机构 meidator) : base(meidator)
        
        
        public override void 接收声明(string 内容, 国家 发表者)
        
            base.接收声明(内容, 发表者);
            if (内容.Contains("核武器"))
            
                Console.WriteLine("<0>说:额是冤枉的,1不要诬陷好人", this, 发表者);
            
        
        public override void 发表声明(string 内容)
        
            if (内容.Contains("恐怖活动"))
            
                Console.WriteLine("<0>:******低调,低调,低调*******", this);
            
            base.发表声明(内容);
        
    

    internal class Program
    
        static void Main(string[] args)
        
            联合国机构 mediator = new 安理会();  //调停者mediator
            国家 usa = new 美国(mediator);  //colleague
            国家 iraq = new 伊拉克(mediator);  //colleague

            usa.发表声明("禁止研制核武器和大规模杀伤武器,否则就开打!");  //不直接与iraq耦合
            iraq.发表声明("严正声明:本国没有研制任何核武器!");  //不直接与usa耦合
            iraq.发表声明("别欺国太甚,否则本国会悍然来次恐怖活动!");
            mediator.开除成员国(iraq);  //iraq不听话,开除之
            usa.发表声明("再次声明:禁止研制核武器和大规模杀伤武器,否则就开打!");  //iraq再也接收不到了

            Console.ReadLine();
        
    

实现结果

七、实现要点

<1>中介者角色集中了太多的责任,所有有关的同事对象都要由它来控制;
<2>建议在使用中介者模式的时候注意控制中介者角色的大小。

八、效果

<1>将同事角色解耦;
<2>系统结构改善:提高了原有系统的可读性、简化原有系统的通信协议——将原有的多对多变为一对多、提高了代码的可复用性;
<3>“与其多个人累,不如我一个人来累"

九、适用性

<1>多个对象互相关联交互的情况,对象之间维持一种复杂的引用关系;
<2>一组对象以定义良好但是复杂的方式进行通信,产生了混乱的依赖关系,也导致对象难以复用。

十、总结

<1>将多个对象间复杂的关联关系解耦,Mediator模式将多个对象间的控制逻辑进行集中管理,变“多个对象互相关联”为“多个对象和一个中介者关联”,简化了系统的维护,抵御了可能的变化;
<2>随着控制逻辑的复杂化,Mediator具体对象的实现可能相当复杂。这时候可以对Mediator对象进行分解处理;
<3>Façade模式是解耦系统外到系统内(单向)的对象关联关系;Mediator模式是解耦系统内各个对象之间(双向)的关联关系。

JAVA SCRIPT设计模式--行为型--设计模式之Mediator中介者模式(17)

         JAVA SCRIPT设计模式是本人根据GOF的设计模式写的博客记录。使用JAVA SCRIPT语言来实现主体功能,所以不可能像C++,JAVA等面向对象语言一样严谨,大部分程序都附上了JAVA SCRIPT代码,代码只是实现了设计模式的主体功能,不代表全部的正确,特此声明。若读者需要了解设原则、设计变化方向,环境相关等信息请查看设计模式开篇

        所有JAVA SCRIPT设计模式快捷连接:

              创建型:(1)  抽象工厂 (2) 生成器 (3) 工厂方法 (4) 原型  (5) 单例

              结构型:(6) 适配器  (7) 桥接  (8) 组合 (9) 装饰 (10) 外观 (11) 享元 (12) 代理​

              行为型:(13) ​职责链 (14) ​命令 (15) ​解释器 (16) ​迭代器 (17) ​中介者 (18) ​备忘录 (119) ​观察者 (20) ​状态​ (21) ​策略 (22) ​模板方法 (23) 访问者​


一、UML类图

参与者:

1.1 Mediator(中介者,如DialogDirector)

  • 中介者定义一个接口用于与各同事(Colleague)对象通信

1.2 ConcreteMediator(具体中介者,如FontDialogDirector)

  • 具体中介者通过协调各同事对象实现协作行为。 
  • 了解并维护它的各个同事。

1.3 Colleagueclass(同事类,如ListBox,EntryField)

  • 每一个同事类都知道它的中介者对象。 
  • 每一个同事对象在需与其他的同事通信的时候,与它的中介者通信。

二、意图

     用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从 而使其耦合松散,而且可以独立地改变它们之间的交互。

三、适用性 

  1. 一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。
  2. 一个对象引用其他很多对象并且直接与这些对象通信 ,导致难以复用该对象。
  3. 想定制一个分布在多个类中的行为,而又不想生成太多的子类。

四、优点和缺点

  1. 减少了子类生成Mediator将原本分布于多个对象间的行为集中在一起。改变这些行为只需生成Meditator的子类即可。这样各个Colleague类可被重用
  2. 它将各Colleague解耦Mediator有利于各Colleague间的松耦合.你可以独立的改变和复用各Colleague类和Mediator类。
  3. 它简化了对象协议用Mediator和各Colleague间的一对多的交互来代替多对多的交互。一对多的关系更易于理解、维护和扩展
  4. 它对对象如何协作进行了抽象将中介作为一个独立的概念并将其封装在一个对象中,使你将注意力从对象各自本身的行为转移到它们之间的交互上来。这有助于弄清楚一个系统中的对象是如何交互的。
  5. 它使控制集中化中介者模式将交互的复杂性变为中介者的复杂性。因为中介者封装了协议,它可能变得比任一个Colleague都复杂。这可能使得中介者自身成为一个难于维护的庞然大物。

五、示例代码

5.1  动机

        面向对象设计鼓励将行为分布到各个对象中。这种分布可能会导致对象间有许多连接。 在最坏的情况下,每一个对象都知道其他所有对象。

        虽然将一个系统分割成许多对象通常可以增强可复用性 , 但是对象间相互连接的激增又会降低其可复用性。大量的相互连接使得一个对象似乎不太可能在没有其他对象的支持下工作 —系统表现为一个不可分割的整体。而且 ,对系统的行为进行任何较大的改动都十分困难, 因为行为被分布在许多对象中。结果是 , 你可能不得不定义很多子类以定制系统的行为。

        例如,考虑一个图形用户界面中对话框的实现。对话框使用一个窗口来展现一系列的窗 口组件, 如按钮、菜单和输入域等, 如下图所示。


        通常对话框中的窗口组件间存在依赖关系。例如 , 当一个特定的输入域为空时 , 某个按钮不能使用;在称为列表框的一列选项中选择一个表目可能会改变一个输入域的内容;反过来,在输入域中输入正文可能会自动的选择一个或多个列表框中相应的表目;一旦正文出现在输 入域中, 其他一些按钮可能就变得能够使用了,这些按钮允许用户做一些操作 , 比如改变或删 除这些正文所指的东西。

        不同的对话框会有不同的窗口组件间的依赖关系。因此即使对话框显示相同类型的窗口 组件, 也不能简单地直接重用已有的窗口组件类 ; 而必须定制它们以反映特定对话框的依赖关 系。由于涉及很多个类,用逐个生成子类的办法来定制它们会很冗长。

        可以通过将集体行为封装在一个单独的中介者(Mediator )对象中以避免这个问题。中介者 负责控制和协调一组对象间的交互。中介者充当一个中介以使组中的对象不再相互显式引用。 这些对象仅知道中介者, 从而减少了相互连接的数目。 

5.2  示例UML

        例如,FontDialogDirector可作为一个对话框中的窗口组件间的中介者。FontDialogDirector对象知道对话框中的各窗口组件,并协调它们之间的交互。它充当窗口组件间通信的中转中心,
如下图所示。

         下面的交互图说明了各对象如何协作处理一个列表框中选项的变化。

         DialogDirector是一个抽象类,它定义了一个对话框的总体行为。客户调用ShowDialog操作将对话框显示在屏幕上。CreateWidgets是创建一个对话框的窗口组件的抽象操作。WidgetChanged是另一个抽象操作;窗口组件调用它来通知它的导控者它们被改变了。DialogDirector的子类将重定义CreateWidgets以创建正确的窗口组件,并重定义WidgetChanged以处理其变化。

目录结构:

5.3 Mediator(中介者,如DialogDirector)

  • 中介者定义一个接口用于与各同事(Colleague)对象通信

export default  class DialogDirector 
    widgetChildrens=[];
	
    constructor( ) 
		 
    
    ShowDialog()    
		this.ctx.clearRect(this.rect.startx,this.rect.starty,this.rect.width,this.rect.height);
		this.ctx.strokeRect(this.rect.startx,this.rect.starty,this.rect.width,this.rect.height);
		for(let n=0;n<this.widgetChildrens.length;n++)
		
			let widge=this.widgetChildrens[n];
			widge.Draw();
		 
    
	
	CreateWidgets() 
		 
	
	WidgetChanged(widget) 
		 
	
	
	 
  

5.4 ConcreteMediator(具体中介者,如FontDialogDirector)

  • 具体中介者通过协调各同事对象实现协作行为。 
  • 了解并维护它的各个同事。
import DialogDirector from '../DialogDirector.js';
import Button from '../../Widget/impl/Button.js';
import ListBox from '../../Widget/impl/ListBox.js';
import AntryField from '../../Widget/impl/AntryField.js';

export default class FontDialogDirector extends DialogDirector 
    ctx;
	rect;
	zooChildrens=[];
	  fontListBox ;
	  cancelButton ;
	  confirmButton ;
	  antryField ;
	
	constructor(ctx,rect) 
		super();
		this.ctx=ctx;
		this.rect=rect;
		this.CreateWidgets();
	
	CreateWidgets() 
        this.zooChildrens=[];
		this.widgetChildrens=[];
		this.ctx.strokeRect(this.rect.startx,this.rect.starty,this.rect.width,this.rect.height);
		let fontList = ['黑体:SimHei',
			'宋体:SimSun ',
			'新宋体:NSimSun',
			'仿宋:FangSong',
			'楷体:KaiTi ',
			'微软雅黑体:Microsoft YaHei'
		];
		let fontListZoo=startx:10,starty:10,width:180,height:200;
		let cancelBuZoo=startx:100,starty:220,width:40,height:25;
		let confirmBu=startx:150,starty:220,width:40,height:25;
		let antryFieldZoo=startx:10,starty:250,width:180,height:25;
		this.zooChildrens.push(fontListZoo);
		this.zooChildrens.push(cancelBuZoo);
		this.zooChildrens.push(confirmBu);
		this.zooChildrens.push(antryFieldZoo);
		
		this.fontListBox = new ListBox(this.ctx,fontList, this,fontListZoo);
		this.cancelButton = new Button(this.ctx,'取消', this,cancelBuZoo);
		this.confirmButton = new Button(this.ctx,'确定', this,confirmBu );
		this.antryField =new AntryField(this.ctx,'', this,antryFieldZoo );
		this.widgetChildrens.push(this.fontListBox);
		this.widgetChildrens.push(this.cancelButton);
		this.widgetChildrens.push(this.confirmButton);
		this.widgetChildrens.push(this.antryField);
		
	

	WidgetChanged(widget) 
		let text=this.fontListBox.GetSelection();
		this.antryField.SetText(text);
	
	
	onmousedown(e)
	
		 let selectN=this.GetActiveItem(e);
		 if(selectN>-1)
		 
		 	for(let n=0;n<this.widgetChildrens.length;n++)
		 	
		 		let widget=this.widgetChildrens[n];
		 	    if(n==selectN)
		 			widget.SetIsClicked(true,e);
		 		else 
		 		    widget.SetIsClicked(false,e);
		 	
		 
	
	onmousemove(e)
	
		  let selectN=this.GetActiveItem(e);
		  if(selectN>-1)
		  
		  	for(let n=0;n<this.widgetChildrens.length;n++)
		  	
		  		let widget=this.widgetChildrens[n];
		  	    if(n==selectN)
		  			widget.SetIsMouseIn(true,e);
		  		else 
		  		    widget.SetIsMouseIn(false,e);
		  	
		  
	
	
	GetActiveItem(e)
	
		 var x = e.clientX;
		 var y = e.clientY;
		 if(x>this.rect.startx&&x<this.rect.startx+this.rect.width
		 &&y>this.rect.starty&&y<this.rect.starty+this.rect.height )
		 
		 	 for(let n=0;n<this.zooChildrens.length;n++)
		 	 
		 	 	 let zooChild=this.zooChildrens[n];
		 	 	 if(x>zooChild.startx&&x<zooChild.startx+zooChild.width
		 	 	 &&y>zooChild.starty&&y<zooChild.starty+zooChild.height )
				 
					 return  n;
				 
		 	 
		  
		 else
		 return -1;
	



5.5 Colleagueclass(同事类,如ListBox,EntryField)

  • 每一个同事类都知道它的中介者对象。 
  • 每一个同事对象在需与其他的同事通信的时候,与它的中介者通信。
import Widget  from '../Widget.js';

export default  class AntryField extends Widget 
	text;
	rect;
	ctx;
    constructor(ctx,text,director,rect) 
		 super(director);
		 this.text=text;
		 this.rect=rect;
		 this.ctx=ctx;
    
    Draw()
    
		this.ctx.strokeRect(this.rect.startx,this.rect.starty,this.rect.width,this.rect.height);
		if(this.isMouseIn)
			this.ctx.save();
			this.ctx.fillStyle="red";
			this.ctx.fillRect(this.rect.startx,this.rect.starty,this.rect.width,this.rect.height);
			this.ctx.restore();
		
    	this.ctx.textAlign="center";
    	this.ctx.fillText(this.text,this.rect.startx+this.rect.width/2,this.rect.starty+3*this.rect.height/4);
     
	Clicked()
	
		 console.log(this.name+` Clicked `); 
	
	SetText(text)
	
		this.text=text;
		this.Draw();
	
	
 
import Widget  from '../Widget.js';

export default  class Button extends Widget 
	name;
	rect;
	ctx;
    constructor(ctx,name,director,rect) 
		 super(director);
		 this.name=name;
		 this.rect=rect;
		 this.ctx=ctx;
    
    Draw()
    
		this.ctx.strokeRect(this.rect.startx,this.rect.starty,this.rect.width,this.rect.height);
		if(this.isMouseIn)
			this.ctx.save();
			this.ctx.fillStyle="red";
			this.ctx.fillRect(this.rect.startx,this.rect.starty,this.rect.width,this.rect.height);
			this.ctx.restore();
		
    	this.ctx.textAlign="center";
    	this.ctx.fillText(this.name,this.rect.startx+this.rect.width/2,this.rect.starty+3*this.rect.height/4);
     
	Clicked()
	
		console.log(this.name+` Clicked `); 
	
	
 

import Widget  from '../Widget.js';

export default  class ListBox extends Widget 
	 
	nameList=[];
	rect;
	itemHeight=20;
	ctx;
	zooChildrens=[];
	selectN=-1;
    constructor(ctx,nameList,director,rect) 
		 super(ctx,director);
		 this.nameList=[];
		 this.nameList=nameList;
		 this.rect=rect;
		 this.ctx=ctx;
    
   Draw()
   
	   this.zooChildrens=[];
   	  this.ctx.strokeRect(this.rect.startx,this.rect.starty,this.rect.width,this.rect.height);
	  let x=this.rect.startx+10;
	  let y=this.rect.starty+20;
	  for(let n=0;n<this.nameList.length;n++)
	  
	  	let name=this.nameList[n];
		let itemRect=startx:x,starty:y,width:180,height:20;
		if(this.isMouseIn&&n==this.selectN)
			this.ctx.save();
			this.ctx.fillStyle="blue";
			this.ctx.fillRect(itemRect.startx,itemRect.starty,itemRect.width-30,itemRect.height);
			this.ctx.restore();
		
		this.ctx.textAlign="left";
		this.ctx.fillText(name,itemRect.startx+10,itemRect.starty+3*itemRect.height/4);
		// this.ctx.fillText(name,x,y);
		this.zooChildrens.push(itemRect);
		y=y+this.itemHeight;
		if(y>this.rect.height) break;
	  
     
   GetSelection()
   
	   if(this.selectN>-1)
	    return this.nameList[this.selectN];
   
  Clicked()
  
  
	this.Changed();
   
  SetIsClicked(isClicked,e)
  
  	this.isclicked=isClicked;
  	if(isClicked)
  	
		this.selectN=this.GetActiveItem(e);
		if(this.selectN>-1)
  		  this.Clicked();
  	
  
  SetIsMouseIn(isMouseIn,e)
  
  	this.isMouseIn=isMouseIn;
	if(isMouseIn)
	
		this.selectN=this.GetActiveItem(e);
	
  
  
  GetActiveItem(e)
  
  	 var x = e.clientX;
  	 var y = e.clientY;
  	 if(x>this.rect.startx&&x<this.rect.startx+this.rect.width
  	 &&y>this.rect.starty&&y<this.rect.starty+this.rect.height )
  	 
  	 	 for(let n=0;n<this.zooChildrens.length;n++)
  	 	 
  	 	 	 let zooChild=this.zooChildrens[n];
  	 	 	 if(x>zooChild.startx&&x<zooChild.startx+zooChild.width
  	 	 	 &&y>zooChild.starty&&y<zooChild.starty+zooChild.height )
  			 
  				 return  n;
  			 
  	 	 
  	  
  	 else
  	 return -1;
  
	
 

5.6  Client

import FontDialogDirector  from './DialogDirector/impl/FontDialogDirector.js';
 
 

export default class Client
    fontDialogDirector;
    constructor(ctx,zooRect) 
    	this.fontDialogDirector=new FontDialogDirector(ctx,startx:0,starty:0,width:300,height:300); 
		this.fontDialogDirector.ShowDialog();
    
	
	onmousedown(e)
	
		  this.fontDialogDirector.onmousedown(e);
		  this.fontDialogDirector.ShowDialog();
	
	onmousemove(e)
	
		  this.fontDialogDirector.onmousemove(e);
		  this.fontDialogDirector.ShowDialog();
	
    
 

5.7 测试HTML

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">


		<script type="module">
			import Client from './Client.js';
			var canvas = document.getElementById("mycanvas")
			var ctx = canvas.getContext("2d") //create 2d object
			let cl = new Client(ctx,startx:0,starty:0,width:900,height:900);
		 canvas.onmousedown = (e) => 
		 	cl.onmousedown(e);
		 ;
		 canvas.onmousemove = (e) => 
		 	cl.onmousemove(e);
		 ;
		 
		</script>
	</head>
	<body>
		<canvas id="mycanvas" width=900px height=900px></canvas>

	</body>
</html>

测试结果:

六、源代码下载

        下载链接:https://pan.baidu.com/s/1XuPqp84cccBNVkbnMY3sKw 
         提取码:q2ut

以上是关于16.Mediator中介者(行为型模式)的主要内容,如果未能解决你的问题,请参考以下文章

手撸golang 行为型设计模式 中介者模式

Python 设计模式 — 行为型模式 — 中介者模式

Python 设计模式 — 行为型模式 — 中介者模式

设计模式——行为型模式之中介者模式

设计模式-行为型模式讲解三(观察者状态中介者)

行为型模式-中介者模式