Codeforces Round #689 (Div. 2) A,B,C,D題解
阿新 • • 發佈:2020-12-13
現實生活中的介面卡例子
泰國插座用的是兩孔的(歐標),可以買個多功能轉換插頭 (介面卡) ,這樣就可以使用了
基本介紹
1) 介面卡模式(Adapter Pattern)將某個類的介面轉換成客戶端期望的另一個介面表示,主的目的是相容性,讓原本因介面不匹配不能一起工作的兩個類可以協同工作。其別名為包裝器(Wrapper)
2) 介面卡模式屬於結構型模式
3) 主要分為三類:類介面卡模式、物件介面卡模式、介面介面卡模式
介面卡模式工作原理
1) 介面卡模式:將一個類的介面轉換成另一種介面.讓原本介面不相容的類可以相容
2) 從使用者的角度看不到被適配者,是解耦的
3) 使用者呼叫介面卡轉化出來的目標介面方法,介面卡再呼叫被適配者的相關介面方法
4) 使用者收到反饋結果,感覺只是和目標介面互動,
類介面卡模式介紹
基本介紹:Adapter類,通過繼承 src類,實現 dst 類介面,完成src->dst的適配。
類介面卡模式應用例項
1) 應用例項說明
以生活中充電器的例子來講解介面卡,充電器本身相當於Adapter,220V交流電相當於src (即被適配者),我們的目dst(即 目標)是5V直流電
2) 思路分析(類圖)
3) 程式碼實現
被適配的類
//被適配的類 public class Voltage220V { //輸出220V的電壓 public int output220V() { int src = 220; System.out.println("電壓=" + src + "伏"); return src; } }
適配介面
//適配介面 public interface IVoltage5V { public int output5V(); }
介面卡類
//介面卡類 public class VoltageAdapter extends Voltage220V implements IVoltage5V { @Override public int output5V() { // TODO Auto-generated method stub //獲取到220V電壓int srcV = output220V(); int dstV = srcV / 44 ; //轉成 5v return dstV; } }
手機
public class Phone { //充電 public void charging(IVoltage5V iVoltage5V) { if(iVoltage5V.output5V() == 5) { System.out.println("電壓為5V, 可以充電~~"); } else if (iVoltage5V.output5V() > 5) { System.out.println("電壓大於5V, 不能充電~~"); } } }
測試:
public class Client { public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(" === 類介面卡模式 ===="); Phone phone = new Phone(); phone.charging(new VoltageAdapter()); } }類介面卡模式注意事項和細節 1) Java是單繼承機制,所以類介面卡需要繼承src類這一點算是一個缺點, 因為這要求dst必須是介面,有一定侷限性; 2) src類的方法在Adapter中都會暴露出來,也增加了使用的成本。 3) 由於其繼承了src類,所以它可以根據需求重寫src類的方法,使得Adapter的靈活性增強了。 物件介面卡模式 物件介面卡模式介紹 1) 基本思路和類的介面卡模式相同,只是將Adapter類作修改,不是繼承src類,而是持有src類的例項,以解決相容性的問題。 即:持有 src類,實現 dst 類介面,完成src->dst的適配 2) 根據“合成複用原則”,在系統中儘量使用關聯關係來替代繼承關係。 3) 物件介面卡模式是介面卡模式常用的一種 物件介面卡模式應用例項 1) 應用例項說明 以生活中充電器的例子來講解介面卡,充電器本身相當於Adapter,220V交流電相當於src (即被適配者),我們的目dst(即目標)是5V直流電,使用物件介面卡模式完成。 2) 思路分析(類圖):只需修改介面卡即可, 3) 程式碼實現 被適配的類
//被適配的類 public class Voltage220V { //輸出220V的電壓,不變 public int output220V() { int src = 220; System.out.println("電壓=" + src + "伏"); return src; } }
適配介面
//適配介面 public interface IVoltage5V { public int output5V(); }
介面卡類
//介面卡類 public class VoltageAdapter implements IVoltage5V { private Voltage220V voltage220V; // 關聯關係-聚合 //通過構造器,傳入一個 Voltage220V 例項 public VoltageAdapter(Voltage220V voltage220v) { this.voltage220V = voltage220v; } @Override public int output5V() { int dst = 0; if(null != voltage220V) { int src = voltage220V.output220V();//獲取220V 電壓 System.out.println("使用物件介面卡,進行適配~~"); dst = src / 44; System.out.println("適配完成,輸出的電壓為=" + dst); } return dst; } }
手機
public class Phone { //充電 public void charging(IVoltage5V iVoltage5V) { if(iVoltage5V.output5V() == 5) { System.out.println("電壓為5V, 可以充電~~"); } else if (iVoltage5V.output5V() > 5) { System.out.println("電壓大於5V, 不能充電~~"); } } }
測試:
public class Client { public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(" === 物件介面卡模式 ===="); Phone phone = new Phone(); phone.charging(new VoltageAdapter(new Voltage220V())); } }物件介面卡模式注意事項和細節 1) 物件介面卡和類介面卡其實算是同一種思想,只不過實現方式不同。根據合成複用原則,使用組合替代繼承, 所以它解決了類介面卡必須繼承src的侷限性問題,也不再要求dst必須是介面。 2) 使用成本更低,更靈活。 介面介面卡模式 介面介面卡模式介紹 1) 一些書籍稱為:介面卡模式(Default Adapter Pattern)或預設介面卡模式。 2) 當不需要全部實現介面提供的方法時,可先設計一個抽象類實現介面,併為該介面中每個方法提供一個預設實現(空方法),那麼該抽象類的子類可有選擇地覆蓋父類的某些方法來實現需求 3) 適用於一個介面不想使用其所有的方法的情況。 介面介面卡模式應用例項 1) Android中的屬性動畫ValueAnimator類可以通過addListener(AnimatorListener listener)方法新增監聽器, 那麼常規寫法如: 2) 有時候我們不想實現Animator.AnimatorListener介面的全部方法,我們只想監聽onAnimationStart,我們會如下寫 3) AnimatorListenerAdapter類,就是一個介面介面卡,程式碼如下:它空實現了Animator.AnimatorListener類(src)的所有方法. 4) AnimatorListener是一個介面. 5) 程式裡的匿名內部類就是Listener 具體實現類 再次理解介面介面卡 類圖:
示例程式碼
介面
public interface Interface4 { public void m1(); public void m2(); public void m3(); public void m4(); }
抽象類
//在AbsAdapter 我們將 Interface4 的方法進行預設實現 public abstract class AbsAdapter implements Interface4 { //預設實現 public void m1() { } public void m2() { } public void m3() { } public void m4() { } }
測試:
public class Client { public static void main(String[] args) { AbsAdapter absAdapter = new AbsAdapter() { //只需要去覆蓋我們 需要使用 介面方法 @Override public void m1() { // TODO Auto-generated method stub System.out.println("使用了m1的方法"); } }; absAdapter.m1(); } }介面卡模式在SpringMVC框架應用的原始碼剖析 1) SpringMvc中的HandlerAdapter, 就使用了介面卡模式 2) SpringMVC處理請求的流程自己回顧一下 3) 使用HandlerAdapter 的原因分析: 可以看到處理器的型別不同,有多重實現方式,那麼呼叫方式就不是確定的,如果需要直接呼叫Controller方法,需要呼叫的時候就得不斷是使用if else來進行判斷是哪一種子類然後執行。那麼如果後面要擴充套件Controller,就得修改原來的程式碼,這樣違背了OCP原則。 大體流程就是:
public class DispatcherServlet extends FrameworkServlet { // 通過HandlerMapping來對映Controller mappedHandler = getHandler(processedRequest); //獲取介面卡 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); //.. // 通過介面卡呼叫controller的方法並返回ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); }
具體:
找到DispatcherServlet 類的原始碼: 當一個請求來的時候,DispatcherServlet 會執行doDispatcher 的一個方法,doDispatcher 會接收到request這裡有一個非常核心的方法,根據請求得到一個mappedHandler,這個mappedHandler就是我們的controller,也就是我們的控制器,
然後通過mappedHandler得到一個HandlerAdapter,也就是介面卡,因為不同的handler 要呼叫的不同的介面卡去處理
這個getHandlerAdapter方法,返回的一個HandlerAdapter,是一個介面,
通過這個HandlerAdapter的原始碼可以看到,它有好幾個實現的介面卡類:
那麼這個getHandlerAdapter方法是怎麼拿到對應的controller的,看原始碼:getHandlerAdapter點進去
可以看到它用的一個迴圈,如果為真,就返回這個handler,主要是這個supports方法來進行判斷的
總結原理:
自己手寫一個加深理解
首先寫一個controller介面:裡面有幾個實現類//多種Controller實現 public interface Controller { } class HttpController implements Controller { public void doHttpHandler() { System.out.println("http..."); } } class SimpleController implements Controller { public void doSimplerHandler() { System.out.println("simple..."); } } class AnnotationController implements Controller { public void doAnnotationHandler() { System.out.println("annotation..."); } }
HandlerAdapter類
///定義一個Adapter介面 public interface HandlerAdapter { public boolean supports(Object handler); public void handle(Object handler); } // 多種介面卡類 class SimpleHandlerAdapter implements HandlerAdapter { public void handle(Object handler) { ((SimpleController) handler).doSimplerHandler(); } public boolean supports(Object handler) { return (handler instanceof SimpleController); } } class HttpHandlerAdapter implements HandlerAdapter { public void handle(Object handler) { ((HttpController) handler).doHttpHandler(); } public boolean supports(Object handler) { return (handler instanceof HttpController); } } class AnnotationHandlerAdapter implements HandlerAdapter { public void handle(Object handler) { ((AnnotationController) handler).doAnnotationHandler(); } public boolean supports(Object handler) { return (handler instanceof AnnotationController); } }DispatchServlet
import java.util.ArrayList; import java.util.List; public class DispatchServlet { public static List<HandlerAdapter> handlerAdapters = new ArrayList<HandlerAdapter>(); public DispatchServlet() { handlerAdapters.add(new AnnotationHandlerAdapter()); handlerAdapters.add(new HttpHandlerAdapter()); handlerAdapters.add(new SimpleHandlerAdapter()); } public void doDispatch() { // 此處模擬SpringMVC從request取handler的物件, // 介面卡可以獲取到希望的Controller HttpController controller = new HttpController(); // AnnotationController controller = new AnnotationController(); //SimpleController controller = new SimpleController(); // 得到對應介面卡 HandlerAdapter adapter = getHandler(controller); // 通過介面卡執行對應的controller對應方法 adapter.handle(controller); } public HandlerAdapter getHandler(Controller controller) { //遍歷:根據得到的controller(handler), 返回對應介面卡 for (HandlerAdapter adapter : this.handlerAdapters) { if (adapter.supports(controller)) { return adapter; } } return null; } public static void main(String[] args) { new DispatchServlet().doDispatch(); // http... } }介面卡模式的注意事項和細節 1) 三種命名方式,是根據 src是以怎樣的形式給到Adapter(在Adapter裡的形式)來命名的。 2) 類介面卡:以類給到,在Adapter裡,就是將src當做類,繼承物件介面卡:以物件給到,在Adapter裡,將src作為一個物件,持有介面介面卡:以介面給到,在Adapter裡,將src作為一個介面,實現 3) Adapter模式最大的作用還是將原本不相容的介面融合在一起工作。 4) 實際開發中,實現起來不拘泥於我們講解的三種經典形式