Effective C++ #35 の内容です.この項はちょっと一風変わっていて,継承と仮想関数の仕組みを用いたシンプルなポリモーフィズムの実装を,異なるデザインパターンでおきかえる話でした.登場したデザインパターンとしては,
1.非仮想のインターフェースを使うテンプレートメソッドパターン
2.関数ポインタを使うストラテジパターン
3.tr1::functionによるストラテジパターン
4.古典的なストラテジパターン
の4つでした.サンプルコードを通して,4つのパターンを見ていきたいと思います.
0.シンプルに仮想関数を使って実装してみる.
下記,Lidarを持つロボットとCameraを持つロボットで環境認識の方法が変わるので,Robotという基底クラスに対してLidarRobot, CameraRobotというクラスを派生させています.う〜ん.我ながら,このクラス設計はいけてないですね(笑).ストラテジパターンを使えばうまくまとまるのかしら.
#include <iostream> class Robot { public: virtual ~Robot(){} virtual double sense() const = 0; }; class CameraRobot : public Robot { public: virtual ~CameraRobot(){} virtual double sense() const { std::cout << "Sensing by Camera." << std::endl; return 0.0; }; }; class LidarRobot : public Robot { public: virtual ~LidarRobot(){}; virtual double sense() const { std::cout << "Sensing by Lidar." << std::endl; return 0.0; }; }; int main(int, char const **) { const Robot* robo = new CameraRobot(); robo->sense(); return 0; }
1.非仮想のインターフェースを使うテンプレートメソッドパターン.
次に,非仮想のインターフェースをパターンですが,こいつは0のケースとそんなに変わってないですね.virtualな関数をprivateにして,上からpublicのテンプレートメソッドから呼ぶようにしただけです.仮想関数はprivateで有るべきという考え方が元になっているパターンのようで,NVI(Non-virtual interface)イディオムとよぶらしいです.
#include <iostream> class Robot { public: virtual ~Robot(){} double sense() const { return this->doSense(); } private: virtual double doSense() const = 0; }; class CameraRobot : public Robot { public: virtual ~CameraRobot(){} private: virtual double doSense() const { std::cout << "Sensing by Camera." << std::endl; return 0.0; }; }; class LidarRobot : public Robot { public: virtual ~LidarRobot(){}; private: virtual double doSense() const { std::cout << "Sensing by Lidar." << std::endl; return 0.0; }; }; int main(int, char const **) { std::cout << "Hello World!" << std::endl; const Robot* robo = new CameraRobot(); robo->sense(); return 0; }
2.関数ポインタを使うストラテジパターン.
ここからの3つはストラテジパターンです.おそらくこの例のパターンはストラテジパターンとして実装すべき内容なのだと思います.
#include <iostream> double cameraSense() { std::cout << "CameraSensing" << std::endl; return 0.0; } double lidarSense() { std::cout << "LidarSensing" << std::endl; return 0.0; } class Robot { public: typedef double (*senseFunc)(void); explicit Robot(senseFunc func) : func(func) {} double sense() const { return this->func(); } private: senseFunc func; }; class Humanoid : public Robot { public: explicit Humanoid(senseFunc func) : Robot(func) {} }; int main(int, char**) { std::cout << "Hello World!" << std::endl; const Robot*robo = new Humanoid(cameraSense); robo->sense(); return 0; }
3.tr1::functionによるストラテジパターン.
std::tr1::functionはいろんなことができるみたいですね...ちょっとここでやりだすと終わらないので,また別の機会にやります.基本的には,関数ポインタをstd::tr1::functionをつかって表現するように変更されただけです.
#include <iostream> #include <tr1/functional> double cameraSense(void) { std::cout << "Camera Sense" << std::endl; return 0.0; } class Robot { public: typedef std::tr1::function<double (void)> senseFunc; explicit Robot(senseFunc func) : func(func) {}; virtual ~Robot() {} double doSense() const { func(); return 0.0; } private: senseFunc func; }; class Humanoid : public Robot { public: explicit Humanoid(senseFunc func) : Robot(func) {} }; int main(int, char**) { std::cout << "Hello World!" << std::endl; const Robot* robo = new Humanoid(cameraSense); robo->doSense(); return 0; }
4.古典的なストラテジパターン.
クラス階層の中の仮想関数を別のクラス階層 ”SensingStrategy” の仮想関数で置き換えました.
#include <iostream> class SensingStrategy { public: virtual ~SensingStrategy() {} virtual double sense() const = 0; }; class CameraSensing : public SensingStrategy { public: virtual ~CameraSensing() {} virtual double sense() const { std::cout << "CameraSensing" << std::endl; return 0.0; } }; class LidarSensing : public SensingStrategy { public: virtual ~LidarSensing() {} virtual double sense() const { std::cout << "LidarSensing" << std::endl; return 0.0; } }; class Robot { public: explicit Robot(std::string strategy) : sStrat(nullptr) { if (strategy == "Camera") { sStrat = new CameraSensing(); } }; virtual ~Robot() {} double doSense() const { sStrat->sense(); return 0.0; } private: SensingStrategy *sStrat; }; class Humanoid : public Robot { public: explicit Humanoid(std::string strategy) : Robot(strategy) {} }; int main(int, char**) { std::cout << "Hello World!" << std::endl; const Robot* robo = new Humanoid("Camera"); robo->doSense(); return 0; }