適配器模式(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
~~~
這樣Adapter繼承了USB3這個類,java中的單繼承不允許它在繼承別的類了,所以就不能再實現別的目標類的需求了,所以這樣就叫做類適配器,量身為一個類做的一個適配器。其實這種類的適配模式用于單一源的適配,由于它的源的單一話,代碼實現不用寫選擇邏輯,很清晰;而對象的適配模式則可用于多源的適配,彌補了類適配模式的不足,使得原本用類適配模式需要寫很多適配器的情況不復存在,弱點是,由于源的數目可以較多,所以具體的實現條件選擇分支比較多,不太清晰。同時在對象的適配器里面,通常是把源或者是目標聚合到適配器里面,就是和適配器擁有共同的生命周期,放在適配器的構造器里面,個人覺得在源放在放在適配器里面比較好,可能多數的時候是在適配器面接收目標的要求,然后通過對要求的分析,解析在用源的實例去調用源的方法的。
下面就簡單的寫了一個對象的適配器的例子:
我定義了一個類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!");
}
}
~~~
- 前言
- 前言(目錄、源碼、資料)
- (一)簡單工廠模式(SimpleFatory)
- (二)工廠方法模式(FactoryMethod)
- (三)抽象工廠模式(AbstractFactory)
- (四)創建者模式(Builder)
- (五)原型模式(Prototype)
- (六)單例模式(Singleton)
- (七)外觀模式(Facade)
- (八)適配器模式(Adapter)
- (九)代理模式(Proxy)
- (十)裝飾模式(Decorator)
- (十一)橋模式(birdge)
- (十二)組合模式(Composite)
- (十三)享元模式(Flyweight)
- (十四)模板方法模式(Template)
- (十五)觀察者模式(Observer)
- (十六)狀態模式(State)