介绍说明

在年初七的时候,学习了工厂模式,今天在复习的时候发现漏了几个知识点,因此重写这篇文章,以循环渐进的描述方式来对比不同的使用技巧。

工厂设计模式属于 “创建型设计模式”,在我理解,就是为一个相同类型的功能模块抽象出一组接口定义,统一采用各种方案实现的相同类型的功能模块。主要的业务逻辑不必关心该功能模块采用什么方案实现,只需关心如何使用抽象出来的接口即可。完美体现出这句名言:计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决.

在我看来,工厂设计模式其核心是标准接口的定义,其次才是体现在对象创建技巧上,应该改为接口设计模式来命名会更加贴切些,不明白为什么会以工厂来命名。可能是工厂生产都是标准化生产,所以这么命名?也可能是由于我刚开始正式涉及应用开发领域,编写和阅读代码量不够,还有其它的场景我未曾经历过,缺乏一些认知导致。若你有不同的见解,请留言说一下,感谢感谢。

应用场景

某个功能模块,有多种实现方案,需要同时兼容这些方案,在不同的场景中需要切换或同时使用的时候。

例如前段时间比较火的智能语音音箱产品,都是以语音识别技术为基础,配合 TTS、NLP 等技术实现。语音识别技术有科大讯飞、奇梦者、声智等企业提供方案。开发以语音识别技术为基础的产品,为了持续提升产品的用户体验,可能都会遇到同时兼容多家方案的情况,那么就可以抽象出一套不依赖具体方案,只体现出具体功能的接口,基于这些接口来封装各家方案的SDK,主要业务逻辑仅使用抽象出来的接口。通过售后部门、测试部门联合确认当前正式版本使用的效果,以及其它各家的效果,来确认下一版本采用哪家方案,这时候只需要修改一个参数或某个配置文件即可完成切换,而不会影响业务逻辑的实现。

其它例子:

  • 日志输出(可以通过串口输出、也可以通过USB输出、也可以通过网络传输到云端服务器又或者写入文件)
  • 数据通信(可以通过 websocket 实现、也可以通过 http、https、socket实现、也可以是 local socket)
  • 数据存储(ini配置文件、数据库、二进制文件等)

设计思想

核心思想就是增加一个中间层接口,不同的实现方案都去实现这些接口,而业务逻辑则去调用这些接口。在 C++ 中,利用的继承和纯虚函数实现,在 C 中,可以通过函数指针实现。

使用方式

  • 直接调用方式
  • 简单工厂模式
  • 工厂方法模式
  • 抽象工厂模式(另外写一篇文章介绍)

示例说明

这里以音频播放器来作为一个例子,为了方便不同具体播放器之间的切换,首先抽象出音频播放器中常用的接口,然后在播放器的具体实现中,采用了 “调用外部命令” 以及 “使用mplayer接口” 两种方式实现播放器(没有提供具体实现,仅为了便于理解工厂设计模式)。

调用外部播放器命令实现,可以通过进程控制技术来实现。示例中之所以增加 init() 和 release() 接口定义,一来是为初始化播放器提供合适的调用机会,二来也是提供了调整某个播放器参数来灵活使用,例如指定外部播放器。

直接调用方式

首先抽象播放器的常用接口,如初始化、播放、暂停、恢复、重播、停止、释放、设置事件监听回调以及相应事件回调接口。这些接口只定义不作具体实现,由后续具体播放器去继承实现,即定义了一组标准接口,由具体的播放器遵循这些标准接口而设计。

/**
******************************************************************************
* @文件 AudioPlayerInterface.h
* @版本 V1.0.0
* @日期
* @概要 工厂模式示例代码-播放器接口定义
* @作者 lmx
* @邮箱 lovemengx@qq.com
* @博客 https://me.csdn.net/lovemengx
******************************************************************************
**/ #ifndef __AUDIO_PLAYER_INTERFACE_H
#define __AUDIO_PLAYER_INTERFACE_H #define AUDIO_PLAYER_INTERFACE_RETURN_SUCCESS 0
#define AUDIO_PLAYER_INTERFACE_RETURN_INVALID_PARAMETER -1
#define AUDIO_PLAYER_INTERFACE_RETURN_FAIL -2 // 事件监听器
class AudioPlayerEventMonitor
{
public: typedef enum
{
PLAYER_EVENT_TYEP_PLAY, // 开始播放
PLAYER_EVENT_TYEP_PAUSE, // 暂停播放
PLAYER_EVENT_TYEP_RESUME, // 恢复播放
PLAYER_EVENT_TYEP_STOP, // 停止播放
PLAYER_EVENT_TYEP_DONE, // 播放完成
}PLAYER_EVENT_TYEP_E; typedef struct
{
PLAYER_EVENT_TYEP_E event; // 事件类型
}TAudioPlayerEvent; public:
virtual void onAudioPlayerEventMonitor(TAudioPlayerEvent* pEvent) = 0;
}; // 抽象结构类:
// 1. 所有播放器都需要继承该来实现里面的所有接口
// 2. 主业务逻辑通过调用这里的接口, 即可控制播放器
class AudioPlayer
{ public: AudioPlayer() { } // 划重点, 如果该析构函数未以 virtual 声明, 那么在 new 子类对象转换为父类指针后再 delete, 子类的析构函数不会被调用
virtual ~AudioPlayer() { } // 设置事件监听
virtual int setEventMonitor(AudioPlayerEventMonitor *pMonitor) = 0; virtual int init(void *pargs) = 0; // 初始化
virtual int release() = 0; // 释放 virtual int play(const char *pAudioFile) = 0; // 播放指定音频文件或网络音频
virtual int reset() = 0; // 重新播放上一次指定的音频
virtual bool isPlaying() = 0; // 判断是否正在播放
virtual int pause() = 0; // 暂停播放
virtual int resume() = 0; // 恢复播放
virtual int stop() = 0; // 停止播放 virtual int setVolume(int volume) = 0; // 设置音量
virtual int getVolume() = 0; // 获取音量
}; #endif

以下为通过调用外部命令实现的具体播放器,继承上述接口(AudioPlayerInterface)来提供具体的实现。

