电脑中如果没有主板,那么就会出现如下图所示的乱成一团的结构。
如果上面的情况发生在软件开发呢?
如果软件开发需要很多模块,难道要每个模块同其他模块各个交互么?那岂不是太不好用么?那么如何简化这种交互呢?跟电脑一样,弄一个中介者就行了。所有的模块跟中介者交互,那么就降低了模块间的耦合性。
2.举一个稍微具体的例子 使用电脑看电影:
首先是光驱读取光盘数据,告诉主板,状态改变
主办得到数据,交给CPU处理
CPU处理完,把数据分为视频和音频,通知主板,它处理完了。
主板去得到CPU处理过后的数据,把数据交给显卡和声卡,然后显示出视频和发出声音。
3.解决方案:中介者模式 3.1中介者定义: 用一个中介对象来封装一系列的对象交互,中介者使得各对象不需要显示的相互引用,从而使其耦合松散,而且可以独立的改变他们之间的交互。这样会有利于对象的修改和维护。
3.2中介者结构图:
4.示例代码如下 4.1同事类的抽象父类(注意是个抽象类)
package demo09.mediator.example1; /** * 同事类的抽象父类 */ public abstract class Colleague { /** * 持有中介者对象,每一个同事类都知道它的中介者对象 */ private Mediator mediator; /** * 构造方法,传入中介者对象 * * @param mediator * 中介者对象 */ public Colleague(Mediator mediator) { this.mediator = mediator; } /** * 获取当前同事类对应的中介者对象 * * @return 对应的中介者对象 */ public Mediator getMediator() { return mediator; } }4.2同事A注意:是继承奥
package demo09.mediator.example1; /** * 具体的同事类A */ public class ConcreteColleagueA extends Colleague { public ConcreteColleagueA(Mediator mediator) { super(mediator); } /** * 示意方法,执行某些业务功能 */ public void someOperation() { //在需要跟其他同事通信的时候,通知中介者对象 getMediator().changed(this); } }4.3同事B注意:是继承
package demo09.mediator.example1; /** * 具体的同事类B */ public class ConcreteColleagueB extends Colleague { public ConcreteColleagueB(Mediator mediator) { super(mediator); } /** * 示意方法,执行某些业务功能 */ public void someOperation() { //在需要跟其他同事通信的时候,通知中介者对象 getMediator().changed(this); } }4.4中介者,定义各个同事对象通信的接口
package demo09.mediator.example1; /** * 中介者,定义各个同事对象通信的接口 */ public interface Mediator { /** * 同事对象在自身改变的时候来通知中介者的方法, * 让中介者去负责相应的与其他同事对象的交互 * @param colleague 同事对象自身,好让中介者对象通过对象实例 * 去获取同事对象的状态 */ public void changed(Colleague colleague); }4.5具体的中介者实现 所有的同事交互都是在中介者中实现
package demo09.mediator.example1; /** * 具体的中介者实现 */ public class ConcreteMediator implements Mediator { /** * 持有并维护同事A */ private ConcreteColleagueA colleagueA; /** * 持有并维护同事B */ private ConcreteColleagueB colleagueB; /** * 设置中介者需要了解并维护的同事A对象 * @param colleague 同事A对象 */ public void setConcreteColleagueA(ConcreteColleagueA colleague) { colleagueA = colleague; } /** * 设置中介者需要了解并维护的同事B对象 * @param colleague 同事B对象 */ public void setConcreteColleagueB(ConcreteColleagueB colleague) { colleagueB = colleague; } public void changed(Colleague colleague) { //某个同事类发生了变化,通常需要与其他同事交户 //具体协调相应的同事对象来实现协作行为 } }5.使用中介者实现范例: 5.1结构示意图
5.2同事类的抽象父类(注意:是个抽象类)
package demo09.mediator.example2; /** * 同事类的抽象父类 */ public abstract class Colleague { /** * 持有中介者对象,每一个同事类都知道它的中介者对象 */ private Mediator mediator; /** * 构造方法,传入中介者对象 * @param mediator 中介者对象 */ public Colleague(Mediator mediator) { this.mediator = mediator; } /** * 获取当前同事类对应的中介者对象 * @return 对应的中介者对象 */ public Mediator getMediator() { return mediator; } }5.3光驱类,一个同事类
package demo09.mediator.example2; /** * 光驱类,一个同事类 */ public class CDDriver extends Colleague{ public CDDriver(Mediator mediator) { super(mediator); } /** * 光驱读取出来的数据 */ private String data = ""; /** * 获取光驱读取出来的数据 * @return 光驱读取出来的数据 */ public String getData(){ return this.data; } /** * 读取光盘 */ public void readCD(){ //逗号前是视频显示的数据,逗号后是声音 this.data = "设计模式,值得好好研究"; //通知主板,自己的状态发生了改变 this.getMediator().changed(this); } }5.4CPU类,一个同事类
package demo09.mediator.example2; /** * CPU类,一个同事类 */ public class CPU extends Colleague{ public CPU(Mediator mediator) { super(mediator); } /** * 分解出来的视频数据 */ private String videoData = ""; /** * 分解出来的声音数据 */ private String soundData = ""; /** * 获取分解出来的视频数据 * @return 分解出来的视频数据 */ public String getVideoData() { return videoData; } /** * 获取分解出来的声音数据 * @return 分解出来的声音数据 */ public String getSoundData() { return soundData; } /** * 处理数据,把数据分成音频和视频的数据 * @param data 被处理的数据 */ public void executeData(String data){ //把数据分解开,前面的是视频数据,后面的是音频数据 String [] ss = data.split(","); this.videoData = ss[0]; this.soundData = ss[1]; //通知主板,CPU的工作完成 this.getMediator().changed(this); } }5.5显卡类,一个同事类
package demo09.mediator.example2; /** * 显卡类,一个同事类 */ public class VideoCard extends Colleague { public VideoCard(Mediator mediator) { super(mediator); } /** * 显示视频数据 * * @param data * 被显示的数据 */ public void showData(String data) { System.out.println("您正观看的是:" + data); } }5.6声卡类,一个同事类
package demo09.mediator.example2; /** * 声卡类,一个同事类 */ public class SoundCard extends Colleague{ public SoundCard(Mediator mediator) { super(mediator); } /** * 按照声频数据发出声音 * @param data 发出声音的数据 */ public void soundData(String data){ System.out.println("画外音:"+data); } }5.7中介者接口
package demo09.mediator.example2; /** * 中介者对象的接口 */ public interface Mediator { /** * 同事对象在自身改变的时候来通知中介者的方法, * 让中介者去负责相应的与其他同事对象的交互 * @param colleague 同事对象自身,好让中介者对象通过对象实例 * 去获取同事对象的状态 */ public void changed(Colleague colleague); }5.8主板类,实现中介者接口
package demo09.mediator.example2; /** * 主板类,实现中介者接口 */ public class MotherBoard implements Mediator{ /** * 需要知道要交互的同事类——光驱类 */ private CDDriver cdDriver = null; /** * 需要知道要交互的同事类——CPU类 */ private CPU cpu = null; /** * 需要知道要交互的同事类——显卡类 */ private VideoCard videoCard = null; /** * 需要知道要交互的同事类——声卡类 */ private SoundCard soundCard = null; public void setCdDriver(CDDriver cdDriver) { this.cdDriver = cdDriver; } public void setCpu(CPU cpu) { this.cpu = cpu; } public void setVideoCard(VideoCard videoCard) { this.videoCard = videoCard; } public void setSoundCard(SoundCard soundCard) { this.soundCard = soundCard; } public void changed(Colleague colleague) { if(colleague == cdDriver){ //表示光驱读取数据了 this.opeCDDriverReadData((CDDriver)colleague); }else if(colleague == cpu){ //表示CPU处理完了 this.opeCPU((CPU)colleague); } } /** * 处理光驱读取数据过后与其他对象的交互 * @param cd 光驱同事对象 */ private void opeCDDriverReadData(CDDriver cd){ //1:先获取光驱读取的数据 String data = cd.getData(); //2:把这些
在上篇文章中我们已经对JMS的基本模型有了基本的了解.下面内容主要是通过一张图来了解JMS的编程模型,并用一个实例来进行辅助说明.
下面这张图是JMS的编程模型,
Connection Factory
创建Connection对象的工厂,针对两种不同的JMS消息模型,分别有QueueConnectionFactory和TopicConnectionFactory两种。可以通过JNDI来查找ConnectionFactory对象。
//获取上下文信息
InitialContextcontext = new InitialContext();
//创建QueueConnectionFactory
QueueConnectionFactoryfactory=(QueueConnectionFactory)context.lookup("ConnectionFactory");
Connection
Connection表示在客户端和JMS系统之间建立的链接(对TCP/IP socket的包装)。Connection可以产生一个或多个Session。跟ConnectionFactory一样,Connection也有两种类型:QueueConnection和TopicConnection。
//创建QueueConnection
QueueConnectionqueueConnection= factory.createQueueConnection();
Session
Session是我们操作消息的接口。可以通过session创建生产者、消费者、消息等。Session提供了事务的功能。当我们需要使用session发送/接收多个消息时,可以将这些发送/接收动作放到一个事务中。同样,也分QueueSession和TopicSession。
//创建QueueSession
QueueSessionqueueSession =queueConnection.createQueueSession(false,QueueSession.AUTO_ACKNOWLEDGE);
Destination
Destination的意思是消息生产者的消息发送目标或者说消息消费者的消息来源。对于消息生产者来说,它的Destination是某个队列(Queue)或某个主题(Topic);对于消息消费者来说,它的Destination也是某个队列或主题(即消息来源)。所以,Destination实际上就是两种类型的对象:Queue、Topic。
可以通过JNDI来查找Destination。
//获取Destination对象
Queuequeue = (Queue)context.lookup("queue/myqueue");
消息生产者
消息生产者由Session创建,并用于将消息发送到Destination。同样,消息生产者分两种类型:QueueSender和TopicPublisher。可以调用消息生产者的方法(send或publish方法)发送消息!
//创建文本对象
TextMessagetextMessage = queueSession.createTextMessage("hello world!");
//创建发送者
QueueSendersender = queueSession.createSender(queue);
//发送消息
sender.send(textMessage);
观察者模式又叫发布订阅模式,有订阅者和发布者;发布者可以包含了多个订阅者订阅的事件,一旦发布者执行,会执行所有的订阅者订阅的事件。我觉得这么讲还是很迷糊。其实就是说“发布者”是一段上层代码,他知道他所需要执行的过程中会发生一些事情,而这些事情具体逻辑自己又不知道,就算知道所有的逻辑,要用条件分支判断执行,这总归的是不好的,所以才有了这个模式。这是一个非常棒的模式。他使得发布者的代码保持不变。而订阅者的事件可以散步在他们自己的代码中。
我们实际应用中最常见的就是页面中的按钮点击事件。当我们双击webform中的按钮后会自动生成一个btn_OnClick的方法,然后在里面编写一些逻辑,同时也生成了btn.Click+= new EventHandler(btn_OnClick)代码(只是2.0之后这个代码就被隐藏起来了),这就是给按钮btn(订阅者)订阅了一个事件。这些逻辑理当属于按钮所在的页面,而不是需要执行这个方法的代码中。
当按钮点击之后,会触发页面的提交,webform框架可以获取是哪个按钮被点击过,然后执行btn.Click(),就可以执行我们具体的逻辑了。 设想如果不用这个模式,按钮的Click方法是不是要写很多switch来判断是哪个按钮,然后调用该有的逻辑。
那么假如说我们不用.NET的这套事件机制,该如何漂亮的抽象出Click的代码呢?
其实只要相当上一节的策略模式,我们只要给Click接受一个IClickEvent接口,然后button类再包含一组IClickEvent的成员,就可以遍历这些成员执行了。订阅的代码就变成了button.AddEvent(new xxClickEvent());即可。
在.net中,我们没必要用IClickEvent接口的形式,因为我们有委托这个方法代理(或者叫方法指针),他可以说是一个只具有一个方法的接口,而.net中的事件本身也是委托。只是事件形式的委托是封闭的,不可在外部直接赋值操作,只能订阅和删除订阅。
综上来看,观察者模式不过是一个处理未知方法的模式,他漂亮的把具体逻辑分散到他该属于地方。
在web前端的Javascript中,这种情况就更为普遍。比如我们用jQuery时,给一个按钮增加一个onClick方法,只需要$(“#btn”).click(function(){})即可。浏览器会知道具体的哪个按钮被点击,甚至我们随便点击页面的一个地方,都会被浏览器截获,假如我们有相应的OnClick方法,他会执行调用,并传值给我们当前鼠标的位置等。 在我们发起一个ajax请求时,会有一个参数是callback方法,在判断完XmlHttpRequest的readyState == 4后调用,每个ajax的请求完后的callback都不一致。所以说他也是观察者模式。我们说这种叫做回调模式是不是更好?所谓的“回”就是使用之前的代码,而不是当前的代码。
说了这么多,不知道表达清楚没有,我们来讲一个实际应用不依赖于框架的。
比如我们发布一篇文章,常用逻辑就是保存文章。如果哪天来了新的需求,比如说跟某某公司合作,发表完文章之后需要给用户增加一些奖励。又过了几天又来一个新的需求,所最近抓的紧,需要对文章审