介面卡模式的介紹:
介面卡模式(Adapter Pattern)是作為兩個不相容介面之間的橋樑,這種型別的設計模式屬於結構型模式。一些書籍也稱介面卡模式為預設介面卡模式(Default Adapter Pattern)。介面卡模式主要分為三類:類介面卡模式、物件介面卡模式、介面介面卡模式。以生活中手機充電為例來講解介面卡模式,手機本身並不能直接用220V交流電,需要將220V的交流電轉換為5V的直流電,在這個過程中,充電器本身相當於Adapter(介面卡),220V交流電相當於Adaptee (適配者),5V直流電則是我們的Target(目標)。
介面卡模式的優點:
介面卡模式的缺點:
介面卡模式的應用:
介面卡模式的角色:
Voltage220V
public class Voltage220V {
public int output220V() {
System.out.println("Voltage220V output220V ...");
return 220;
}
}
Voltage5V
public interface Voltage5V {
//定義一個標準,充電器來實現
public int output5V();
}
VoltageAdapter
public class VoltageAdapter extends Voltage220V implements Voltage5V {
@Override
public int output5V() {
//獲取220V交流電
int output220V = output220V();
//轉換為5V直流電
int output5V = output220V / 44;
System.out.println("VoltageAdapter output5V ...");
return output5V;
}
}
Phone
public class Phone {
//充電
public void charging(Voltage5V voltage5V) {
if (voltage5V.output5V() == 5) {
System.out.println("電壓剛好5V, 可以充電");
} else if (voltage5V.output5V() > 5) {
System.out.println("電壓大於5V, 不能充電");
}
}
}
PhoneTest
public class PhoneTest {
public static void main(String[] args) {
//建立一部手機
Phone phone = new Phone();
//用充電器充電
phone.charging(new VoltageAdapter());
}
}
Voltage220V output220V ...
VoltageAdapter output5V ...
電壓剛好5V, 可以充電
Voltage220V
類的方法在介面卡VoltageAdapter
類中都會暴露出來,也增加了使用的成本。但是由於其繼承了適配者Voltage220V
類,所以它可以根據需求重寫該類的方法,使得介面卡VoltageAdapter
類的靈活性增強了。基本思路和類介面卡模式相同,只是將VoltageAdapter
類作修改,不是繼承Voltage220V
類,而是持有Voltage220V
類的範例,以解決相容性的問題。
根據「合成複用原則」,在系統中儘量使用關聯關係(聚合)來替代繼承關係。
Voltage220V
public class Voltage220V {
public int output220V() {
System.out.println("Voltage220V output220V ...");
return 220;
}
}
Voltage5V
public interface Voltage5V {
//定義一個標準,充電器來實現
public int output5V();
}
VoltageAdapter
public class VoltageAdapter implements Voltage5V {
private Voltage220V voltage220V;
public VoltageAdapter(Voltage220V voltage220V) {
this.voltage220V = voltage220V;
}
@Override
public int output5V() {
//獲取220V交流電
int output220V = voltage220V.output220V();
//轉換為5V直流電
int output5V = output220V / 44;
System.out.println("VoltageAdapter output5V ...");
return output5V;
}
}
Phone
public class Phone {
//充電
public void charging(Voltage5V voltage5V) {
if (voltage5V.output5V() == 5) {
System.out.println("電壓剛好5V, 可以充電");
} else if (voltage5V.output5V() > 5) {
System.out.println("電壓大於5V, 不能充電");
}
}
}
PhoneTest
public class PhoneTest {
public static void main(String[] args) {
//建立一部手機
Phone phone = new Phone();
//用充電器充電(傳入220V交流電,傳出5V直流電)
phone.charging(new VoltageAdapter(new Voltage220V()));
}
}
Voltage220V output220V ...
VoltageAdapter output5V ...
電壓剛好5V, 可以充電
VoltageAdapter
必須繼承Voltage220V
的侷限性問題,也不再強制要求Voltage5V
必須是介面。使用成本更低,更靈活。因此,物件介面卡模式是介面卡模式常用的一種。介面介面卡介紹:當不需要全部實現介面提供的方法時,可先設計一個抽象類實現介面,併為該介面中每個方法提供 一個預設實現(空方法),那麼該抽象類的子類可有選擇地覆蓋父類別的某些方法來實現需求。
Animation
public interface Animation {
public void fadeIn();
public void fadeInUp();
public void fadeInDown();
public void fadeInLeft();
public void fadeInRight();
public void fadeOut();
public void fadeOutUp();
public void fadeOutDown();
public void fadeOutLeft();
public void fadeOutRight();
}
AnimationAdapter
public class AnimationAdapter implements Animation {
@Override
public void fadeIn() {}
@Override
public void fadeInUp() {}
@Override
public void fadeInDown() {}
@Override
public void fadeInLeft() {}
@Override
public void fadeInRight() {}
@Override
public void fadeOut() {}
@Override
public void fadeOutUp() {}
@Override
public void fadeOutDown() {}
@Override
public void fadeOutLeft() {}
@Override
public void fadeOutRight() {}
}
JFrameAnimation
public class JFrameAnimation extends AnimationAdapter {
@Override
public void fadeIn() {
System.out.println("JFrameAnimation fadeIn ...");
}
@Override
public void fadeOut() {
System.out.println("JFrameAnimation fadeOut ...");
}
}
Client
public class Client {
public static void main(String[] args) {
Animation animation = new JFrameAnimation();
animation.fadeIn();
animation.fadeOut();
}
}
JFrameAnimation fadeIn ...
JFrameAnimation fadeOut ...
javax.servlet.http.HttpServlet
繼承javax.servlet.GenericServlet
空實現於javax.servlet.Servlet
最佳實踐:介面卡模式在 SpringMVC 框架應用
由於Controller
的型別不同,有多重實現方式,那麼呼叫方式就不是確定的,如果需要直接呼叫Controller
方法,需要呼叫的時候就得不斷是使用if else
來進行判斷是哪一種子類然後執行。那麼如果後面要擴充套件Controller
,就得修改原來的程式碼,這樣違背了 OCP 原則。
因此SpringMVC
定義了一個適配介面HandlerAdapter
,使得每一種Controller
有一種對應的介面卡實現類, 讓介面卡代替Controller
執行相應的方法。這樣在擴充套件Controller
時,只需要增加一個介面卡類就完成了SpringMVC
的擴充套件了。
org.springframework.web.servlet.HandlerAdapter
,相當於介面卡模式中的目標(Target)
public interface HandlerAdapter {
boolean supports(Object handler);
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}
org.springframework.web.servlet.DispatcherServlet
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
//通過request可以得到請求對應的控制器handler
mappedHandler = getHandler(processedRequest);
//根據handler得到對應的介面卡
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//通過介面卡去呼叫對應controller的方法,並返回ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
}
getHandlerAdapter:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
我們隨便找一個HandlerAdapter
的實現類,相當於介面卡模式中的介面卡(Adapter),其中Object handler
為適配者(Adaptee)
public class HttpRequestHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
}