/**
******************************************************************************
* @文件 AudioPlayerExternal.h
* @版本 V1.0.0
* @日期
* @概要 工厂设计模式示例代码 - 通过调用外部命令实现播放器
* @作者 lmx
* @邮箱 lovemengx@qq.com
* @博客 https://me.csdn.net/lovemengx
******************************************************************************
**/ #ifndef __AUDIO_PLAYER_EXTERNAL_H
#define __AUDIO_PLAYER_EXTERNAL_H #include <iostream>
#include <string>
#include "../AudioPlayerInterface.h" using namespace std; // 继承音频播放器接口并实现
class AudioPlayerExternal : public AudioPlayer
{
private: string mExternalCommand;
string mPlayerFile; AudioPlayerEventMonitor *mpAPEventMonitor = NULL; public: AudioPlayerExternal() { printf("AudioPlayerExternal()\n"); }
~AudioPlayerExternal() { printf("~AudioPlayerExternal()\n"); } // 设置事件监听
virtual int setEventMonitor(AudioPlayerEventMonitor* pAPEventMonitor) { mpAPEventMonitor = pAPEventMonitor; return 0; }; virtual int init(void *pExternalCommand) override; // 初始化
virtual int release() override; // 释放 virtual int play(const char* pAudioFile) override; // 播放指定音频文件或网络音频
virtual int reset() override; // 重新播放上一次指定的音频
virtual bool isPlaying() override; // 判断是否在播放
virtual int pause() override; // 暂停播放
virtual int resume() override; // 恢复播放
virtual int stop() override; // 停止播放 virtual int setVolume(int volume) override; // 设置音量
virtual int getVolume() override; // 获取音量
}; #endif
/**
******************************************************************************
* @文件 AudioPlayerExternal.cpp
* @版本 V1.0.0
* @日期
* @概要 工厂设计模式示例代码 - 通过调用外部命令实现播放器
* @作者 lmx
* @邮箱 lovemengx@qq.com
* @博客 https://me.csdn.net/lovemengx
******************************************************************************
**/ #include <iostream>
#include "AudioPlayerExternal.h" int AudioPlayerExternal::init(void *pExternalCommand)
{
printf("AudioPlayerExternal: init()...\n"); // 如果没有指定播放器, 那么默认为 tinyplayer
if(NULL == pExternalCommand)
pExternalCommand = (void*)"tinyplayer"; mExternalCommand = (char *)pExternalCommand;
return AUDIO_PLAYER_INTERFACE_RETURN_SUCCESS;
} int AudioPlayerExternal::release()
{
printf("AudioPlayerExternal: release()...\n");
return AUDIO_PLAYER_INTERFACE_RETURN_SUCCESS;
} int AudioPlayerExternal::play(const char* pAudioFile)
{
printf("AudioPlayerExternal: play()...\n");
return AUDIO_PLAYER_INTERFACE_RETURN_SUCCESS;
} int AudioPlayerExternal::reset()
{
printf("AudioPlayerExternal: reset()...\n");
return AUDIO_PLAYER_INTERFACE_RETURN_SUCCESS;
} bool AudioPlayerExternal::isPlaying()
{
printf("AudioPlayerExternal: isPlaying()...\n");
return AUDIO_PLAYER_INTERFACE_RETURN_SUCCESS;
} int AudioPlayerExternal::pause()
{
printf("AudioPlayerExternal: pause()...\n");
return AUDIO_PLAYER_INTERFACE_RETURN_SUCCESS;
} int AudioPlayerExternal::resume()
{
printf("AudioPlayerExternal: resume()...\n");
return AUDIO_PLAYER_INTERFACE_RETURN_SUCCESS;
} int AudioPlayerExternal::stop()
{
printf("AudioPlayerExternal: stop()...\n");
return AUDIO_PLAYER_INTERFACE_RETURN_SUCCESS;
} int AudioPlayerExternal::setVolume(int volume)
{
printf("AudioPlayerExternal: setVolume() volume:%d ...\n", volume);
return AUDIO_PLAYER_INTERFACE_RETURN_SUCCESS;
} int AudioPlayerExternal::getVolume()
{
printf("AudioPlayerExternal: getVolume() ...\n");
return AUDIO_PLAYER_INTERFACE_RETURN_SUCCESS;
}

以下为通过调用 mplayer 实现的具体播放器,也是通过继承接口(AudioPlayerInterface)来提供具体的实现。

