王大虎

王大虎:33.1.2 桥梁模式实现邮件发送

王大虎

桥梁模式关注的是抽象和实现的分离,它是结构型模式,结构型模式研究的是如何建立一个软件架构,下面我们就来看看桥梁模式是如何构件一套发送邮件的架构的,如图33-3所示。

类图中我们增加了SendMail和Postfix两个邮件服务器来实现类,在邮件模板中允许增加发送者标记,其他与策略模式都相同。我们在这里已经完成了一个独立的架构,邮件有了,发送邮件的服务器也具备了,是一个完整的邮件发送程序。需要读者注意的是,SendMail类不是一个动词行为(发送邮件),它指的是一款开源邮件服务器产品,一般*nix系统的默认邮件服务器就是SendMail;Postfix也是一款开源的邮件服务器产品,其性能、稳定性都在逐步赶超SendMail。

王大虎
王大虎

图33-3 桥梁模式实现邮件发送的类图

我们来看代码实现,邮件模板仅仅增加了一个add方法,如代码清单33-6所示。

代码清单33-6 邮件模板

 

王大虎

 

public abstract class MailTemplate { /* *该部分代码不变,请参考代码清单33-1 */ //允许增加邮件发送标志 public void add(String sendInfo){ context = sendInfo + context; } }

 

* * *

 

文本邮件、超文本邮件都没有任何改变,如代码清单33-2、33-3所示,这里不再赘述。

我们来看邮件服务器,也就是桥梁模式的抽象化角色,如代码清单33-7所示。

代码清单33-7 邮件服务器

 

王大虎

 

public abstract class MailServer { //发送的是哪封邮件 protected final MailTemplate m; public MailServer(MailTemplate _m){ this.m = _m; } //发送邮件 public void sendMail(){ System.out.println(“====正在发送的邮件信息====”); //发件人 System.out.println(“发件人:” + m.getFrom()); //收件人 System.out.println(“收件人:” + m.getTo()); //标题 System.out.println(“邮件标题:” + m.getSubject() ); //邮件内容 System.out.println(“邮件内容:” + m.getContext()); } }

 

* * *

 

该类相对于策略模式的环境角色有两个改变:

·修改为抽象类。为什么要修改成抽象类?因为我们在设计一个架构,邮件服务器是一个具体的、可实例化的对象吗?“给我一台邮件服务器”能实现吗?不能,只能说“给我一台Postfix邮件服务器”,这才能实现,必须有一个明确的可指向对象。

·变量m修改为Protected访问权限,方便子类调用。

我们再来看看Postfix邮件服务器的实现,如代码清单33-8所示。

代码清单33-8 Postfix邮件服务器

 

王大虎

 

public class Postfix extends MailServer { public Postfix(MailTemplate _m) { super(_m); } //修正邮件发送程序 public void sendMail(){ //增加邮件服务器信息 String context =”Received: from XXXX (unknown [xxx.xxx.xxx.xxx]) by aaa.aaa.com (Postfix) with ESMTP id 8DBCD172B8\n” ; super.m.add(context); super.sendMail(); } }

 

为什么要覆写sendMail程序呢?这是因为每个邮件服务器在发送邮件时都会在邮件内容上留下自己的标志,一是广告作用,二是为了互联网上统计需要,三是方便同质软件的共振。我们再来看SendMail邮件服务器的实现,如代码清单33-9所示。

代码清单33-9 SendMail邮件服务器

public class SendMail extends MailServer { //传递一封邮件 public SendMail(MailTemplate _m) { super(_m); } //修正邮件发送程序 @Override public void sendMail(){ //增加邮件服务器信息 super.m.add(“Received: (sendmail); 7 Nov 2009 04:14:44 +0100”); super.sendMail(); } }

 

邮件和邮件服务器都有了,我们来看怎么发送邮件,如代码清单33-10所示。

代码清单33-10 场景类

 

王大虎

 

public class Client { public static void main(String[] args) { //创建一封TEXT格式的邮件 MailTemplate m = new HtmlMail(“a@a.com”,”b@b.com”,”外星人攻击地球了”,” 结局是外星人被地球人打败了!”); //使用Postfix发送邮件 MailServer mail = new Postfix(m); //发送邮件 mail.sendMail(); } }

 

运行结果如下所示:

====正在发送的邮件信息==== 发件人:a@a.com 收件人:b@b.com 邮件标题:外星人攻击地球了 邮件内容: Content-Type: multipart/mixed;charset=GB2312 Received: from XXXX (unknown [xxx.xxx.xxx.xxx]) by aaa.aaa.com (Postfix) with ESMTP id 8DBCD172B8 结局是外星人被地球人打败了! 邮件格式为:超文本格式

 

当然了,还有其他三种发送邮件的方式:Postfix发送文本邮件以及SendMail发送文本邮件和超文本邮件。修改量很小,读者可以自行修改实现,体会一下桥梁模式的特点。

33.1.3 最佳实践

策略模式和桥梁模式是如此相似,我们只能从它们的意图上来分析。策略模式是一个行为模式,旨在封装一系列的行为,在例子中我们认为把邮件的必要信息(发件人、收件人、标题、内容)封装成一个对象就是一个行为,封装的格式(算法)不同,行为也就不同。而桥梁模式则是解决在不破坏封装的情况下如何抽取出它的抽象部分和实现部分,它的前提是不破坏封装,让抽象部分和实现部分都可以独立地变化,在例子中,我们的邮件服务器和邮件模板是不是都可以独立地变化?不管是邮件服务器还是邮件模板,只要继承了抽象类就可以继续扩展,它的主旨是建立一个不破坏封装性的可扩展架构。

简单来说,策略模式是使用继承和多态建立一套可以X切换算法的模式,桥梁模式是在不破坏封装的前提下解决抽象和实现都可以独立扩展的模式。桥梁模式必然有两个“桥墩”——抽象化角色和实现化角色,只要桥墩搭建好,桥就有了,而策略模式只有一个抽象角色,可以没有实现,也可以有很多实现。

还是很难区分,是吧?多想想两者的意图,就可以理解为什么要建立两个相似的模式了。我们在做系统设计时,可以不考虑到底使用的是策略模式还是桥梁模式,只要好用,能够解决问题就成,“不管黑猫白猫,X老鼠的就是好猫”。

 

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注