如何选择结构型模式?

  1. 接口不兼容 → 适配器

  2. 抽象与实现解耦 → 桥接

  3. 动态扩展功能 → 装饰器

  4. 处理树形结构 → 组合

  5. 简化复杂子系统 → 外观

  6. 优化资源占用 → 享元

  7. 控制对象访问 → 代理

  • 适配器模式

适配器模式概述

适配器模式(Adapter Pattern)是一种结构型设计模式,它允许接口不兼容的类能够一起工作。适配器模式通过将一个类的接口转换成客户希望的另外一个接口,解决了由于接口不兼容而不能一起工作的类的问题。

核心角色

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

  2. 被适配者(Adaptee): 需要被适配的现有接口

  3. 适配器(Adapter): 将Adaptee接口转换为Target接口

两种实现方式

  1. 类适配器: 通过多重继承实现(Adapter继承Target和Adaptee)

  2. 对象适配器: 通过组合实现(Adapter持有Adaptee的引用)

使用场景

适配器模式在以下情况下特别有用:

  1. 系统需要使用现有的类,而这些类的接口不符合系统的需求

  2. 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的类一起工作

  3. 需要统一多个不同接口的类

  4. 旧系统升级改造,需要保留旧系统的功能但又需要与新接口协同工作

C++代码实现示例

示例1:类适配器(通过多重继承)

#include <iostream>
#include <string>

// 目标接口(客户端期望的接口)
class Target {
public:
    virtual ~Target() = default;
    virtual std::string request() const {
        return "Target: 默认目标行为";
    }
};

// 被适配者(需要适配的类)
class Adaptee {
public:
    std::string specificRequest() const {
        return ".eetpadA eht fo roivaheb laicepS";
    }
};

// 适配器(通过多重继承适配)
class Adapter : public Target, private Adaptee {
public:
    Adapter() {}
    std::string request() const override {
        // 调用被适配者的方法并转换结果
        std::string to_reverse = this->specificRequest();
        std::reverse(to_reverse.begin(), to_reverse.end());
        return "Adapter: (翻译后) " + to_reverse;
    }
};

// 客户端代码
void clientCode(const Target *target) {
    std::cout << target->request();
}

int main() {
    std::cout << "客户端: 我能很好地与目标对象一起工作:\n";
    Target *target = new Target;
    clientCode(target);
    std::cout << "\n\n";
    
    Adaptee *adaptee = new Adaptee;
    std::cout << "客户端: Adaptee类有一个奇怪的接口。看不懂:\n";
    std::cout << "Adaptee: " << adaptee->specificRequest();
    std::cout << "\n\n";
    
    std::cout << "客户端: 但是通过适配器我能理解它:\n";
    Adapter *adapter = new Adapter;
    clientCode(adapter);
    std::cout << "\n";

    delete target;
    delete adaptee;
    delete adapter;

    return 0;
}

示例2:对象适配器(通过组合)

#include <iostream>
#include <string>
#include <algorithm>

// 目标接口
class MediaPlayer {
public:
    virtual ~MediaPlayer() = default;
    virtual void play(const std::string& audioType, const std::string& fileName) = 0;
};

// 高级媒体播放器接口
class AdvancedMediaPlayer {
public:
    virtual ~AdvancedMediaPlayer() = default;
    virtual void playVlc(const std::string& fileName) = 0;
    virtual void playMp4(const std::string& fileName) = 0;
};

// 具体高级播放器实现
class VlcPlayer : public AdvancedMediaPlayer {
public:
    void playVlc(const std::string& fileName) override {
        std::cout << "播放VLC文件: " << fileName << std::endl;
    }
    
    void playMp4(const std::string& /*fileName*/) override {}
};

class Mp4Player : public AdvancedMediaPlayer {
public:
    void playVlc(const std::string& /*fileName*/) override {}
    
    void playMp4(const std::string& fileName) override {
        std::cout << "播放MP4文件: " << fileName << std::endl;
    }
};