/**
******************************************************************************
* @文件 AudioPlayerMPlayer.h
* @版本 V1.0.0
* @日期
* @概要 工厂设计模式示例代码 - 通过 MPlayer 实现播放器
* @作者 lmx
* @邮箱 lovemengx@qq.com
* @博客 https://me.csdn.net/lovemengx
******************************************************************************
**/ #ifndef __AUDIO_PLAYER_MPLAYER_H
#define __AUDIO_PLAYER_MPLAYER_H #include <iostream>
#include <string>
#include "../AudioPlayerInterface.h" using namespace std; // 继承音频播放器接口并实现
class AudioPlayerMPlayer : public AudioPlayer
{
private: string mPlayerFile;
AudioPlayerEventMonitor *mpAPEventMonitor = NULL; public: AudioPlayerMPlayer() { printf("AudioPlayerMPlayer()\n"); }
~AudioPlayerMPlayer() { printf("~AudioPlayerMPlayer()\n"); } // 设置事件监听
virtual int setEventMonitor(AudioPlayerEventMonitor* pAPEventMonitor) { mpAPEventMonitor = pAPEventMonitor; return 0; }; virtual int init(void* pExternalCommand) override; // 初始化
virtual int release() override; // 释放 virtual int play(const char* pAudioFile) override; // 播放指定音频文件或网络音频
virtual int reset() override; // 重新播放上一次指定的音频
virtual bool isPlaying() override; // 判断是否在播放
virtual int pause() override; // 暂停播放
virtual int resume() override; // 恢复播放
virtual int stop() override; // 停止播放 virtual int setVolume(int volume) override; // 设置音量
virtual int getVolume() override; // 获取音量
}; #endif
/**
******************************************************************************
* @文件 AudioPlayerMPlayer.cpp
* @版本 V1.0.0
* @日期
* @概要 工厂设计模式示例代码 - 通过 MPlayer 实现播放器(这里仅用于理解工厂设计模式)
* @作者 lmx
* @邮箱 lovemengx@qq.com
* @博客 https://me.csdn.net/lovemengx
******************************************************************************
**/ #include <iostream>
#include "AudioPlayerMPlayer.h" int AudioPlayerMPlayer::init(void* pMPlayerCommand)
{
printf("AudioPlayerMPlayer: init()...\n");
return AUDIO_PLAYER_INTERFACE_RETURN_SUCCESS;
} int AudioPlayerMPlayer::release()
{
printf("AudioPlayerMPlayer: release()...\n");
return AUDIO_PLAYER_INTERFACE_RETURN_SUCCESS;
} int AudioPlayerMPlayer::play(const char* pAudioFile)
{
printf("AudioPlayerMPlayer: play()...\n");
return AUDIO_PLAYER_INTERFACE_RETURN_SUCCESS;
} int AudioPlayerMPlayer::reset()
{
printf("AudioPlayerMPlayer: reset()...\n");
return AUDIO_PLAYER_INTERFACE_RETURN_SUCCESS;
} bool AudioPlayerMPlayer::isPlaying()
{
printf("AudioPlayerMPlayer: isPlaying()...\n");
return AUDIO_PLAYER_INTERFACE_RETURN_SUCCESS;
} int AudioPlayerMPlayer::pause()
{
printf("AudioPlayerMPlayer: pause()...\n");
return AUDIO_PLAYER_INTERFACE_RETURN_SUCCESS;
} int AudioPlayerMPlayer::resume()
{
printf("AudioPlayerMPlayer: resume()...\n");
return AUDIO_PLAYER_INTERFACE_RETURN_SUCCESS;
} int AudioPlayerMPlayer::stop()
{
printf("AudioPlayerMPlayer: stop()...\n");
return AUDIO_PLAYER_INTERFACE_RETURN_SUCCESS;
} int AudioPlayerMPlayer::setVolume(int volume)
{
printf("AudioPlayerMPlayer: setVolume() volume:%d ...\n", volume);
return AUDIO_PLAYER_INTERFACE_RETURN_SUCCESS;
} int AudioPlayerMPlayer::getVolume()
{
printf("AudioPlayerMPlayer: getVolume() ...\n");
return 0;
}

单元测试代码:具体关注 interfaceTest()

