博客 / 詳情

返回

三、適配器模式

一、模式定義

  將一個類的接口轉換成使用者希望的另一個接口,Adapter模式使得原本由於接口不兼容而不能一起工作的那些類可以一起工作。
clipboard

二、應用場景

  當使用者用到的某些類的接口與其它代碼不兼容時,就可以使用適配器模式來改造。當使用者希望重用幾個現有的子類,但這些類缺少一些不能添加到超類中的公共功能時,也可以使用適配器模式。
2.1、優點
①、符合單一職責原則(Single responsibility principle)
②、符合開閉原則(open-closed principle)

三、幾種不同的適配器實現方式

3.1、對象適配器
  • 對象適配器代碼一
package adaptor;

public class ObjectAdapterDesignPattern {
   public static void main(String[] args) {
      //實例化對象
      Product product = new Product();
      //將對象注入到適配器中
      Target target = new Adapter(product);
      int i = target.output5V();
      System.out.println("適配後,修改參數為:"+i);
   }
}
//適配器接口,想要修改或適配的規則
interface Target{
   int output5V();
}
//原始產品
class Product{
   public int output220V(){
      return 220;
   }
}
//對象適配器
class Adapter implements Target {
   private Product product;

   public Adapter(Product product) {
      this.product = product;
   }
   //進行適配,返回需要的適配結果
   @Override
   public int output5V() {
      int i_220 = product.output220V();
      System.out.println(String.format("原始電壓:%d v  —>  輸出電壓:%d  v ", i_220, 5));
      return 5;
   }
}

上面代碼的運行結果如下:
clipboard

  • 對象適配器代碼二
package adaptor.objectAdapter;

public class AdapterPattern {
   public static void main(String[] args) {
      //實例化對象
      Source source = new Source();
      //將對象注入適配器
      Adapter adapter = new Adapter(source);
      adapter.method1();
      adapter.method2();
   }
}
//定義需要執行的標準方法
interface Targetable {
   /**
    * 與Source類中的方法名相同
    */
   public void method1();

   /**
    * 新的方法
    */
   public void method2();
}

class Source {
   public void method1(){
      System.out.println("this is Source method...");
   }
}
//對象適配器類型,要注入對象的實體類
class Adapter implements Targetable {
   public Source source;

   public Adapter(Source source) {
      this.source = source;
   }

   //在標準方法中調用注入對象的方法
   @Override
   public void method1() {
      source.method1();
   }

   @Override
   public void method2() {
      System.out.println("This is the targetable method...");
   }
}

上面代碼的運行結果如下:
clipboard

3.2、類適配器
  • 類適配器代碼一
package adaptor;

public class ClassAdapterDesignPattern {
   public static void main(String[] args) {
      //只用實例化適配器就可以。
      Target_ target_ = new Adapter_();
      int i_5 = target_.output5V();
      System.out.println("適配後,修改參數為:"+i_5);
      //類適配器,會對接口和適配器產生一些污染,可以調用到父類中的方法
      int i_220 = ((Adapter_) target_).output220V();
      System.out.println("產生污染的對象,可以獲取父類中的方法:"+i_220);
   }
}

//原始產品
class Product_{
   public int output220V(){
      return 220;
   }
}

//適配器接口,想要修改或適配的規則
interface Target_{
   int output5V();
}

//類適配器,直接繼承原始產品,而不是注入原始產品的實例
//可能產生問題:會對接口和適配器產生一些污染,可以調用到父類中的方法
class Adapter_ extends Product_ implements Target_{
   //進行適配,返回需要的適配結果
   @Override
   public int output5V() {
      int i_220 = super.output220V();
      System.out.println(String.format("原始電壓:%d v  —>  輸出電壓:%d  v ", i_220, 5));
      return 5;
   }
}

上面代碼的運行結果如下:
clipboard

  • 類適配器代碼二
package adaptor.classAdapter;

public class AdapterPattern {
   public static void main(String[] args) {
      //只用實例化適配器
      Adapter adapter = new Adapter();
      adapter.method1();
      adapter.method2();
   }
}

interface Targetable {
   /**
    * 與Source類中的方法名相同
    */
   public void method1();

   /**
    * 新類的方法
    */
   public void method2();
}

class Source {
   //原始產品中,有與接口中聲明的方法同名的method1()方法
   public void method1(){
      System.out.println("this is Source method...");
   }
}
//繼承原始產品,等於重寫了接口的同名方法
class Adapter extends Source implements Targetable {
   @Override
   public void method2() {
      System.out.println("This is the targetable method...");
   }
}

上面代碼的運行結果如下:
clipboard

3.3、接口適配器

  下面代碼中的abstract Wrapper.class為適配類。其餘的類只要繼承Wrapper類即可適配Port.interface接口中定義的函數

package adaptor.interfaceAdaptor;

public class AdapterPattern {

   private static Port chatPort = new Chat();
   private static Port serverPort = new Server();

   public static void main(String[] args) {
      // 聊天服務
      chatPort.NET();

      // 服務器
      serverPort.SSH();
      serverPort.NET();
      serverPort.Tomcat();
      serverPort.MySQL();
   }
}

interface Port {
   /**
    * 遠程SSH端口為22
    */
   void SSH();

   /**
    * 網絡端口為80
    */
   void NET();

   /**
    * Tomcat容器端口為8080
    */
   void Tomcat();

   /**
    * MySQL數據庫端口為3306
    */
   void MySQL();
}

/**
 * 定義抽象類實現端口接口,但是什麼事情都不做
 */
abstract class Wrapper implements Port {
   @Override
   public void SSH() {

   }

   @Override
   public void NET() {

   }

   @Override
   public void Tomcat() {

   }

   @Override
   public void MySQL() {

   }
}

/**
 * 網站服務器
 * 需要Tomcat容器,Mysql數據庫,網絡服務,遠程服務
 */
class Server extends Wrapper {
   @Override
   public void SSH() {
      System.out.println("Connect success...");
   }

   @Override
   public void NET() {
      System.out.println("WWW...");
   }

   @Override
   public void Tomcat() {
      System.out.println("Tomcat is running...");
   }

   @Override
   public void MySQL() {
      System.out.println("MySQL is running...");
   }
}


/**
 * 提供聊天服務
 * 需要網絡功能
 */
class Chat extends Wrapper {

   @Override
   public void NET() {
      System.out.println("Hello World...");
   }
}

四、適配器模式的應用

  Java IO 庫採用了裝飾器模式(Decorator Pattern)和適配器模式(Adapter Pattern)的組合設計模式,其中InputStream是一個抽象類:
①、FileInputStream.class覆蓋(適配了)了abstract InputStream.class的read()函數,skip()函數等,是一個讀取字節的輸入Stream;
②、ObjectInputStream.class覆蓋(適配了)了abstract InputStream.class的read()函數,skip()函數等,然後又有一些abstract InputStream.class種沒有的函數,比如readObject()函數等,是一個從流中讀入一個自定義的對象的輸入Stream,但是需要與ObjectOutputStream與配合使用,且按同樣的順序(寫入的對象的順序決定了讀取對象的順序)。
  這3個類的UML圖,如下所示:
image

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.