`

Adapter - 适配器模式

    博客分类:
  • Java
OOP 
阅读更多

假设模型:媒体播放器,既能播放音频文件AudioMedia,又能播放视频文件VideoMedia 。其中音频文件又分MP3格式和WMV格式;视频文件又分MPEG格式和RM格式。

经过功能分析,不管哪种类型的文件都要有一个播放的方法,也就是Play() ,原型结构如图:

Adapter_Initial

当各个类之间继承/实现关系理清楚后,我们现在可以实现一个播放器类MediaPlayer,它可以播放任何一种格式(MP3WAVRMMPEG)的文件。

在此我们要注意一点:在调用类对象的属性和方法时,尽量避免将具体类对象作为传递参数,而应传递其抽象对象,更好地是传递接口,将实际的调用和具体对象完全剥离开,这样可以提高代码的灵活性。(使用接口有什么好处?那就是你的主程序可以在没有具体业务类的时候,同样可以编译通过。因此,即使你增加了新的业务,你的主程序是不用改动的。)

 

在本文中体现在MediaPlayer代码中,如下所示:

package adapter_media;

public class MediaPlayer {

         public void Play(IMedia media){

                   media.Play();

         }

}

相关代码如下:

package adapter_media;

public interface IMedia {
	public void Play();
}

 

package adapter_media;

public abstract class AudioMedia implements IMedia{
	public abstract void Play();
}

 

package adapter_media;

public abstract class VideoMedia implements IMedia {
	public abstract void Play();
}

 

package adapter_media;

public class MP3 extends AudioMedia {

	@Override
	public void Play() {
		System.out.println("Play AudioMedia with the type of MP3");
	}
}

 

package adapter_media;

public class WAV extends AudioMedia {

	@Override
	public void Play() {
		System.out.println("Play AudioMedia with the type of WAV");
	}
}

 

package adapter_media;

public class MPEG extends VideoMedia {

	@Override
	public void Play() {
		System.out.println("Play VideoMedia with the type of MPEG");
	}
}

 

package adapter_media;

public class RM extends VideoMedia {

	@Override
	public void Play() {
		System.out.println("Play VideoMedia with the type of RM");
	}
}

 

 

package adapter_media;
/**
 *************************************
  * @Title   MediaPlayer.java
  * @Author  张作强
  * @Date    2010-8-15
  * @Comment 在调用类对象的属性和方法时,尽量避免将具体类对象作为传递参数,
  * 		   而应传递其抽象对象,更好地是传递接口,
  *          将实际的调用和具体对象完全剥离开,这样可以提高代码的灵活性。
 *************************************
 */
public class MediaPlayer {
	public void Play(IMedia media){
		media.Play();
	}
}

 

引入Adapter模式:

原来的RMMPEG类继承了VideoMedia抽象类,而VideoMedia类又实现了IMedia接口,该接口仅仅提供了Play()方法。现在我们希望为RMMPEG提供与AudioMedia不同的属性和方法。例如,对于视频媒体而言,应该有一个调整画面大小的方法,如Resize()。而这个方法是IMedia接口所不具备的。

 

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

 

Adapter模式分为两种:类的Adapter模式、对象的Adapter模式。

 

下面试图根据本例对两种方式进行说明及实现。在实现Adapter模式之前,有必要看看原来的类结构:

  

一、类的Adapter模式

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

 

Adapter_Class_Model

 

图中的类RMAdapterMPEGAdapter就是通过Adapter模式获得的对象,它在保留了原有行为的同时,又拥有了IVideoScreen的功能。

相关代码如下:

package adapter_media;

public interface IVideoScreen {
	public void Resize();
}

 

package adapter_media;

public class MPEGAdapter extends MPEG implements IVideoScreen {

	public void Resize() {
		System.out.println("Play VideoMedia with the size of MPEG");
	}
	
	@Override
	public void Play() {
		super.Play();
	}
}

 

package adapter_media;

public class RMAdapter extends RM implements IVideoScreen {

	public void Resize() {
		System.out.println("Play VideoMedia with the size of RM");
	}
	
	@Override
	public void Play() {
		super.Play();
	}
}

 

也许很多人已经注意到了,在使用这种方式建立Adapter时,存在一个局限,就是我们必须为每一个要包裹(Wrapping)的类,建立一个相应的Adapter类。如上所述的RM对应RMAdapterMPEG对应MPEGAdapter。必须如此,为什么呢?虽然RMMPEG继承了同一个抽象类VideoMedia,但其Play()方法,可能是不相同的。此时,相对应的Adpater类只有直接继承该具体类,方才可以保留其原来的Play()方法的行为本质。

 

分享到:
评论
1 楼 csdn_zuoqiang 2010-08-15  
OOP中很重要的思想是,尽量使用聚合而非继承。让我们换一种思路来考察Adapter模式。

二、对象的Adapter模式

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


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

对于对象的Adapter模式(在VideoMedia)中,按照传递对象的方式又可以分为:通过构造函数传递被包裹对象的方式、通过方法属性方式传递被包裹对象。

相关代码如下:
package adapter_media;

public class VideoAdapter implements IVideoScreen {

	private VideoMedia _video;
	
	//无参构造函数
	public VideoAdapter() {
	}
	
	//构造函数方式传递对象
	public VideoAdapter(VideoMedia videoMedia) {
		this._video = videoMedia;
	}
	//方法属性方式传递对象,同时要求此类必须有一个无参构造函数
	public void setVideoMedia(VideoMedia video) {
		this._video = video;
	}

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

	public void Resize() {
		System.out.println("Play VideoMedia with the size of " + _video.getClass().getSimpleName());
	}

}


测试类:

package adapter_media;
/**
 *************************************
  * @Title   TestMedia.java
  * @Author  张作强
  * @Date    2010-8-15
  * @Comment 原有类型和经过Adapter扩展后的类型
 *************************************
 */
public class TestMedia {
	public static void main(String[] args) {
		IMedia mp3 = new MP3();
		IMedia wav = new WAV();
		IMedia rm = new RM();
		IMedia mpeg = new MPEG();
		
		MediaPlayer media = new MediaPlayer();
		System.out.println("================ type =================");
		media.Play(mp3);
		media.Play(wav);
		media.Play(rm);
		media.Play(mpeg);
		
		System.out.println("============= 类的Adapter模式 =============");
		IVideoScreen rmAdapter = new RMAdapter();
		rmAdapter.Resize();
		((RMAdapter) rmAdapter).Play();
		
		IVideoScreen mpegAdapter = new MPEGAdapter();
		mpegAdapter.Resize();
		((MPEGAdapter) mpegAdapter).Play();
		
		System.out.println("=============== 对象的Adapter模式(构造函数方式传递对象) ===============");
		VideoAdapter rmAdapter2 = new VideoAdapter(new RM());
		rmAdapter2.Play();
		rmAdapter2.Resize();
		
		VideoAdapter mpegAdapter2 = new VideoAdapter(new MPEG());
		mpegAdapter2.Play();
		mpegAdapter2.Resize();
		
		System.out.println("=============== 对象的Adapter模式(方法属性方式传递对象) ===============");
		VideoAdapter rmAdapter3 = new VideoAdapter();
		rmAdapter3.setVideoMedia(new RM());
		rmAdapter3.Play();
		rmAdapter3.Resize();
		
		VideoAdapter mpegAdapter3 = new VideoAdapter();
		mpegAdapter3.setVideoMedia(new MPEG());
		mpegAdapter3.Play();
		mpegAdapter3.Resize();
	}
}


结果:

================ type =================
Play AudioMedia with the type of MP3
Play AudioMedia with the type of WAV
Play VideoMedia with the type of RM
Play VideoMedia with the type of MPEG
============= 类的Adapter模式 =============
Play VideoMedia with the size of RM
Play VideoMedia with the type of RM
Play VideoMedia with the size of MPEG
Play VideoMedia with the type of MPEG
=============== 对象的Adapter模式(构造函数方式传递对象) ===============
Play VideoMedia with the type of RM
Play VideoMedia with the size of RM
Play VideoMedia with the type of MPEG
Play VideoMedia with the size of MPEG
=============== 对象的Adapter模式(方法属性方式传递对象) ===============
Play VideoMedia with the type of RM
Play VideoMedia with the size of RM
Play VideoMedia with the type of MPEG
Play VideoMedia with the size of MPEG


比较两种实现方式的类图,可以得出两个结论:

1、 对象的Adapter模式,减少了对象的个数;
2、 耦合度更加松散;

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

参考地址:http://www.cnblogs.com/wayfarer/articles/70453.html

相关推荐

Global site tag (gtag.js) - Google Analytics