一、概述
一般问题:在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。
核心方案:将一个类的接口转换成客户希望的另外一个接口,从而使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
设计意图:适配器一般不在最初设计之中,而是某些接口无法满足新需求,又不能或不希望修改其现有结构,所以才不得不做适配。
适配器的根本任务是将“源”接口包装成“目标”接口,那么首先适配器依然依赖“源”接口的大部分方法;然后,将需要适配的方法单独提炼成一个“目标”接口,适配器实现“目标”接口。
适配器模式类图如下:
其中,
- Adaptee是被适配类
- Adaptee可以不止一个,实现多源适配
- Adaptee只有一个时,可以改为继承,避免Adapter中重写过多的重名方法
代码示例:
public class Adapter implements Target { Adaptee adaptee; public Adapter(Adaptee adaptee) { this.adaptee = adaptee; } public void operation1() { adaptee.operation1(); } public void operation2() { adaptee.operation2(); } //new add method public void operation3() { } }
二、运用实例
一种设计模式提供了一类问题的核心解决方案,实际开发中并非都是理想的标准模式,Android系统中有很多借鉴了适配器模式的设计架构,最常见的是listview的adapter将数据与item view绑定。除此之外还有一些适用场景值得借鉴。
(1)适配回调接口
当回调接口中有太多方法,我们又不会每次都用到时,可以用适配器来简化调用,如AnimatorListenerAdapter
/** * This adapter class provides empty implementations of the methods from {@link android.animation.Animator.AnimatorListener}. * Any custom listener that cares only about a subset of the methods of this listener can * simply subclass this adapter class instead of implementing the interface directly. */ public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener, Animator.AnimatorPauseListener { @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationPause(Animator animation) { } @Override public void onAnimationResume(Animator animation) { } }
这样为Animator设置AnimatorListener时,不必总是实现所有AnimatorListener接口。
(2)合并多源接口
一些相似功能类可以封装在一个适配器类,提供统一的目标接口,如Android System中的PackageManagerAdapter
// Adapter that wraps calls to PackageManager or IPackageManager for {@link TileLifecycleManager}. // TODO: This is very much an intermediate step to allow for PackageManager mocking and should be // abstracted into something more useful for other tests in systemui. public class PackageManagerAdapter { private static final String TAG = "PackageManagerAdapter"; private PackageManager mPackageManager; private IPackageManager mIPackageManager; // Uses the PackageManager for the provided context. // When necessary, uses the IPackagemanger in AppGlobals. public PackageManagerAdapter(Context context) { mPackageManager = context.getPackageManager(); mIPackageManager = AppGlobals.getPackageManager(); } public ServiceInfo getServiceInfo(ComponentName className, int flags, int userId) throws RemoteException { return mIPackageManager.getServiceInfo(className, flags, userId); } public ServiceInfo getServiceInfo(ComponentName component, int flags) throws PackageManager.NameNotFoundException { return mPackageManager.getServiceInfo(component, flags); } public PackageInfo getPackageInfoAsUser(String packageName, int flags, @UserIdInt int userId) throws PackageManager.NameNotFoundException { return mPackageManager.getPackageInfoAsUser(packageName, flags, userId); } }
将mPackageManager和mIPackageManager两个Adaptee封装在同一个适配器中,外部不必关心内部谁来执行。
三、总结
优点:
- 可以让任何两个没有关联的类一起运行
- 提高了类的复用,解决了现存类和复用环境要求不一致的问题
- 将目标类和适配者类解耦,通过引入一个适配器类重用现有的适配者类,而无需修改原有代码
缺点:过多地使用适配器,会让系统非常零乱,不易整体进行把握
总结:适配器模式是一种结构型设计模式,避免了对被适配类原有逻辑的破坏,同时满足了新接口需求,是一种经济灵活的设计模式。用一句俗语概括就是
“新瓶装旧酒,再添点水”。