跳转到内容

适配器

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

待办事项
添加更多插图。


抽象工厂 计算机科学设计模式
适配器
桥接模式


你对本页有任何问题吗?
在这里询问


在本手册上创建一个新页面


华夏公益教科书