/**
******************************************************************************
* @文件 test_AudioPlayer.h
* @版本 V1.0.0
* @日期
* @概要 工厂设计模式单元测试
* @作者 lmx
* @邮箱 lovemengx@qq.com
* @博客 https://me.csdn.net/lovemengx
******************************************************************************
**/ #include "AudioPlayerInterface.h" // 纯接口测试需要用到
#include "player/AudioPlayerMPlayer.h"
#include "player/AudioPlayerExternal.h" class TestAudioPlayer : public AudioPlayerEventMonitor
{
private: // 通过 AudioPlayerEventMonitor 继承
virtual void onAudioPlayerEventMonitor(TAudioPlayerEvent* pEvent) override;
void audioPlayerOps(AudioPlayer* pAudioPlayer); // 音频播放器接口测试
void interfaceTest(void); // 纯接口测试 public: void test(void); };
/**
******************************************************************************
* @文件 test_AudioPlayer.cpp
* @版本 V1.0.0
* @日期
* @概要 工厂设计模式单元测试
* @作者 lmx
* @邮箱 lovemengx@qq.com
* @博客 https://me.csdn.net/lovemengx
******************************************************************************
**/
#include <iostream>
#include "test_AudioPlayer.h" // 播放器事件状态回调通知
void TestAudioPlayer::onAudioPlayerEventMonitor(TAudioPlayerEvent* pEvent)
{ } // 音频播放器操作
void TestAudioPlayer::audioPlayerOps(AudioPlayer* pAudioPlayer)
{
pAudioPlayer->setEventMonitor(this);
pAudioPlayer->init(NULL);
pAudioPlayer->setVolume(15);
pAudioPlayer->play("test.mp3");
pAudioPlayer->reset();
pAudioPlayer->pause();
pAudioPlayer->resume();
pAudioPlayer->stop();
pAudioPlayer->release(); return ;
} // 接口测试(业务逻辑代码中会暴露具体实现)
void TestAudioPlayer::interfaceTest(void)
{
AudioPlayer* pAudioPlayer = NULL; // 测试以 mplayer 为基础实现的播放器
puts("-----------------------------------------------------------------------");
pAudioPlayer = new AudioPlayerMPlayer();
audioPlayerOps(pAudioPlayer);
delete pAudioPlayer; // 测试以调用外部命令的方式实现的播放器
puts("-----------------------------------------------------------------------");
pAudioPlayer = new AudioPlayerExternal();
audioPlayerOps(pAudioPlayer);
delete pAudioPlayer; puts("-----------------------------------------------------------------------");
} void TestAudioPlayer::test(void)
{
puts("interface test ...");
interfaceTest(); return;
}

执行结果

interface test ...
-----------------------------------------------------------------------
AudioPlayerMPlayer()
AudioPlayerMPlayer: init()...
AudioPlayerMPlayer: setVolume() volume:15 ...
AudioPlayerMPlayer: play()...
AudioPlayerMPlayer: reset()...
AudioPlayerMPlayer: pause()...
AudioPlayerMPlayer: resume()...
AudioPlayerMPlayer: stop()...
AudioPlayerMPlayer: release()...
~AudioPlayerMPlayer()
-----------------------------------------------------------------------
AudioPlayerExternal()
AudioPlayerExternal: init()...
AudioPlayerExternal: setVolume() volume:15 ...
AudioPlayerExternal: play()...
AudioPlayerExternal: reset()...
AudioPlayerExternal: pause()...
AudioPlayerExternal: resume()...
AudioPlayerExternal: stop()...
AudioPlayerExternal: release()...
~AudioPlayerExternal()
-----------------------------------------------------------------------

通过 TestAudioPlayer 单元测试类可以发现,具体的播放器暴露在业务逻辑上了,违反设计模式六大原则中的 “迪米特法则”(又称最少知道原则),因此需要再封装一层简单的创建、释放接口,来屏蔽具体播放器对象的创建细节,这就是传说中的 “简单工厂模式” 。

简单工厂模式

定义一个 AudioManager 类,来作为具体播放器类对象的创建和释放。业务逻辑通过 AudioManager 来创建和释放具体的播放器类对象。命名空间 simple 的定义是为了方便后续增加 “工厂方法模式” 示例,与本文介绍的工厂模式无关。

/**
******************************************************************************
* @文件 AudioManager.h
* @版本 V1.0.0
* @日期
* @概要 工厂设计模式示例代码 - 音频管理类的实现,用于创建和销毁指定类型的播放器
* @作者 lmx
* @邮箱 lovemengx@qq.com
* @博客 https://me.csdn.net/lovemengx
******************************************************************************
**/
#ifndef __AUDIO_MANAGER_H
#define __AUDIO_MANAGER_H #include "AudioPlayerInterface.h" namespace simple { class AudioManager
{
public: typedef enum {
AUDIO_PLAYER_TYPE_EXTERNAL, // 外部命令扩展实现播放器
AUDIO_PLAYER_TYPE_MPLAYER, // 封装 mplayer 实现播放器
}AUDIO_PLAYER_TYPE_E; public: AudioPlayer *create(AUDIO_PLAYER_TYPE_E type); // 创建指定类型的播放器
int destroy(AudioPlayer **ppAudioPlayer); // 释放播放器 }; } #endif
/**
******************************************************************************
* @文件 AudioManager.cpp
* @版本 V1.0.0
* @日期
* @概要 工厂设计模式示例代码 - 音频管理类的实现,用于创建和销毁指定类型的播放器
* @作者 lmx
* @邮箱 lovemengx@qq.com
* @博客 https://me.csdn.net/lovemengx
******************************************************************************
**/ #include "AudioMenager.h"
#include "../player/AudioPlayerExternal.h"
#include "../player/AudioPlayerMPlayer.h" using namespace simple; AudioPlayer *AudioManager::create(AUDIO_PLAYER_TYPE_E type)
{
AudioPlayer *pAudioPlayer = NULL; printf("namespace simple: AudioManager->create()...\n");
switch (type)
{
case AUDIO_PLAYER_TYPE_EXTERNAL:
pAudioPlayer = new AudioPlayerExternal();
break; case AUDIO_PLAYER_TYPE_MPLAYER:
pAudioPlayer = new AudioPlayerMPlayer();
break; default:
return NULL;
} return pAudioPlayer;
} int AudioManager::destroy(AudioPlayer **ppAudioPlayer)
{
printf("namespace simple: AudioManager->destroy()...\n"); if (NULL == ppAudioPlayer || NULL == *ppAudioPlayer)
return -1; delete (*ppAudioPlayer);
*ppAudioPlayer = nullptr; return 0;
}

单元测试代码:具体关注 simpleTest()

/**
******************************************************************************
* @文件 test_AudioPlayer.h
* @版本 V1.0.0
* @日期
* @概要 工厂设计模式单元测试
* @作者 lmx
* @邮箱 lovemengx@qq.com
* @博客 https://me.csdn.net/lovemengx
******************************************************************************
**/ #include "AudioPlayerInterface.h" // 纯接口测试需要用到
#include "player/AudioPlayerMPlayer.h"
#include "player/AudioPlayerExternal.h" // 简单工厂模式需要用到
#include "../Factory/simple/AudioMenager.h" class TestAudioPlayer : public AudioPlayerEventMonitor
{
private: // 通过 AudioPlayerEventMonitor 继承
virtual void onAudioPlayerEventMonitor(TAudioPlayerEvent* pEvent) override;
void audioPlayerOps(AudioPlayer* pAudioPlayer); // 音频播放器接口测试 void interfaceTest(void); // 纯接口测试
void simpleTest(void); // 简单工厂模式测试 public: void test(void); };
/**
******************************************************************************
* @文件 test_AudioPlayer.cpp
* @版本 V1.0.0
* @日期
* @概要 工厂设计模式单元测试
* @作者 lmx
* @邮箱 lovemengx@qq.com
* @博客 https://me.csdn.net/lovemengx
******************************************************************************
**/
#include <iostream>
#include "test_AudioPlayer.h" // 播放器事件状态回调通知
void TestAudioPlayer::onAudioPlayerEventMonitor(TAudioPlayerEvent* pEvent)
{ } // 音频播放器操作
void TestAudioPlayer::audioPlayerOps(AudioPlayer* pAudioPlayer)
{
pAudioPlayer->setEventMonitor(this);
pAudioPlayer->init(NULL);
pAudioPlayer->setVolume(15);
pAudioPlayer->play("test.mp3");
pAudioPlayer->reset();
pAudioPlayer->pause();
pAudioPlayer->resume();
pAudioPlayer->stop();
pAudioPlayer->release(); return ;
} // 接口测试(业务逻辑代码中会暴露具体实现)
void TestAudioPlayer::interfaceTest(void)
{
AudioPlayer* pAudioPlayer = NULL; // 测试以 mplayer 为基础实现的播放器
puts("-----------------------------------------------------------------------");
pAudioPlayer = new AudioPlayerMPlayer();
audioPlayerOps(pAudioPlayer);
delete pAudioPlayer; // 测试以调用外部命令的方式实现的播放器
puts("-----------------------------------------------------------------------");
pAudioPlayer = new AudioPlayerExternal();
audioPlayerOps(pAudioPlayer);
delete pAudioPlayer; puts("-----------------------------------------------------------------------");
} // 简单工厂设计模式测试(虽然不会暴露具体实现,但是每次增加一个具体实现都需要修改原有AudioManager代码)
void TestAudioPlayer::simpleTest(void)
{
simple::AudioManager mAudioManager;
AudioPlayer *pAudioPlayer = NULL; // 测试以 mplayer 为基础实现的播放器
puts("-----------------------------------------------------------------------");
pAudioPlayer = mAudioManager.create(simple::AudioManager::AUDIO_PLAYER_TYPE_MPLAYER);
audioPlayerOps(pAudioPlayer);
mAudioManager.destroy(&pAudioPlayer); // 测试以调用外部命令的方式实现的播放器
puts("-----------------------------------------------------------------------");
pAudioPlayer = mAudioManager.create(simple::AudioManager::AUDIO_PLAYER_TYPE_EXTERNAL);
audioPlayerOps(pAudioPlayer);
mAudioManager.destroy(&pAudioPlayer); puts("-----------------------------------------------------------------------");
} void TestAudioPlayer::test(void)
{
puts("interface test ...");
interfaceTest(); puts("simple factort mode test ...");
simpleTest(); return;
}

执行结果

interface test ...
-----------------------------------------------------------------------
AudioPlayerMPlayer()
AudioPlayerMPlayer: init()...
AudioPlayerMPlayer: setVolume() volume:15 ...
AudioPlayerMPlayer: play()...
AudioPlayerMPlayer: reset()...
AudioPlayerMPlayer: pause()...
AudioPlayerMPlayer: resume()...
AudioPlayerMPlayer: stop()...
AudioPlayerMPlayer: release()...
~AudioPlayerMPlayer()
-----------------------------------------------------------------------
AudioPlayerExternal()
AudioPlayerExternal: init()...
AudioPlayerExternal: setVolume() volume:15 ...
AudioPlayerExternal: play()...
AudioPlayerExternal: reset()...
AudioPlayerExternal: pause()...
AudioPlayerExternal: resume()...
AudioPlayerExternal: stop()...
AudioPlayerExternal: release()...
~AudioPlayerExternal()
-----------------------------------------------------------------------
simple factort mode test ...
-----------------------------------------------------------------------
namespace simple: AudioManager->create()...
AudioPlayerMPlayer()
AudioPlayerMPlayer: init()...
AudioPlayerMPlayer: setVolume() volume:15 ...
AudioPlayerMPlayer: play()...
AudioPlayerMPlayer: reset()...
AudioPlayerMPlayer: pause()...
AudioPlayerMPlayer: resume()...
AudioPlayerMPlayer: stop()...
AudioPlayerMPlayer: release()...
namespace simple: AudioManager->destroy()...
~AudioPlayerMPlayer()
-----------------------------------------------------------------------
namespace simple: AudioManager->create()...
AudioPlayerExternal()
AudioPlayerExternal: init()...
AudioPlayerExternal: setVolume() volume:15 ...
AudioPlayerExternal: play()...
AudioPlayerExternal: reset()...
AudioPlayerExternal: pause()...
AudioPlayerExternal: resume()...
AudioPlayerExternal: stop()...
AudioPlayerExternal: release()...
namespace simple: AudioManager->destroy()...
~AudioPlayerExternal()
-----------------------------------------------------------------------

从 AudioManager 类里面可以看到,创建何种类型的播放器,取决于 create() type 参数,这意味着每次新增加一个新的具体播放器实现类,就需要修改一次原有的 AudioManager 类定义,违反设计模式六大原则中的 “开闭原则”。因此引出工厂模式的进阶版 “工厂方法模式”。

工厂方法模式

通过将 AudioManager 进一步抽象化,每一个具体的播放器类都有一个 AudioManager 对应,需要使用哪个类,就实例化哪个 AudioManager 类。(AudioManager 命名在这里似乎不太合适)

AudioManager 不再实现具体的对象创建:

/**
******************************************************************************
* @文件 AudioManager.h
* @版本 V1.0.0
* @日期
* @概要 工厂设计模式示例代码 - 音频管理类的实现,用于创建和销毁指定类型的播放器
* @作者 lmx
* @邮箱 lovemengx@qq.com
* @博客 https://me.csdn.net/lovemengx
******************************************************************************
**/
#ifndef __AUDIO_MANAGER_METHOD_H
#define __AUDIO_MANAGER_METHOD_H #include "../AudioPlayerInterface.h" namespace method { class AudioManager
{
public: virtual AudioPlayer* create() = 0; // 创建指定类型的播放器
virtual int destroy(AudioPlayer **ppAudioPlayer) = 0; // 释放播放器 }; } #endif

MPlayer 播放器的 AudioManager:

/**
******************************************************************************
* @文件 AudioManagerMPlayer.h
* @版本 V1.0.0
* @日期
* @概要 工厂设计模式示例代码 - 音频管理类的实现,用于创建和销毁指定类型的播放器(工厂方法设计模式)
* @作者 lmx
* @邮箱 lovemengx@qq.com
* @博客 https://me.csdn.net/lovemengx
******************************************************************************
**/
#ifndef __AUDIO_MANAGER_MPLAYER_H
#define __AUDIO_MANAGER_MPLAYER_H #include "../AudioPlayerInterface.h"
#include "AudioMenager.h" class AudioManagerMPlayer : public method::AudioManager
{
public: virtual AudioPlayer* create() override; // 创建指定类型的播放器
virtual int destroy(AudioPlayer **ppAudioPlayer) override; // 释放播放器 }; #endif
/**
******************************************************************************
* @文件 AudioManagerMPlayer.cpp
* @版本 V1.0.0
* @日期
* @概要 工厂设计模式示例代码 - 音频管理类的实现,用于创建和销毁指定类型的播放器(工厂方法设计模式)
* @作者 lmx
* @邮箱 lovemengx@qq.com
* @博客 https://me.csdn.net/lovemengx
******************************************************************************
**/ #include "../player/AudioPlayerExternal.h"
#include "../player/AudioPlayerMPlayer.h"
#include "AudioMenager.h"
#include "AudioMenagerMPlayer.h" AudioPlayer* AudioManagerMPlayer::create()
{
printf("namespace method: AudioManagerMPlayer->create()...\n");
return new AudioPlayerMPlayer();
} int AudioManagerMPlayer::destroy(AudioPlayer **ppAudioPlayer)
{
printf("namespace method: AudioManagerMPlayer->destroy()...\n"); if (NULL == ppAudioPlayer || NULL == *ppAudioPlayer)
return -1; delete (*ppAudioPlayer);
*ppAudioPlayer = nullptr; return 0;
}

调用外部命令实现播放器的 AudioManager:

/**
******************************************************************************
* @文件 AudioManagerExternal.h
* @版本 V1.0.0
* @日期
* @概要 工厂设计模式示例代码 - 音频管理类的实现,用于创建和销毁指定类型的播放器(工厂方法设计模式)
* @作者 lmx
* @邮箱 lovemengx@qq.com
* @博客 https://me.csdn.net/lovemengx
******************************************************************************
**/
#ifndef __AUDIO_MANAGER_EXTERNALE_H
#define __AUDIO_MANAGER_EXTERNALE_H #include "../AudioPlayerInterface.h"
#include "AudioMenager.h" class AudioManagerExternal : public method::AudioManager
{
public: virtual AudioPlayer* create() override; // 创建指定类型的播放器
virtual int destroy(AudioPlayer **ppAudioPlayer) override; // 释放播放器 }; #endif
/**
******************************************************************************
* @文件 AudioManagerExternal.cpp
* @版本 V1.0.0
* @日期
* @概要 工厂设计模式示例代码 - 音频管理类的实现,用于创建和销毁指定类型的播放器(工厂方法设计模式)
* @作者 lmx
* @邮箱 lovemengx@qq.com
* @博客 https://me.csdn.net/lovemengx
******************************************************************************
**/ #include "../player/AudioPlayerExternal.h"
#include "../player/AudioPlayerMPlayer.h"
#include "AudioMenager.h"
#include "AudioMenagerExternal.h" AudioPlayer* AudioManagerExternal::create()
{
printf("namespace method: AudioManagerExternal->create()...\n");
return new AudioPlayerExternal();
} int AudioManagerExternal::destroy(AudioPlayer **ppAudioPlayer)
{
printf("namespace method: AudioManagerExternal->destroy()...\n"); if (NULL == ppAudioPlayer || NULL == *ppAudioPlayer)
return -1; delete (*ppAudioPlayer);
*ppAudioPlayer = nullptr; return 0;
}

单元测试代码

/**
******************************************************************************
* @文件 test_AudioPlayer.h
* @版本 V1.0.0
* @日期
* @概要 工厂设计模式单元测试
* @作者 lmx
* @邮箱 lovemengx@qq.com
* @博客 https://me.csdn.net/lovemengx
******************************************************************************
**/ #include "AudioPlayerInterface.h" // 纯接口测试需要用到
#include "player/AudioPlayerMPlayer.h"
#include "player/AudioPlayerExternal.h" // 简单工厂模式需要用到
#include "../Factory/simple/AudioMenager.h" // 工厂模式方法需要用到
#include "method/AudioMenager.h"
#include "../Factory/method/AudioMenagerMPlayer.h"
#include "../Factory/method/AudioMenagerExternal.h" class TestAudioPlayer : public AudioPlayerEventMonitor
{
private: void audioPlayerOps(AudioPlayer* pAudioPlayer); // 音频播放器接口测试 void interfaceTest(void); // 纯接口测试
void simpleTest(void); // 简单工厂模式测试
void methodTest(void); // 工厂方法模式测试 public: void test(void); // 通过 AudioPlayerEventMonitor 继承
virtual void onAudioPlayerEventMonitor(TAudioPlayerEvent* pEvent) override; };
/**
******************************************************************************
* @文件 test_AudioPlayer.cpp
* @版本 V1.0.0
* @日期
* @概要 工厂设计模式单元测试
* @作者 lmx
* @邮箱 lovemengx@qq.com
* @博客 https://me.csdn.net/lovemengx
******************************************************************************
**/
#include <iostream>
#include "test_AudioPlayer.h" // 播放器事件状态回调通知
void TestAudioPlayer::onAudioPlayerEventMonitor(TAudioPlayerEvent* pEvent)
{ } // 音频播放器操作
void TestAudioPlayer::audioPlayerOps(AudioPlayer* pAudioPlayer)
{
pAudioPlayer->setEventMonitor(this);
pAudioPlayer->init(NULL);
pAudioPlayer->setVolume(15);
pAudioPlayer->play("test.mp3");
pAudioPlayer->reset();
pAudioPlayer->pause();
pAudioPlayer->resume();
pAudioPlayer->stop();
pAudioPlayer->release(); return ;
} // 接口测试(业务逻辑代码中会暴露具体实现)
void TestAudioPlayer::interfaceTest(void)
{
AudioPlayer* pAudioPlayer = NULL; // 测试以 mplayer 为基础实现的播放器
puts("-----------------------------------------------------------------------");
pAudioPlayer = new AudioPlayerMPlayer();
audioPlayerOps(pAudioPlayer);
delete pAudioPlayer; // 测试以调用外部命令的方式实现的播放器
puts("-----------------------------------------------------------------------");
pAudioPlayer = new AudioPlayerExternal();
audioPlayerOps(pAudioPlayer);
delete pAudioPlayer; puts("-----------------------------------------------------------------------");
} // 简单工厂设计模式测试(虽然不会暴露具体实现,但是每次增加一个具体实现都需要修改原有AudioManager代码)
void TestAudioPlayer::simpleTest(void)
{
simple::AudioManager mAudioManager;
AudioPlayer *pAudioPlayer = NULL; // 测试以 mplayer 为基础实现的播放器
puts("-----------------------------------------------------------------------");
pAudioPlayer = mAudioManager.create(simple::AudioManager::AUDIO_PLAYER_TYPE_MPLAYER);
audioPlayerOps(pAudioPlayer);
mAudioManager.destroy(&pAudioPlayer); // 测试以调用外部命令的方式实现的播放器
puts("-----------------------------------------------------------------------");
pAudioPlayer = mAudioManager.create(simple::AudioManager::AUDIO_PLAYER_TYPE_EXTERNAL);
audioPlayerOps(pAudioPlayer);
mAudioManager.destroy(&pAudioPlayer); puts("-----------------------------------------------------------------------");
} // 工厂方法设计模式测试(虽然每次增加一个具体实现都不需要修改原有代码,但是却需要多增加一个工厂类)
void TestAudioPlayer::methodTest(void)
{
method::AudioManager *pAudioManager = NULL;
AudioPlayer *pAudioPlayer = NULL; // 测试以 mplayer 为基础实现的播放器
puts("-----------------------------------------------------------------------");
pAudioManager = new AudioManagerMPlayer();
pAudioPlayer = pAudioManager->create();
audioPlayerOps(pAudioPlayer);
pAudioManager->destroy(&pAudioPlayer);
delete pAudioManager; // 测试以调用外部命令的方式实现的播放器
puts("-----------------------------------------------------------------------");
pAudioManager = new AudioManagerExternal();
pAudioPlayer = pAudioManager->create();
audioPlayerOps(pAudioPlayer);
pAudioManager->destroy(&pAudioPlayer);
delete pAudioManager; puts("-----------------------------------------------------------------------"); } void TestAudioPlayer::test(void)
{
puts("interface test ...");
interfaceTest(); puts("simple factort mode test ...");
simpleTest(); puts("method factort mode test ...");
methodTest();
return;
}

执行结果

interface test ...
-----------------------------------------------------------------------
AudioPlayerMPlayer()
AudioPlayerMPlayer: init()...
AudioPlayerMPlayer: setVolume() volume:15 ...
AudioPlayerMPlayer: play()...
AudioPlayerMPlayer: reset()...
AudioPlayerMPlayer: pause()...
AudioPlayerMPlayer: resume()...
AudioPlayerMPlayer: stop()...
AudioPlayerMPlayer: release()...
~AudioPlayerMPlayer()
-----------------------------------------------------------------------
AudioPlayerExternal()
AudioPlayerExternal: init()...
AudioPlayerExternal: setVolume() volume:15 ...
AudioPlayerExternal: play()...
AudioPlayerExternal: reset()...
AudioPlayerExternal: pause()...
AudioPlayerExternal: resume()...
AudioPlayerExternal: stop()...
AudioPlayerExternal: release()...
~AudioPlayerExternal()
-----------------------------------------------------------------------
simple factort mode test ...
-----------------------------------------------------------------------
namespace simple: AudioManager->create()...
AudioPlayerMPlayer()
AudioPlayerMPlayer: init()...
AudioPlayerMPlayer: setVolume() volume:15 ...
AudioPlayerMPlayer: play()...
AudioPlayerMPlayer: reset()...
AudioPlayerMPlayer: pause()...
AudioPlayerMPlayer: resume()...
AudioPlayerMPlayer: stop()...
AudioPlayerMPlayer: release()...
namespace simple: AudioManager->destroy()...
~AudioPlayerMPlayer()
-----------------------------------------------------------------------
namespace simple: AudioManager->create()...
AudioPlayerExternal()
AudioPlayerExternal: init()...
AudioPlayerExternal: setVolume() volume:15 ...
AudioPlayerExternal: play()...
AudioPlayerExternal: reset()...
AudioPlayerExternal: pause()...
AudioPlayerExternal: resume()...
AudioPlayerExternal: stop()...
AudioPlayerExternal: release()...
namespace simple: AudioManager->destroy()...
~AudioPlayerExternal()
-----------------------------------------------------------------------
method factort mode test ...
-----------------------------------------------------------------------
namespace method: AudioManagerMPlayer->create()...
AudioPlayerMPlayer()
AudioPlayerMPlayer: init()...
AudioPlayerMPlayer: setVolume() volume:15 ...
AudioPlayerMPlayer: play()...
AudioPlayerMPlayer: reset()...
AudioPlayerMPlayer: pause()...
AudioPlayerMPlayer: resume()...
AudioPlayerMPlayer: stop()...
AudioPlayerMPlayer: release()...
namespace method: AudioManagerMPlayer->destroy()...
~AudioPlayerMPlayer()
-----------------------------------------------------------------------
namespace method: AudioManagerExternal->create()...
AudioPlayerExternal()
AudioPlayerExternal: init()...
AudioPlayerExternal: setVolume() volume:15 ...
AudioPlayerExternal: play()...
AudioPlayerExternal: reset()...
AudioPlayerExternal: pause()...
AudioPlayerExternal: resume()...
AudioPlayerExternal: stop()...
AudioPlayerExternal: release()...
namespace method: AudioManagerExternal->destroy()...
~AudioPlayerExternal()
-----------------------------------------------------------------------

从 AudioManagerMPlayer 和 AudioManagerExternal 类就可以发现,”工厂方法模式“ 的缺陷在于每次新增一个播放器都需要创建新的 AudioManagerXxxxxx 类,较为繁琐。

另一种方式

还有一种使用方式,那就是提供一个创建音频播放器的方法,隐藏具体的实现方式,业务逻辑只在需要的时候创建并使用,例如默认可以提供 Mplayer 的方式实现音频播放器。后续维护或者有其它的需求时,Mplayer不满足,那么就可以另外实现,然后只需要修改 AudioManager 类的 create() 接口,即可完成替换而不需要修改业务逻辑代码。

/**
******************************************************************************
* @文件 AudioManager.h
* @版本 V1.0.0
* @日期
* @概要 工厂设计模式示例代码 - 音频管理类的实现,用于创建和销毁音频播放器
* @作者 lmx
* @邮箱 lovemengx@qq.com
* @博客 https://me.csdn.net/lovemengx
******************************************************************************
**/
#ifndef __AUDIO_MANAGER_SIMPLE_H
#define __AUDIO_MANAGER_SIMPLE_H #include "../AudioPlayerInterface.h" class AudioManager
{ public: AudioPlayer* create(); // 创建播放器
int destroy(AudioPlayer **ppAudioPlayer); // 释放播放器 }; #endif /**
******************************************************************************
* @文件 AudioManager.cpp
* @版本 V1.0.0
* @日期
* @概要 工厂设计模式示例代码 - 音频管理类的实现,用于创建和销毁音频播放器
* @作者 lmx
* @邮箱 lovemengx@qq.com
* @博客 https://me.csdn.net/lovemengx
******************************************************************************
**/ #include "AudioMenager.h"
#include "../player/AudioPlayerExternal.h"
#include "../player/AudioPlayerMPlayer.h" AudioPlayer *AudioManager::create()
{
return new AudioPlayerMPlayer();;
} int AudioManager::destroy(AudioPlayer **ppAudioPlayer)
{
printf("namespace simple: AudioManager->destroy()...\n"); if (NULL == ppAudioPlayer || NULL == *ppAudioPlayer)
return -1; delete (*ppAudioPlayer);
*ppAudioPlayer = nullptr; return 0;
} void TestAudioPlayer::simpleTest(void)
{
simple::AudioManager mAudioManager;
AudioPlayer *pAudioPlayer = NULL; // 测试以 mplayer 为基础实现的播放器
puts("-----------------------------------------------------------------------");
pAudioPlayer = mAudioManager.create();
audioPlayerOps(pAudioPlayer);
mAudioManager.destroy(&pAudioPlayer);
puts("-----------------------------------------------------------------------");
}

【学习笔记】C/C++ 设计模式 - 工厂模式(上)的更多相关文章

  1. C#设计模式学习笔记:(3)抽象工厂模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7596897.html,记录一下学习过程以备后续查用. 一.引言 接上一篇C#设计模式学习笔记:简单工厂模式( ...

  2. Java设计模式学习笔记,二:工厂模式

    工厂模式,主要实现了创建者和调用者的分离. 分类:1.简单工厂模式:2.工厂方法模式:3.抽象工厂模式. 核心:实例化对象时,用工厂方法代替new操作. 一.简单工厂模式 也叫静态工厂模式,工厂类中实 ...

  3. 读书笔记之 - javascript 设计模式 - 工厂模式

    一个类或者对象中,往往会包含别的对象.在创建这种对象的时候,你可能习惯于使用常规方式,即用 new 关键字和类构造函数. 这会导致相关的俩个类之间产生依赖. 工厂模式,就是消除这俩个类之间的依赖性的一 ...

  4. .NET设计模式: 工厂模式

    .NET设计模式: 工厂模式(转) 转自:http://www.cnblogs.com/bit-sand/archive/2008/01/25/1053207.html   .NET设计模式(1): ...

  5. 【设计模式】Java设计模式 -工厂模式

    [设计模式]Java设计模式 -工厂模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 目 ...

  6. jquery源码学习笔记三:jQuery工厂剖析

    jquery源码学习笔记二:jQuery工厂 jquery源码学习笔记一:总体结构 上两篇说过,query的核心是一个jQuery工厂.其代码如下 function( window, noGlobal ...

  7. Flutter学习笔记(25)--ListView实现上拉刷新下拉加载

    如需转载,请注明出处:Flutter学习笔记(25)--ListView实现上拉刷新下拉加载 前面我们有写过ListView的使用:Flutter学习笔记(12)--列表组件,当列表的数据非常多时,需 ...

  8. Redis学习笔记八:集群模式

    作者:Grey 原文地址:Redis学习笔记八:集群模式 前面提到的Redis学习笔记七:主从复制和哨兵只能解决Redis的单点压力大和单点故障问题,接下来要讲的Redis Cluster模式,主要是 ...

  9. Laravel学习笔记(三)--在CentOS上配置Laravel

    在Laravel框架上开发了几天,不得不说,确实比较优雅,处理问题逻辑比较清楚.     今天打算在CentOS 7上配置一个Laravel,之前都是在本机上开发,打算实际配置一下.     1)系统 ...

  10. 并发编程学习笔记(9)----AQS的共享模式源码分析及CountDownLatch使用及原理

    1. AQS共享模式 前面已经说过了AQS的原理及独享模式的源码分析,今天就来学习共享模式下的AQS的几个接口的源码. 首先还是从顶级接口acquireShared()方法入手: public fin ...

随机推荐

  1. numpy常用知识点备忘

    常用函数 a.max(axis=0) a.max(axis=1) a.argmax(axis=1) : 每列的最大值(在行方向找最大值).每行的最大值(在列方向找对大致).最大值的坐标 sum()求和 ...

  2. 19_Vue如何监测到对象类型数据发生改变的?

    数据更新 关于监视 我们之前讲过,我们在data当中配置的属性,最终会挂载在vue实例身上,而data这个配置项,最终也会在vue身上成为一个新的属性 == _data 当我们在页面DOM当中,去使用 ...

  3. 我的Spark学习笔记

    一.架构设计 Driver根据用户代码构建计算流图,拆解出分布式任务并分发到 Executors 中去:每个Executors收到任务,然后处理这个 RDD 的一个数据分片子集 DAGSchedule ...

  4. ES 客户端 RestHighLevelClient Connection reset by peer 亲测有效 2022-11-05

    导读 最新公司ES集群老出现连接关闭,进而导致查询|写入ES时报错,报错日志显示如下 [2m2022-10-23 14:13:10.088[0;39m - [31mERROR[0;39m - [35m ...

  5. vue 数组更新(push【可用】,$set,slice,filter,map【都属于浅拷贝】)问题

    this.$axios.post('https://....php',this.$qs.stringify({ user: 'suess' })) .then(res => { this.dat ...

  6. 嵌入式-C语言基础:指针是存放变量的地址,那为什么要区分类型?

    指针是存放变量的地址,那为什么要区分类型?不能所有类型的变量都用一个类型吗?下面用一个例子来说明这个问题. #include<stdio.h> int main() { int a=0x1 ...

  7. OpenCvSharp的安装和使用

    OpencvSharp是opencv的C#版本,使用习惯了opencv的人学起OpenCvSharp会很容易上手,看了网上很多的安装方式,最后我感觉还是自己去下载安装包的方式最简单,通过Nuget的方 ...

  8. 2022春每日一题:Day 40

    题目:[NOI2010] 超级钢琴 前求出美妙值的前缀和,然后倍增处理一下前缀和的最大值,然后对于一个左端点s,他能取到右端点的只有s+l到s+r,而他的最大贡献就是s+l 到s+r的最大子段和,因此 ...

  9. 2022春每日一题:Day 28

    题目:最大上升子序列和 就是最长上升子序列的改版,贡献由1改为a[i]其他全部不变 代码: #include <cstdio> #include <cstdlib> #incl ...

  10. 【云原生 · Kubernetes】Kubernetes 编排部署GPMall(一)

    1.规划节点 IP 主机名 节点 10.24.2.156 master Kubernetes master 节点 10.24.2.157 node Kubernetes worker 节点 2.基础准 ...