オブジェクト指向
-継承-

■継承

継承既存のクラスのメンバを引き継いで新しいクラスを作成する機能のことで、
オブジェクト指向プログラミングを行うでよく利用されています。
継承については継承で説明していますが
汎化と継承を使用した際のメリットについて再度記述をします。

●汎化
	汎化とは複数のクラスから共通のメンバを抽出して1つの基底クラスを作成することです。
	汎化を実現することで再利用性の高いクラスを作成し、それを継承することで
	汎化の特性を持ちつつ新しい機能を持ったクラスを作成することができます。

	・敵クラス(汎化なし)
		// 敵クラス(左右移動のみ)
		class EnemyType1
		{
		public:
			// コンストラクタ
			EnemyType1() 
			{
				m_PosX = 100.0f;
				m_PosY = 400.0f;
				m_Speed = 10;
			}

			// ゲッター
			float GetPosX(void)
			{
				return m_PosX;
			}

			float GetPosY(void)
			{
				return m_PosY;
			}

			int GetSpeed(void)
			{
				return m_Speed;
			}
				
		private:
			float m_PosX;
			float m_PosY;
			int m_Speed;
		};

		// 敵クラス(弾発射型)
		class EnemyType2
		{
		public:
			// コンストラクタ
			EnemyType2(int shot_type) 
			{
				m_PosX = 300.0f;
				m_PosY = 400.0f;
				m_Speed = 4;
				m_ShotType = shot_type;
			}

			// ゲッター
			float GetPosX(void)
			{
				return m_PosX;
			}

			float GetPosY(void)
			{
				return m_PosY;
			}

			int GetSpeed(void)
			{
				return m_Speed;
			}
		private:
			float m_PosX;
			float m_PosY;
			int m_Speed;
			int m_ShotType;
		};

		// 敵クラス(索敵型)
		class EnemyType3
		{
		public:
			EnemyType3(float area)
			{
				m_PosX = 600.0f;
				m_PosY = 400.0f;
				m_Speed = 8;
				m_SearchArea = area;
			}

			// ゲッター
			float GetPosX(void)
			{
				return m_PosX;
			}

			float GetPosY(void)
			{
				return m_PosY;
			}

			int GetSpeed(void)
			{
				return m_Speed;
			}
		private:
			float m_PosX;
			float m_PosY;
			int m_Speed;
			float m_SearchArea;
		};

		void main(void)
		{
			// 敵1、2、3をインスタンス化
			EnemyType1 kuribo;
			EnemyType2 burosu(1);
			EnemyType3 wanwan(100);
		}

		上記のコードでは敵クラス1、2、3を宣言し、無事インスタンス化していますが、
		プランナーの仕様書の記述ミスで各クラスに設定している速度が実数であること、
		また、メンバ変数に対してコメントをつけ忘れていることからコードの修正を
		行うことになりました。

		修正箇所:
			・メンバ変数の内容をコメントで書く
			・m_Speedの型をfloatに直して、warning対策として
			 使用箇所に「f」をつける
				

		// 敵クラス(左右移動のみ)
		class EnemyType1
		{
		public:
			// コンストラクタ
			EnemyType1() 
			{
				m_PosX = 100.0f;
				m_PosY = 400.0f;
				m_Speed = 10.0f;
			}

			// ゲッター
			float GetPosX(void)
			{
				return m_PosX;
			}

			float GetPosY(void)
			{
				return m_PosY;
			}

			float GetSpeed(void)
			{
				return m_Speed;
			}
				
		private:
			float m_PosX;	// 座標X
			float m_PosY;	// 座標Y
			float m_Speed;	// 速度
		};

		// 敵クラス(弾発射型)
		class EnemyType2
		{
		public:
			// コンストラクタ
			EnemyType2(int shot_type) 
			{
				m_PosX = 300.0f;
				m_PosY = 400.0f;
				m_Speed = 4.0f;
				m_ShotType = shot_type;
			}

			// ゲッター
			float GetPosX(void)
			{
				return m_PosX;
			}

			float GetPosY(void)
			{
				return m_PosY;
			}

			float GetSpeed(void)
			{
				return m_Speed;
			}
		private:
			float m_PosX;	// 座標X
			float m_PosY;	// 座標Y
			float m_Speed;	// 速度
			int m_ShotType;	// 弾の種類
		};

		// 敵クラス(索敵型)
		class EnemyType3
		{
		public:
			EnemyType3(float area)
			{
				m_PosX = 600.0f;
				m_PosY = 400.0f;
				m_Speed = 8.0f;
				m_SearchArea = area;
			}

			// ゲッター
			float GetPosX(void)
			{
				return m_PosX;
			}

			float GetPosY(void)
			{
				return m_PosY;
			}

			float GetSpeed(void)
			{
				return m_Speed;
			}
		private:
			float m_PosX;	// 座標X
			float m_PosY;	// 座標Y
			float m_Speed;	// 速度
			float m_SearchArea; // 索敵範囲
		};

		void main(void)
		{
			// 敵1、2、3をインスタンス化
			EnemyType1 kuribo;
			EnemyType2 burosu(1);
			EnemyType3 wanwan(100);
		}
			
	EnemyType1、2、3に対して行った修正で同じ内容がいくつもあったことを気づかれた方は
	多いのではないでしょうか。
	正直コピー&ペーストすればいいと感じたり、実際にやった方もいるかもしれません。
	このように同じような内容の修正は効率が悪く、かつヒューマンエラーが出やすい箇所です。
	同じだと思って作業的にやって、その結果抜けが出るということが多々発生します。
	次は汎化を使用したコードを書いてみたいと思います。

	・敵クラス(汎化有り)
		/*
			汎化クラス名 => EnemyBase

			汎化で抽出するメンバ変数
				座標X
				座標Y
				速度

			汎化で抽出するメンバ関数
				座標Xのゲッター
				座標Yのゲッター
				速度のゲッター
		*/
		class EnemyBase
		{
		public:
			// コンストラクタ
			EnemyBase(float posx, float posy, int speed)
			{
				m_PosX = posx;
				m_PosY = posy;
				m_Speed = speed;
			}

			float GetPosX(void)
			{
				return m_PosX;
			}

			float GetPosY(void)
			{
				return m_PosY;
			}

			int GetSpeed(void)
			{
				return m_Speed;
			}
		protected:
			float m_PosX;
			float m_PosY;
			int m_Speed;
		};

		// EnemyType1
		// 敵クラス(左右移動のみ)
		class EnemyType1 : public EnemyBase
		{
		public:
			// コンストラクタ
			EnemyType1() : EnemyBase(100.0f, 400.0f, 10)
			{
			}
		};

		// 敵クラス(弾発射型)
		class EnemyType2 : public EnemyBase
		{
		public:
			// コンストラクタ
			EnemyType2(int shot_type) : EnemyBase(300.0f, 400.0f, 4)
			{
				m_ShotType = shot_type;
			}
		private:
			int m_ShotType;
		};

		// 敵クラス(索敵型)
		class EnemyType3 : public EnemyBase
		{
		public:
			EnemyType3(float area) : EnemyBase(600.0f, 400.0f, 8)
			{
				m_SearchArea = area;
			}

		private:
			float m_SearchArea;
		};

		void main(void)
		{
			// 敵1、2、3をインスタンス化
			EnemyType1 kuribo;
			EnemyType2 burosu(1);
			EnemyType3 wanwan(100);
		}

	汎化クラスのEnemyBaseを作成し、EnemyType1、2、3がそれを継承したことで、
	派生クラスはかなりすっきりとした内容になっています。
	この後は先ほどと同様にコメントと型修正の実装をしたいと思います。

		/*
			汎化クラス名 => EnemyBase

			汎化で抽出するメンバ変数
				座標X
				座標Y
				速度

			汎化で抽出するメンバ関数
				座標Xのゲッター
				座標Yのゲッター
				速度のゲッター
		*/
		class EnemyBase
		{
		public:
			// コンストラクタ
			EnemyBase(float posx, float posy, float speed)
			{
				m_PosX = posx;
				m_PosY = posy;
				m_Speed = speed;
			}

			float GetPosX(void)
			{
				return m_PosX;
			}

			float GetPosY(void)
			{
				return m_PosY;
			}

			float GetSpeed(void)
			{
				return m_Speed;
			}
		protected:
			float m_PosX;	// 座標X
			float m_PosY;	// 座標Y
			float m_Speed;	// 速度
		};

		// EnemyType1
		// 敵クラス(左右移動のみ)
		class EnemyType1 : public EnemyBase
		{
		public:
			// コンストラクタ
			EnemyType1() : EnemyBase(100.0f, 400.0f, 10.0f)
			{
			}
		};

		// 敵クラス(弾発射型)
		class EnemyType2 : public EnemyBase
		{
		public:
			// コンストラクタ
			EnemyType2(int shot_type) : EnemyBase(300.0f, 400.0f, 4.0f)
			{
				m_ShotType = shot_type;
			}
		private:
			int m_ShotType;	// 弾の種類
		};

		// 敵クラス(索敵型)
		class EnemyType3 : public EnemyBase
		{
		public:
			EnemyType3(float area) : EnemyBase(600.0f, 400.0f, 8.0f)
			{
				m_SearchArea = area;
			}

		private:
			float m_SearchArea;	// 索敵範囲
		};

		void main(void)
		{
			// 敵1、2、3をインスタンス化
			EnemyType1 kuribo;
			EnemyType2 burosu(1);
			EnemyType3 wanwan(100);
		}

	これで、汎化無しと同じ修正が完了しました。
	この修正で注目すべきポイントは汎化無しの際にEnemyType1、2、3で行った修正のほとんどが、
	EnemyBaseを修正することで問題が解決した点です。
	EnemyBaseの各修正箇所を修正することでEnemyType1、2、3を修正する必要がなくなりました。
	先ほどの汎化無しと比べるとかなり効率よく修正ができたのではないかと思います。
	これが汎化を利用するメリットの1つです。

●継承のメリット
	・作業の効率化
		汎化を用いることで再利用性のあるクラスを作成し、
		それを継承することで、作業の効率があがります。

	・保守性の向上
		しっかりとしたチェックを行った基底クラスを継承した派生クラスで
		問題が発生した場合、それはかなりの確率で派生クラスで実装している
		処理の問題となります。
		なぜならば、基底クラスの動作はチェックなどの結果で保証されているからです。
		きちんとした基底クラスを作成すれば問題が発生しづらくなったり、
		問題の発生元を特定しやすくなり、保守性が向上します。

	・クラス間のつながりを浅くする
		継承を使用することで、クラス間のつながりをあさくすることができます。
		例えばクラスA、B、Cで実装しているメンバがあり、
		それを別のクラスD、E、Fが使用しているとします。
		このメンバを汎化できるのならば、A、B、Cの基底クラスGを作成し、
		D、E、FはGのメンバを使わせるようにします。
		すると各クラスのつながりが整頓される、かつ少なくなります。

		pgtheory_0043
		pgtheory_0044