【UE4 设计模式】适配器模式 Adapter Pattern
概述
描述
-
将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。
套路
- Target(目标抽象类)
目标抽象类定义了客户所需要的接口,可以是一个抽象类或接口,也可以是一个具体的类。 - Adapter(适配器类)
适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配。适配器类是适配者模式的核心,在适配器模式中,它通过继承Target并关联一个Adaptee对象使二者产生联系。 - Adaptee(适配者类)
适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
使用场景
- 系统需要使用现有的类,而这些类的接口不符合系统的需要。
- 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
- 示例
- 为了处理项目代码更新后与原来接口不兼容,可以创建适配器来保证“旧调新”、“新调旧”的功能需求。
- 已定下接口后,要求调用第三方SDK等(类似于电源适配器,要求输出12V,但是输入220V)
优缺点
- 优点
- 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。
- 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。
- 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。
- 缺点
- 过多的使用适配器,会让系统非常零乱,不易整体进行把握
UE4 实践
-
假设自定义一个媒体播放器,接口已经写好,为了使第三方SDK适应自己的接口,此时可以使用适配器。
-
创建目标抽象类,此处以接口形式
UINTERFACE(MinimalAPI) class UMediaInterface : public UInterface { GENERATED_BODY() }; class DESIGNPATTERNS_API IMediaInterface { GENERATED_BODY() public: virtual void PlayVideo(const FString& Filepath) = 0; };
-
创建目适配者类 —— Uffmpeg 、UVLC
UCLASS() // 适配者类 —— ffmpeg class DESIGNPATTERNS_API Uffmpeg : public UObject { GENERATED_BODY() public: virtual void OpenFile(const FString& Filepath) { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" use ffmpeg sdk to play media")); } }; UCLASS() // 适配者类 —— VLC class DESIGNPATTERNS_API UVLC : public UObject { GENERATED_BODY() public: virtual void OpenFile(const FString& Filepath) { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" use VLC sdk to play media")); } };
-
创建适配器类,继承自目标抽象类 —— UffmpegAdapter、UffmpegAdapter
- 本质使重载接口函数,调用其他的接口,来适应新的接口
UCLASS() // 适配器类 —— UffmpegAdapter class DESIGNPATTERNS_API UffmpegAdapter : public UObject, public IMediaInterface { GENERATED_BODY() public: UPROPERTY() Uffmpeg* m_pffmpegSDK; UffmpegAdapter() { m_pffmpegSDK=NewObject<Uffmpeg>(); } ~UffmpegAdapter() {} virtual void PlayVideo(const FString& Filepath) override { m_pffmpegSDK->OpenFile(Filepath); } }; UCLASS() // 适配器类 —— UVLCAdapter class DESIGNPATTERNS_API UffmpegAdapter: public UObject, public IMediaInterface { GENERATED_BODY() public: UPROPERTY() UVLC* m_pVLCSDK; UVLCAdapter() { m_pVLCSDK = NewObject<UVLC>(); } ~UVLCAdapter() {} virtual void PlayVideo(const FString& Filepath) override { m_pVLCSDK->OpenFile(Filepath); } };
-
调用测试
UCLASS() class DESIGNPATTERNS_API AAdapterTestActor : public AActor { GENERATED_BODY() public: AAdapterTestActor(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override { // 使用 ffmpegAdapter 适配器 IMediaInterface* MediaPlayer = Cast<IMediaInterface>(NewObject<UffmpegAdapter>()); MediaPlayer->PlayVideo(TEXT("C:/浪客剑心.最终章.人诛篇.mp4")); // 使用 VLCAdapter 适配器 MediaPlayer = Cast<IMediaInterface>(NewObject<UVLCAdapter>()); MediaPlayer->PlayVideo(TEXT("C:/浪客剑心.最终章.人诛篇.mp4")); } };
-
调式输出
LogTemp: Warning: Uffmpeg::OpenFile use ffmpeg sdk to play media LogTemp: Warning: UVLC::OpenFile use VLC sdk to play media
参考
作者:砥才人
出处:https://www.cnblogs.com/shiroe
本系列文章为笔者整理原创,只发表在博客园上,欢迎分享本文链接,如需转载,请注明出处!