适配器模式(Adapter)就是由源到目标的一个适配,通常我们定义的接口或者类里面提供了好多方法,但是定义好的接口里面的方法有时候用起来不是很符合我们的需要,这时候如果去修改源码也不是一个好方法,通常设计的时候也很少修改源码的。这样就提供了适配器这个类,用一个类来达到源和目标的匹配就可以了,当然可以实现我们想要的各种匹配。
在Spring,IO里面都有这方面的设计,最简单的BeanUtils里面的转换器就是一个很好的应用,就是当我们使用BeanUtils的时候,它一般只支持8种基本数据类型,简单的写一个例子,如果我们的VO里面的属性都是基础类型的这样就能操作了:
package com.hiccer.cms.utils; import java.lang.reflect.InvocationTargetException; import java.util.Iterator; import java.util.Map; import java.util.Set; import javax.servlet.http.HttpServletRequest; import org.apache.commons.beanutils.BeanUtils; public class RequestUtils { public static Object copyParams(Class entryClass, HttpServletRequest request) { try { Object entity = entryClass.newInstance(); Map allParams = request.getParameterMap(); Set entries = allParams.entrySet(); for (Iterator iterator = entries.iterator(); iterator.hasNext();) { Map.Entry entry = (Map.Entry) iterator.next(); String name = (String) entry.getKey(); String[] value = (String[]) entry.getValue(); if (value != null) { if (value.length == 1) { BeanUtils.copyProperty(entity, name, value[0]); } else { BeanUtils.copyProperty(entity, name, value); } } } return entity; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return null; } }
这时候如果我们想要让BeanUtils也能操作我们自己定义的VO对象里面的类型,我这里是有一个Channel对象作为Article对象的属性,就要定义如下:Converter来实现这个功能,这就是一个简单的适配器编程方式:
package com.hiccer.cms.utils; import java.util.HashSet; import java.util.Set; import org.apache.commons.beanutils.Converter; import com.hiccer.cms.backend.vo.Channel; public class ChannelsConverter implements Converter { @Override public Object convert(Class type, Object value) { String[] channelIds = null; if (value instanceof String) { channelIds = new String[] { (String) value }; } if (value instanceof String[]) { channelIds = (String[]) value; } if (channelIds != null) { Set channels = new HashSet(); for (String channelId : channelIds) { Channel c = new Channel(); c.setId(Integer.parseInt(channelId)); channels.add(c); } return channels; } return null; } }
那么我们这样写一个
适配器模式(Adapter)的简单的原理图:
这次我把UML类图也添加了备注,把图片放大了,这样方便查看和复习,同时清楚了实线空心箭头是“继承的”的意思,虚线空心箭头是“实现了”的意思,上面用的是去实现了,其实有的版本上面确切是是用实线箭头,就是关联的意思,其实表达出大致的意思就行了。
适配器模式有两种,一种是类适配器,一种是对象适配器,其宗旨都是解决源对于目标的不匹配,下面分别从简单的例子看两种适配器然后整体分析:
第一种适配器
我的电脑耳机坏了,但是我耳机是USB2.0的插口,不幸运的是我只有一个3.0的耳机了,于是我去买了一个适配器把3.0的和2.0的接到了一起,就能用了,事例代码如下:
package com.designpattern.adapter; public interface USB2 { public void need(); }
package com.designpattern.adapter; public class USB3 { public void add() { System.out.println("I'm a USB3.0 headset"); } }
package com.designpattern.adapter; public class Adapter extends USB3 implements USB2 { @Override public void need() { this.add(); } }
package com.designpattern.adapter; public class Client { public static void main(String[] args) { Adapter adapter = new Adapter(); adapter.add(); } }
I'm a USB3.0 headset
下面就简单的写了一个对象的适配器的例子:
我定义了一个类Utils简单的用List存放了三个字符串,作为我的源,但是我的Application用的时候是必须是HashMap,这样我就不能用了,于是定义的一个ListAdapter类,来解决这两个问题,具体代码如下:
package com.designpattern.adapter; import java.util.ArrayList; import java.util.List; public class Utils { @SuppressWarnings("unchecked") public static List getElements() { List list = new ArrayList(); list.add("first"); list.add("second"); list.add("third"); return list; } }
package com.designpattern.adapter; import java.util.HashMap; public class Application { @SuppressWarnings("unchecked") public static void print(HashMap map) { for (int i = 0; i < map.size(); i++) { System.out.print(map.get(i) + " "); } } }
package com.designpattern.adapter; import java.util.HashMap; import java.util.List; @SuppressWarnings( { "unchecked", "serial" }) public class ListAdapter extends HashMap { @SuppressWarnings("unchecked") private List list; @SuppressWarnings("unchecked") public ListAdapter(List list) { this.list = list; } public int size() { return list.size(); } public Object get(Object i) { return list.get((Integer.valueOf(i.toString())).intValue()); } }
package com.designpattern.adapter; public class Client { public static void main(String[] args) { System.out.println(Utils.getElements()); ListAdapter listadapter = new ListAdapter(Utils.getElements()); Application.print(listadapter); } }
[first, second, third] first second third其旨在对于仅能操作HashMap的用户一样可以使用这个只能操作List的工具包。
使用适配器模式,可以讲一个系统的接口和本来不相容的另一个系统的类联系起来,从而使得这两个类能够一起工作,强调了对接口的转换。
这里也简单的说一下默认适配器模式:
这个么模式是大多数API接口使用的方式,以方便用户的使用,简单的定义一个接口里面有好多方法,为了不不得不的实现那么多方法,紧接着定义一个抽象类来实现这个接口,这样就可以继承抽象类来重写自己想要的方法而不写过多的没有意义的方法了。简单的例子如下:
package com.designpattern.adapter; public interface Phone { public void sendMessage(); public void surfInternet(); public void receiveCall(); public void AsAlarm(); }
package com.designpattern.adapter; public abstract class ChinaMobile implements Phone { @Override public void AsAlarm() { // TODO Auto-generated method stub } @Override public void receiveCall() { // TODO Auto-generated method stub } @Override public void sendMessage() { // TODO Auto-generated method stub } @Override public void surfInternet() { // TODO Auto-generated method stub } }
package com.designpattern.adapter; public class MyPhone extends ChinaMobile { @Override public void AsAlarm() { System.out.println("I just use it as a alarm!"); } }