BASIC OOP ~Vol.3~

BASIC OOP ~Vol.3~

Adapter

概要

  • 互換性の無いインターフェースを適応させる

解説

モジュールを扱うスーパークラスの定義です。

//	モジュールを扱うスーパークラス
class IModule
{
protected:
	IModule() = default;
public:
	virtual ~IModule() = default;
public:
	virtual bool Load() = 0;
	virtual void Unload() = 0;
};

継承しモジュールを扱うサブクラスの定義を行いました。

//	モジュールを扱うサブクラス
class CModule final : public IModule
{
public:
	CModule(const char* path) : IModule(), m_stPath(path){}
	virtual ~CModule() = default;
public:
	virtual bool Load()
	{
		//	m_stPathのモジュールをロード
		std::cout << "Module:" << m_stPath.c_str() << ":Load" << std::endl;
		return true;
	}
	virtual void Unload()
	{
		//	アンロードする
		std::cout << "Module:" << m_stPath.c_str() << ":Unload" << std::endl;
	}
private:
	std::string m_stPath;
};

そしてそれらを管理するクラスを定義してみます。

//	システム
class CSystem final
{
public:
	CSystem() : m_Modules() {}
	~CSystem() { UnloadModules(); }
public:
	//	登録されたモジュールの一括ロード
	bool LoadModules()
	{
		bool result = true;
		for (auto it = m_Modules.begin();
			it != m_Modules.end();
			++it)
		{
			result &= (*it)->Load();
		}
		return result;
	}
	//	登録されたモジュールの一括アンロード
	void UnloadModules()
	{
		for (auto it = m_Modules.begin();
			it != m_Modules.end();
			++it)
		{
			(*it)->Unload();
			delete (*it);
		}
		m_Modules.clear();
	}
public:
	//	モジュールの登録
	void AppendModule(IModule* pModule)
	{
		m_Modules.push_back(pModule);
	}
private:
	std::list<IModule*> m_Modules;
};

上記のシステムに対しミドルウェアを採用して機能拡張を行いたいのですが
提供されたライブラリには独自のモジュールを扱うクラス定義がありました。

//	外部提供されたモジュールを扱うクラス
class CExternalModule
{
public:
	CExternalModule() = default;
	~CExternalModule() = default;
public:
	//	(本来は実装は見えない):ロードを行うらしい
	bool Create() {	return true; }
	//	(本来は実装は見えない):アンロードを行うらしい
	void Destroy() {}
	//	(本来は実装は見えない):Create後に呼ぶ必要があるらしい
	bool Initialize() { return true; }
	//	(本来は実装は見えない):Destroy前に呼ぶ必要があるらしい
	void Finalize() {}
};

既存のモジュールを扱うクラスとはメソッド名も異なりますし
同様の挙動を得る為に行う手順も異なります。

  • 既存のシステムを変更したくない
  • 組み込むインターフェースを変更出来ない

こういったケースであればAdapterパターンを検討してみます。
大きく分けて以下の2パターンです。

1.内部インスタンス保持型

//	内部にインスタンスを持つパターン
class CExternalModuleAdapter0 final : public IModule
{
public:
	CExternalModuleAdapter0() : IModule(), m_exModule(){}
	virtual ~CExternalModuleAdapter0() = default;
public:
	virtual bool Load()
	{
		std::cout << "ExternalModule0:Load" << std::endl;
		return m_exModule.Create() && m_exModule.Initialize();
	}
	virtual void Unload()
	{
		std::cout << "ExternalModule0:Unload" << std::endl;
		m_exModule.Finalize();
		m_exModule.Destroy();
	}
private:
	CExternalModule m_exModule;
};

オーソドックスなパターンとしてはこちらになります。

2.多重継承型

//	多重継承を行うパターン
class CExternalModuleAdapter1 final : public CExternalModule, public IModule
{
public:
	CExternalModuleAdapter1() : CExternalModule(), IModule(){}
	virtual ~CExternalModuleAdapter1() = default;
public:
	virtual bool Load()
	{
		std::cout << "ExternalModule1:Load" << std::endl;
		return CExternalModule::Create() && CExternalModule::Initialize();
	}
	virtual void Unload()
	{
		std::cout << "ExternalModule1:Unload" << std::endl;
		CExternalModule::Finalize();
		CExternalModule::Destroy();
	}
};
  • 多重継承は高リスクを伴う
  • 提供されたインターフェースは継承出来ないケースが多い

との理由から少ない傾向ですが継承関係を持っているので
外部から提供されたスーパークラスと同様に扱えるメリットがあります。

int main()
{
	CSystem system;
	//
	system.AppendModule(new CModule("sample0"));
	system.AppendModule(new CModule("sample1"));
	system.AppendModule(new CExternalModuleAdapter0());
	system.AppendModule(new CExternalModuleAdapter1());
	//
	system.LoadModules();
	//
	system.UnloadModules();
	//
	return EXIT_SUCCESS;
}

実行結果

Module:sample0:Load
Module:sample1:Load
ExternalModule0:Load
ExternalModule1:Load
Module:sample0:Unload
Module:sample1:Unload
ExternalModule0:Unload
ExternalModule1:Unload

サブクラスの実装だけで既存のシステムに組み込みました。

前回の記事※1・Template Method にてサンプルとして挙げた
「ログファイルを出力する~」のインターフェースも
実はAdapterパターンの要素を含んでいます。

使える場面は多いと思いますので是非活用して下さい。

脚注

↑ 1. ・Template Method

Tags: