转载1

原创 2007年10月02日 23:18:00

在拙文《<让僵冷的翅膀飞起来>系列之一——从实例谈OOP、工厂模式和重构》中,冰汽水提出了一个问题,“如果我想让RM, MPEG类具有自己的一些特定属性的话怎么做呢?”原来的RM和MPEG类继承了VideoMedia抽象类,而VideoMedia类又实现了IMedia接口,该接口仅仅提供了Play()方法。冰汽水的意思是希望为RM,MPEG提供与AudioMedia不同的属性和方法。例如,对于视频媒体而言,应该有一个调整画面大小的方法,如Resize()。而这个方法是IMedia接口所不具备的。

那么怎样为RM,MPEG类提供IMedia接口所不具备的Resize()方法呢?非常自然地,通过这个问题我们就引出Adapter模式的命题了。首先,要假设一个情况,就是原文的所有代码,我们是无法改变的,这包括暴露的接口,类与接口的关系等等,都无法通过编码的方式实现新的目标。只有这样,引入Adapter模式才有意义。

熟悉Adapter模式的人都知道,Adapter模式分为两种:类的Adapter模式、对象的Adapter模式。下面我试图根据本例对两种方式进行说明及实现。在实现Adapter模式之前,有必要看看原来的类结构:


 

左边橙色的类为音频媒体类型,右边蓝色的类为视频媒体类型。所有的这些类型,包括类和接口都是无法改变的。现在我们的目的就是要让RM、MPEG具有Resize()方法。那么首先定义一个接口IVideoMedia,该接口具有Resize()方法。 


下面我们就根据Adapter模式来实现需求。

一、 类的Adapter模式

既然要让RM、MPEG具有Resize()方法,最好的办法就是让它们直接实现IVedioScreen接口。然而受到条件的限制,这两个类类型是不可修改的。唯一可行的办法就是为相应的类新引入一个类类型,这就是Adapter模式中所谓的Adapter类了。它好比是一个转接头,通过它去实现IVedioScreen接口,同时又令其继承原有的RM或MPEG类,以保留原有的行为。类图如下:

图中的类RMAdapter和MPEGAdapter就是通过Adapter模式获得的对象,它在保留了原有行为的同时,又拥有了IVedioScreen的功能。
代码如下:
public interface IVedioScreen
{
 void Resize();
}

public class RMAdapter:RM,IVedioScreen
{
 public void Resize()
 {
  MessageBox.Show("Change the RM screen's size.");
 }
}

public class MPEGAdapter:MPEG,IVedioScreen
{
 public void Resize()
 {
  MessageBox.Show("Change the MPEG screen's size.");
 }
}
也许很多人已经注意到了,在使用这种方式建立Adapter时,存在一个局限,就是我们必须为每一个要包裹(Wrapping)的类,建立一个相应的Adapter类。如上所述的RM对应RMAdapter,MPEG对应MPEGAdapter。必须如此,为什么呢?虽然RM和MPEG继承了同一个抽象类VedioMedia,但其Play()方法,可能是不相同的。此时,相对应的Adpater类只有直接继承该具体类,方才可以保留其原来的Play()方法的行为本质。

OOP中很重要的思想是,尽量使用聚合而非继承。让我们换一种思路来考察Adapter模式。

二、 对象的Adapter模式

对象的Adapter模式,与类的Adapter模式,最大的区别就是不采用继承的方式,而是将要包裹的对象,以聚合的方式放到Adapter中,然后用委托的方式调用其对象的方法,实现类图如下:
 


比较两种实现方式的类图,可以得出两个结论:
1、 对象的Adapter模式,减少了对象的个数;
2、 耦合度更加松散;

代码如下:
public interface IVedioScreen
{
 void Resize();
}

public class VedioAdapter:IVedioScreen
{
 private vedioMedia _vedio;
 
 public VedioAdapter(vedioMedia vedio)
 {
  _vedio = vedio;
 }

 public void Play()
 {
  _vedio.Play();  
 }

 public void Resize()
{
  if (_vedio is RM)
   MessageBox.Show("Change the RM screen's size.");
  else
   MessageBox.Show("Change the MPEG screen's size."); 
}
}

以这种方式形成的VedioAdapter,由于没有和RM、MPEG直接发生关系,并通过在构造函数传递参数的方式,等待客户端使用Adapter时,才将具体的VedioMedia对象传递给Adapter,显得耦合度更加松散,更加灵活。

我们来看客户端调用时,两者的区别:
1、 类的Adapter模式
public class Client
{
 public static void Main()
 {
  RMAdapter rmAdapter = new RMAdapter();
  MPEGAdapter mpegAdapter = new MPEGAdapter();
  
  rmAdapter.Play();
  rmAdapter.Resize();
  mpegAdapter.Play();
  mpegAdapter.Resize();
 }
}
2、 对象的Adapter模式
public class Client
{
 public static void Main()
 {
  VedioAdapter rmAdapter = new VedioAdapter(new RM());
  VedioAdapter mpegAdapter = new VedioAdapter(new MPEG());
  
  rmAdapter.Play();
  rmAdapter.Resize();
  mpegAdapter.Play();
  mpegAdapter.Resize();
 }
}

