跳轉到內容

介面卡

25% developed
來自華夏公益教科書,開放書籍,開放世界

抽象工廠 計算機科學設計模式
介面卡
橋接模式

當客戶端類需要呼叫不相容的提供者類時,使用介面卡模式。 讓我們想象一個需要呼叫LegacyEmployee類中方法的MailingClient

MailingClient
+firstName: String
+lastName: String
 
IEmployee
+mailEmployee(firstName: String, lastName: String)
 
LegacyEmployee
+mailEmployee(fullName: String)

MailingClient已經呼叫了實現IEmployee介面的類,但是LegacyEmployee沒有實現它。 我們可以向LegacyEmployee新增一個新方法來實現IEmployee介面,但是LegacyEmployee是遺留程式碼,無法修改。 我們可以修改MailingClient類來呼叫LegacyEmployee,但是它需要改變每次呼叫。 格式化程式碼會在所有地方重複。 此外,MailingClient將無法再呼叫實現IEmployee介面的其他提供者類。

所以解決方案是在另一個獨立的類,介面卡中編寫格式化程式碼,也稱為包裝類

IEmployee
+mailEmployee(firstName: String, lastName: String)
MailingClient
+firstName: String
+lastName: String
EmployeeAdapter
+mailEmployee(firstName: String, lastName: String)
LegacyEmployee
+mailEmployee(fullName: String)

EmployeeAdapter實現了IEmployee介面。 MailingClient呼叫EmployeeAdapterEmployeeAdapter格式化資料並呼叫LegacyEmployee。 這種型別的介面卡稱為物件介面卡。 另一種型別的介面卡是類介面卡


Clipboard

待辦事項
描述類介面卡。


示例

WebGL-2D是一個實現了介面卡模式的 JavaScript 庫。 該庫用於 HTML5 畫布元素。 畫布元素有兩個介面:2d 和 WebGL。 第一個非常易於使用,第二個更復雜但已最佳化且更快。 WebGL-2D 將 WebGL 介面“適配”到 2d 介面,以便客戶端僅呼叫 2d 介面。

成本

在實現此模式之前三思而後行。 此模式不應在設計時規劃。 如果你計劃從頭開始為專案使用它,這意味著你沒有理解此模式。 它應該只用於遺留程式碼。 這是最不糟糕的解決方案。

建立

它的實現很簡單,但可能很昂貴。 你不應該需要重構程式碼,因為客戶端和提供者應該還不能一起工作。

維護

這是最糟糕的部分。 大部分程式碼都有冗餘(但比沒有模式要少)。 現代介面應該始終提供與遺留介面需要工作一樣多的資訊。 如果現代介面缺少某條資訊,則可能會對模式提出質疑。

移除

此模式可以輕鬆移除,因為自動重構操作可以輕鬆移除它的存在。

建議

  • 在介面卡類的名稱中新增介面卡一詞,以向其他開發人員指示該模式的使用。

實現

物件介面卡

在 Java 中實現

我們的公司是透過合併建立的。 一個員工列表可以在你可以透過CompanyAEmployees類訪問的資料庫中找到

/**
 * Employees of the Company A.
 */
public class CompanyAEmployees {
  /**
   * Retrieve the employee information from the database.
   *
   * @param sqlQuery The SQL query.
   * @return The employee object.
   */
  public Employee getEmployee(String sqlQuery) {
      Employee employee = null;
     
      // Execute the request.
     
      return employee;
    }
}

一個員工列表可以在你可以透過CompanyBEmployees類訪問的 LDAP 中找到

/**
 * Employees of the Company B.
 */
public class CompanyBEmployees {
  /**
   * Retrieve the employee information from the LDAP.
   *
   * @param sqlQuery The SQL query.
   * @return The employee object.
   */
  public Employee getEmployee(String distinguishedName) {
      Employee employee = null;
     
      // Call the LDAP.
     
      return employee;
    }
}

