※実装目的によって解が異なることを理解して読むこと.
よくスーパークラス(A)とサブクラス(B)の関係はB_is_Aとなっていることが求められると言われる.
では正方形は長方形のサブクラスとすべきであろうか.
下記はよくある多重継承(他に適切な言葉がない)の説明に使われる設計である.
四角形クラスがスーパークラスとしてあり,長方形や菱形に派生している.
長方形と菱形両方の性質をもっているので正方形はその両方をスーパークラスとしてもつ.
しかしながら,この例を書く人は実装まで考えていないのだろう.
さてRectangleクラスの定義には色々な設計ができるだろうが,縦の長さ,横の長さ,位置の設定はできるだろう.
それを直接保存するか,頂点情報に変換して保存するか程度の違いである.
Squareクラスには何も宣言していないが,Rectangleクラスをスーパークラスとして書いた後に,
何を宣言すべきかわからなくなったからである.
Squareに必要なインターフェイスは「辺の長さ」と「位置」である.
RectangleやRhombusが持つ他の関数はSquareにとって必要のない関数となるのである.
では正方形は長方形のサブクラスとすべきであろうか.
下記はよくある多重継承(他に適切な言葉がない)の説明に使われる設計である.
class Quadrangle {}; class Rectangle : public Quadrangle {}; class Rhombus : public Quadrangle {}; class Square : public Rectangle, public Rhombus {};
四角形クラスがスーパークラスとしてあり,長方形や菱形に派生している.
長方形と菱形両方の性質をもっているので正方形はその両方をスーパークラスとしてもつ.
しかしながら,この例を書く人は実装まで考えていないのだろう.
class Quadrangle { public: //省略 protected: std::array<Point, 4> vertex; }; class Rectangle : public Quadrangle { public: Rectangle(); void SetWidth(const double value); void SetHeight(const double value); void SetCenter(const Point& value); }; class Rhombus : public Quadrangle { public: void SetSideLength(const double value); void SetAngle(const double angle, const Point& target_vertex); void SetCenter(const Point& value); }; class Square: public Rectangle, public Rhombus { };
さてRectangleクラスの定義には色々な設計ができるだろうが,縦の長さ,横の長さ,位置の設定はできるだろう.
それを直接保存するか,頂点情報に変換して保存するか程度の違いである.
Squareクラスには何も宣言していないが,Rectangleクラスをスーパークラスとして書いた後に,
何を宣言すべきかわからなくなったからである.
Squareに必要なインターフェイスは「辺の長さ」と「位置」である.
RectangleやRhombusが持つ他の関数はSquareにとって必要のない関数となるのである.
そもそも正方形を抽象的にしたものが長方形なのかというところに疑問がある.
条件の一部が同じというだけで長方形は幅と高さを独立して設定できる必要があり,正方形は独立して設定できない.
これは親子関係にあるとしてよいのだろうか.
たしかに正方形は長方形の条件を満たすが,長方形を生成するRectangleクラスとしては振舞えない.
その点にこの問題の肝がある.
そこで下のように設計する.
正方形は長方形や菱形に変換できるが,長方形や菱形の全ての機能を継承するわけではないから親子関係にはないとするのである.
条件の一部が同じというだけで長方形は幅と高さを独立して設定できる必要があり,正方形は独立して設定できない.
これは親子関係にあるとしてよいのだろうか.
たしかに正方形は長方形の条件を満たすが,長方形を生成するRectangleクラスとしては振舞えない.
その点にこの問題の肝がある.
そこで下のように設計する.
class Quadrangle; class Rectangle; class Rhombus; class Square; class Rectangle : public Quadrangle { public: Rectangle(const Square& other); Rectangle& operator=(const Square& other); }; class Rhombus : public Quadrangle { public: Rhombus(const Square& other); Rhombus& operator=(const Square& other); }; class Square : public Quadrangle {};
正方形は長方形や菱形に変換できるが,長方形や菱形の全ての機能を継承するわけではないから親子関係にはないとするのである.
似たような例にboost::ublas::identity_matrixがある.
identity_matrixは
一見,matrixの派生クラスとできそうだがこれは長方形と正方形の関係に非常によく似ている.
ではBoostでの実装はどうなっているかというと
となっている.これは先程の正方形・長方形の例と同様である.
identity_matrixは
- 正方行列である
- i == jの場合は1, i != jは0である
一見,matrixの派生クラスとできそうだがこれは長方形と正方形の関係に非常によく似ている.
ではBoostでの実装はどうなっているかというと
template<class T, class L, class A> class matrix : public matrix_container<matrix<T, L, A> > {}; template<class T, class ALLOC> class identity_matrix: public matrix_container<identity_matrix<T, ALLOC> > {};
となっている.これは先程の正方形・長方形の例と同様である.
コメントをかく