其实,对于对象的Adapter模式,还可以做一些改进,就是用属性或方法来取代构造函数传递被包裹对象的方式。代码修改如下:
public class VedioAdapter:IVedioScreen
{
 private vedioMedia _vedio;
 
 public VedioMedia Vedio
 {
  set {_vedio = value;}
 }
 ……
}
这样,上面的客户端调用就更简单了:
public class Client
{
 public static void Main()
 {
  VedioAdapter adapter = new VedioAdapter();
  adapter.Vedio = new RM();
  adapter.Play();
  adapter.Resize();

  adapter.Vedio = new MPEG();
  adapter.Play();
  adapter.Resize();
 }
}

通过运用Adapter模式,扩展了新的接口,而原有的类型并不需要做任何改变,这就是Adapter模式的实质,也是为什么取名为Adapter的原因之所在了。同时,我们要注意的是,在运用Adapter模式时,必须审时度势,根据具体的情况,抉择最优的方式,或者采用类的Adapter模式,或者采用对象的Adapter模式。决定权在与你,菜单给你送上来了,看看自己的腰包,想想点什么样的菜吧。
 

一键转载CSDN博客

前言  将喜欢的博客进行转载是对博客的收藏,也能够在以后不断的提高自己,而CSDN并没有一键转载功能,现在提供一种方法方便大家进行转载。实现   我们首先打开要转载的博客,然后鼠标右键就会出现下面的菜...
  • kevinelstri
  • kevinelstri
  • 2017年03月09日 13:38
  • 569

如何转载CSDN博客

小弟我刚大学毕业,正在找工作,在家等面试的时候就想着看点什么。面试的时候,什么都问你。如果有问题把你问住了,那基本就完了。**,out。 刚看了几篇郭霖大神的几篇博客,就像转载下来,以后自己留着看。结...
  • ppiglets
  • ppiglets
  • 2016年06月27日 15:40
  • 970

csdn如何转载博客

1、找到要转载的文章,用chrome浏览器打开,右键选择审查元素 2、在chrome中下方的框里找到对应的内容,html脚本中找到对应的节点,选中节点,网页上被选中内容会被高亮显示,然后右键菜单选中...
  • sddxqlrjxr
  • sddxqlrjxr
  • 2015年11月15日 19:12
  • 2991

CSDN如何转载他人文章

利用Markdown可以解析html的特点,我们可以很方便的转载他人的文章。
  • Michael753951
  • Michael753951
  • 2017年04月21日 04:01
  • 519

如何从CSDN上转载文章

1.看到自己喜欢的文章,鼠标右键 2.选择装载文章的部分: 注:为什么选择HTML里面,是因为我们读者要读,文章就需要写出来,所以选这Input的意识 3.登录自己的CSDN博客: 4.尊重原...
  • csdnZCjava
  • csdnZCjava
  • 2017年07月07日 22:20
  • 298

1.如何转载CSDN其他人的博客

对于喜欢逛CSDN的人来说,看别人的博客确实能够对自己有不小的提高,有时候看到特别好的博客想转载下载,但是不能一个字一个字的敲了,这时候我们就想快速转载别人的博客,把别人的博客移到自己的空间里面,当然...
  • weixin_39914245
  • weixin_39914245
  • 2018年01月07日 21:24
  • 40

CSDN的博文如何转载

最近发现CSDN的博客,是不能够转载的(或者说根本就没有转载连接;但是可以分享连接的,也只能分享微qq,人人,sina微博等非csdn博文)—— 最后在csdn官网上查看到,原来csdn是本着尊重原创...
  • u010700335
  • u010700335
  • 2014年05月28日 17:55
  • 2056

CSDN博客被莫名转载

今天猛然发现自己的一些博客(写的内容比较多的一两篇)被好几个网站莫名地拿去它们网站发布了,基本都是那些满屏弹窗广告的那种烂网站,有些起码会在第一句话说文章是引用自我的博客,但有些直接就只是在标题下很小...
  • Lv_Victor
  • Lv_Victor
  • 2016年08月16日 22:34
  • 368

如何转载别人csdn博客

对于喜欢逛CSDN的人来说,看别人的博客确实能够对自己有不小的提高,有时候看到特别好的博客想转载下载,但是不能一个字一个字的敲了,这时候我们就想快速转载别人的博客,把别人的博客移到自己的空间里面,当然...
  • BrotherJing8023
  • BrotherJing8023
  • 2016年12月15日 01:04
  • 206

本文为原创文章,转载请标明出处

一、optionsMenu:选项菜单 1.创建:重写oncreateOptionsMenu();​ 方式一:代码动态添加:   添加项:menu.add();   添加图标:m...
  • lwh13794625320
  • lwh13794625320
  • 2015年12月06日 15:32
  • 250
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:转载1
举报原因:
原因补充:

(最多只允许输入30个字)