為了訪問公司 A 的前員工和公司 B 的前員工,我們定義了一個介面,該介面將被兩個介面卡使用,EmployeeBrowser

/**
 * Retrieve information about the employees.
 */
interface EmployeeBrowser {
  /**
   * Retrieve the employee information.
   *
   * @param direction The employee direction.
   * @param division The employee division.
   * @param department The employee departement.
   * @param service The employee service.
   * @param firstName The employee firstName.
   * @param lastName The employee lastName.
   *
   * @return The employee object.
   */
  Employee getEmployee(String direction, String division, String department, String service, String firstName, String lastName);
}

我們為前公司 A 的程式碼建立一個介面卡,CompanyAAdapter

/**
 * Adapter for the company A legacy code.
 */
public class CompanyAAdapter implements EmployeeBrowser {
  /**
   * Retrieve the employee information.
   *
   * @param direction The employee direction.
   * @param division The employee division.
   * @param department The employee department.
   * @param service The employee service.
   * @param firstName The employee firstName.
   * @param lastName The employee lastName.
   *
   * @return The employee object.
   */
  public Employee getEmployee(String direction, String division, String department, String service, String firstName, String lastName) {
    String distinguishedName = "SELECT *"
      + " FROM t_employee as employee"
      + " WHERE employee.company= 'COMPANY A'"
      + " AND employee.direction = " + direction
      + " AND employee.division = " + division
      + " AND employee.department = " + department
      + " AND employee.service = " + service
      + " AND employee.firstName = " + firstName
      + " AND employee.lastName = " + lastName;
   
    CompanyAEmployees companyAEmployees = new CompanyAEmployees();
    return companyAEmployees.getEmployee(distinguishedName);
  }
}

我們為前公司 B 的程式碼建立一個介面卡,CompanyBAdapter

/**
 * Adapter for the company B legacy code.
 */
public class CompanyBAdapter implements EmployeeBrowser {
  /**
   * Retrieve the employee information.
   *
   * @param direction The employee direction.
   * @param division The employee division.
   * @param department The employee department.
   * @param service The employee service.
   * @param firstName The employee firstName.
   * @param lastName The employee lastName.
   *
   * @return The employee object.
   */
  public Employee getEmployee(String direction, String division, String department, String service, String firstName, String lastName) {
    String distinguishedName = "ov1 = " + direction
      + ", ov2 = " + division
      + ", ov3 = " + department
      + ", ov4 = " + service
      + ", cn = " + firstName + lastName;
   
    CompanyBEmployees companyBEmployees = new CompanyBEmployees();
    return companyBEmployees.getEmployee(distinguishedName);
  }
}
在 Ruby 中實現

Ruby

class Adaptee
  def specific_request
    # do something
  end
end

class Adapter
  def initialize(adaptee)
    @adaptee = adaptee
  end

  def request
    @adaptee.specific_request
  end
end

client = Adapter.new(Adaptee.new)
client.request
在 Python 中實現
class Adaptee:
    def specific_request(self):
        return 'Adaptee'
 
class Adapter:
    def __init__(self, adaptee):
        self.adaptee = adaptee
 
    def request(self):
        return self.adaptee.specific_request()
 
client = Adapter(Adaptee())
print client.request()
在 Scala 中實現
trait Socket220V {
  def plug220()
}

trait Socket19V {
  def plug19()
}

class Laptop extends Socket19V {
  def plug19() {
    println("Charging....")
  }
}

class LaptopAdapter(laptop: Laptop) extends Socket220V {
  def plug220() {
    println("Transform1...")
    laptop.plug19()
  }
}

object Test {
  def main(args: Array[String]) {
    //you can do it like this:
    new LaptopAdapter(new Laptop).plug220()

    //or like this (doesn't need LaptopAdapter)
    new Laptop with Socket220V {
      def plug220() {
        println("Transform2...")
        this.plug19()
      }
    } plug220()
  }
}
在 Delphi 中實現
program Adapter;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils;

type
  (* Its interface *)
  TTarget = class abstract
    function Request: string; virtual; abstract;
  end;

  (* A client accessed to this. *)
  TAdaptee = class(TTarget)
    function Request: string; override;
  end;

  (* Object Adapter uses composition and can wrap classes or interfaces, or both.*)
  (* Redirect call to Adaptee. It is loose coupling of client and adapter.*)
  (*
    *It can do this since it contains, as a private, encapsulated member,
    *the class or interface object instance it wraps.
  *)
  TObjectAdapter = class
    fAdaptee: TAdaptee;
    function SpecialRequest: string;
    constructor Create(adaptee: TAdaptee);
  end;

  { TObjectAdapter }

constructor TObjectAdapter.Create;
begin
  fAdaptee := TAdaptee.Create;
end;

function TObjectAdapter.SpecialRequest: string;
begin
  Result := fAdaptee.Request;
end;

{ TAdaptee }

function TAdaptee.Request: string;
begin
  Result := 'Adaptee';
end;

var
  clientObject: TObjectAdapter;

begin
  try
    { TODO -oUser -cConsole Main : Insert code here }
    clientObject := TObjectAdapter.Create(TAdaptee.Create);

    WriteLn('Call method Object Adapter: '+clientObject.SpecialRequest);

    WriteLn(#13#10+ 'Press any key to continue...');
    ReadLn;

    clientObject.Free;
  except
    on E: Exception do
      WriteLn(E.ClassName, ': ', E.Message);
  end;

end.


類介面卡

在 Python 中實現
class Adaptee1:
    def __init__(self, *args, **kw):
        pass
   
    def specific_request(self):
        return 'Adaptee1'

class Adaptee2:
    def __init__(self, *args, **kw):
        pass
   
    def specific_request(self):
        return 'Adaptee2'
 
class Adapter(Adaptee1, Adaptee2):
    def __init__(self, *args, **kw):
        Adaptee1.__init__(self, *args, **kw)
        Adaptee2.__init__(self, *args, **kw)
 
    def request(self):
        return Adaptee1.specific_request(self), Adaptee2.specific_request(self)
 
client = Adapter()
print client.request()
在 Delphi 中實現
program Adapter;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils;

type
  (* Its interface *)
  TTarget = class abstract
    function Request: string; virtual; abstract;
  end;

  (* A client accessed to this. *)
  TAdaptee = class(TTarget)
    function Request: string; override;
  end;

  (* Class Adapter uses inheritance and can only wrap a class.*)
  (* This plain old inheritance. *)
  (* It cannot wrap an interface since by definition*)
  (* it must derive from some base class as Adaptee in example*)
  (*
    * Can't reuse Class Adapter without rewrite code
    * You need implements other adapter with other method in other class.
  *)
   TClassAdapter = class(TAdaptee)
   function SpecialRequest: string;
   end;

{ TClassAdapter }

function TClassAdapter.SpecialRequest: string;
begin
  //use inherited Request as SpecialRequest
  Result:= inherited Request;
end;

{ TAdaptee }

function TAdaptee.Request: string;
begin
  Result := 'Adaptee';
end;

var
  clientClass:TClassAdapter;

begin
  try
    { TODO -oUser -cConsole Main : Insert code here }
    clientClass:= TClassAdapter.Create;

    WriteLn('Call method Class Adapter: '+clientClass.SpecialRequest);

    WriteLn(#13#10+ 'Press any key to continue...');
    ReadLn;

    clientClass.Free;
  except
    on E: Exception do
      WriteLn(E.ClassName, ': ', E.Message);
  end;

end.


Clipboard

待辦事項
新增更多插圖。


抽象工廠 計算機科學設計模式
介面卡
橋接模式


你對本頁有任何問題嗎?
在這裡詢問


在本手冊上建立一個新頁面


華夏公益教科書