文章

设计模式解密:适配器模式的终极指南(PHP/Go双实现)

一、什么是适配器模式?

适配器模式(Adapter Pattern) 是一种结构型设计模式,用于将一个类的接口转换成客户端期望的另一个接口。适配器模式的核心思想是通过一个中间层(适配器)来解决接口不兼容的问题,使原本不兼容的类能够协同工作。

核心角色:

  1. 目标接口(Target):客户端期望的接口

  2. 适配者(Adaptee):需要被适配的类

  3. 适配器(Adapter):实现目标接口并包装适配者

适配器模式结构图


二、适用场景

  • ✅ 需要使用现有类,但其接口与客户端不兼容

  • ✅ 需要复用一些现有的类,但这些类不符合系统接口规范

  • ✅ 需要统一多个类的接口

  • ✅ 需要解耦客户端与具体实现


三、PHP实现方案

1. 类适配器(继承方式)

interface Target {
    public function request(): string;
}

class Adaptee {
    public function specificRequest(): string {
        return "Specific request";
    }
}

class Adapter extends Adaptee implements Target {
    public function request(): string {
        return $this->specificRequest();
    }
}

// 使用示例
$adapter = new Adapter();
echo $adapter->request(); // 输出:Specific request

2. 对象适配器(组合方式)

interface Target {
    public function request(): string;
}

class Adaptee {
    public function specificRequest(): string {
        return "Specific request";
    }
}

class Adapter implements Target {
    private $adaptee;

    public function __construct(Adaptee $adaptee) {
        $this->adaptee = $adaptee;
    }

    public function request(): string {
        return $this->adaptee->specificRequest();
    }
}

// 使用示例
$adaptee = new Adaptee();
$adapter = new Adapter($adaptee);
echo $adapter->request(); // 输出:Specific request

四、Go实现方案

1. 接口适配器

package main

import "fmt"

type Target interface {
    Request() string
}

type Adaptee struct{}

func (a *Adaptee) SpecificRequest() string {
    return "Specific request"
}

type Adapter struct {
    adaptee *Adaptee
}

func NewAdapter(adaptee *Adaptee) *Adapter {
    return &Adapter{adaptee: adaptee}
}

func (a *Adapter) Request() string {
    return a.adaptee.SpecificRequest()
}

// 使用示例
func main() {
    adaptee := &Adaptee{}
    adapter := NewAdapter(adaptee)
    fmt.Println(adapter.Request()) // 输出:Specific request
}

2. 函数适配器

package main

import "fmt"

type TargetFunc func() string

type Adaptee struct{}

func (a *Adaptee) SpecificRequest() string {
    return "Specific request"
}

func Adapt(adaptee *Adaptee) TargetFunc {
    return func() string {
        return adaptee.SpecificRequest()
    }
}

// 使用示例
func main() {
    adaptee := &Adaptee{}
    adapter := Adapt(adaptee)
    fmt.Println(adapter()) // 输出:Specific request
}

五、关键实现差异对比

特性

PHP

Go

接口定义

使用interface关键字

使用interface关键字

适配器实现

支持类适配器和对象适配器

通常使用对象适配器

函数适配器

不支持

支持函数适配器

多态支持

通过接口实现

通过接口实现

类型系统

强类型需显式声明

类型推断简化代码


六、模式优缺点分析

👍 优点:

  • 解耦客户端与适配者:客户端无需修改即可使用适配者

  • 复用现有类:无需修改现有代码即可复用

  • 灵活性高:支持多种适配方式(类适配器、对象适配器)

👎 缺点:

  • 增加复杂度:引入额外的类和接口

  • 过度设计:简单场景下可能显得冗余


七、实际应用案例

1. 第三方库集成

// PHP示例
$thirdPartyService = new ThirdPartyService();
$adapter = new ThirdPartyServiceAdapter($thirdPartyService);
$client = new Client($adapter);
$client->execute();

2. 日志系统适配

// Go示例
type Logger interface {
    Log(message string)
}

type FileLogger struct{}

func (f *FileLogger) LogToFile(message string) {
    fmt.Println("Log to file:", message)
}

type FileLoggerAdapter struct {
    fileLogger *FileLogger
}

func NewFileLoggerAdapter(fileLogger *FileLogger) *FileLoggerAdapter {
    return &FileLoggerAdapter{fileLogger: fileLogger}
}

func (f *FileLoggerAdapter) Log(message string) {
    f.fileLogger.LogToFile(message)
}

// 使用示例
func main() {
    fileLogger := &FileLogger{}
    adapter := NewFileLoggerAdapter(fileLogger)
    adapter.Log("Hello, World!") // 输出:Log to file: Hello, World!
}

3. 数据格式转换

// PHP示例
$jsonData = '{"name": "John", "age": 30}';
$adapter = new JsonToXmlAdapter($jsonData);
$xmlData = $adapter->convert();
echo $xmlData;

八、与装饰器模式的区别

适配器模式

装饰器模式

目的

解决接口不兼容问题

动态扩展对象功能

核心思想

转换接口

包装对象

适用场景

接口不兼容

功能扩展

对象关系

适配者与目标接口无直接关系

装饰者与目标接口有直接关系


九、总结

适配器模式是解决接口不兼容问题的经典设计模式。通过引入适配器,我们可以复用现有代码,同时保持系统的灵活性和可维护性。无论是PHP的类适配器还是Go的函数适配器,适配器模式都能显著提升代码的复用性和扩展性。

在下一篇文章中,我们将探讨 桥接模式 及其在抽象与实现分离中的应用。敬请期待!


下一篇预告:设计模式系列(七)——桥接模式:抽象与实现的桥梁

License:  CC BY 4.0