// 媒体适配器
class MediaAdapter : public MediaPlayer {
private:
    AdvancedMediaPlayer* advancedMusicPlayer;
    
public:
    MediaAdapter(const std::string& audioType) {
        if (audioType == "vlc") {
            advancedMusicPlayer = new VlcPlayer();
        } else if (audioType == "mp4") {
            advancedMusicPlayer = new Mp4Player();
        } else {
            advancedMusicPlayer = nullptr;
        }
    }
    
    ~MediaAdapter() {
        delete advancedMusicPlayer;
    }
    
    void play(const std::string& audioType, const std::string& fileName) override {
        if (audioType == "vlc") {
            advancedMusicPlayer->playVlc(fileName);
        } else if (audioType == "mp4") {
            advancedMusicPlayer->playMp4(fileName);
        }
    }
};

// 音频播放器
class AudioPlayer : public MediaPlayer {
private:
    MediaAdapter* mediaAdapter;
    
public:
    ~AudioPlayer() {
        delete mediaAdapter;
    }
    
    void play(const std::string& audioType, const std::string& fileName) override {
        // 内置支持MP3格式
        if (audioType == "mp3") {
            std::cout << "播放MP3文件: " << fileName << std::endl;
        } 
        // 适配器支持其他格式
        else if (audioType == "vlc" || audioType == "mp4") {
            mediaAdapter = new MediaAdapter(audioType);
            mediaAdapter->play(audioType, fileName);
        } 
        else {
            std::cout << "无效的媒体类型: " << audioType << std::endl;
        }
    }
};

// 客户端代码
int main() {
    AudioPlayer audioPlayer;
    
    audioPlayer.play("mp3", "song.mp3");
    audioPlayer.play("vlc", "movie.vlc");
    audioPlayer.play("mp4", "video.mp4");
    audioPlayer.play("avi", "movie.avi");
    
    return 0;
}

示例3:STL中的适配器

#include <iostream>
#include <stack>
#include <queue>
#include <vector>
#include <deque>

// STL中的适配器示例
void stlAdapterExample() {
    // stack是适配器,默认使用deque作为底层容器
    std::stack<int> stackAdapter;
    
    // queue是适配器,默认使用deque作为底层容器
    std::queue<int> queueAdapter;
    
    // priority_queue是适配器,默认使用vector作为底层容器
    std::priority_queue<int> priorityQueueAdapter;
    
    // 可以使用不同的底层容器
    std::stack<int, std::vector<int>> vectorStack;
    std::queue<int, std::list<int>> listQueue;
}

int main() {
    stlAdapterExample();
    return 0;
}

适配器模式的优缺点

优点

  1. 单一职责原则:可以将接口转换代码与程序的主要业务逻辑分离

  2. 开闭原则:可以在不修改现有客户端代码的情况下在程序中引入新类型的适配器

  3. 提高了类的复用性:通过适配器可以复用现有的类,而不需要修改原有代码

  4. 灵活性好:可以动态地更换适配器,或者同时使用多个适配器

缺点

  1. 增加复杂性:引入适配器会增加系统中类的数量,可能使系统更加复杂

  2. 性能开销:由于额外的间接层,可能会带来轻微的性能损失

  3. 过度使用会导致系统混乱:如果不是必要情况,直接修改接口可能更简单

实际应用案例

  1. 数据库驱动程序:JDBC、ODBC等数据库驱动就是适配器模式的典型应用

  2. 日志框架:如SLF4J作为日志门面,适配各种具体日志实现

  3. 电源适配器:将220V电压转换为设备需要的电压

  4. 旧系统改造:在新系统中兼容旧系统的接口

  5. 第三方库集成:将第三方库的接口适配为自己的接口规范

适配器模式是一种非常实用的设计模式,特别是在系统集成和接口转换场景中。通过合理使用适配器模式,可以大大提高代码的复用性和系统的扩展性

  • 桥接模式
  • 装饰器模式
  • 组合模式
  • 外观模式
  • 享元模式
  • 代理模式

后面再逐一说明

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值