chipilov
chipilov

Reputation: 569

Does a design pattern exist for enforcing rules on the data of a subclass?

Let's say a class represents a 3x3 matrix and takes 9 arbitrary numbers in its constructor and let that class have an inverse() method. Since NOT all matrices are invertible, the inverse() method returns an optional value (or a nullable pointer) which must be checked by callers at runtime.

However, certain 3x3 matrices are ALWAYS invertible, as long as the values in the matrix follow certain rules, e.g. 3D rotation matrices. We can codify that with a subclass, which does NOT allow direct construction but is constructed by using some special rules, e.g the rotation angle around a Cartesian axis.

The end result is this hierarchy, which should allow a client of the hierarchy to have compile time safety when using the subclass (e.g. a client is guaranteed that object.rotate(rotationMatrix.inverse()) will always work, assuming rotationMatrix is of type RotationMatrix):

class Matrix {
 public:
  Matrix(double e_0_0, double e_0_1, double e_0_2,
         double e_1_0, double e_1_1, double e_1_2,
         double e_2_0, double e_2_1, double e_2_2) {
      ...
  }

  std::unique_ptr<Matrix3x3> inverse() const {
    if (isInvertible) {
      return std::unique_ptr(new Matrix3x3(...));
    }

    return std::unique_ptr();
  }
}

class RotationMatrix : public Matrix3x3 {
 public:
  static RotationMatrix getRotationAroundX(double angle) {
    return asRotationMatrix(Matrix(1,          0,           0,
                                   0, cos(angle), -sin(angle),
                                   0, sin(angle), cos(angle)));
  }

  RotationMatrix inverse() const {
    return asRotationMatrix(*Matrix::inverse().get()));
  }

 private:
  static const RotationMatrix3D& asRotationMatrix(const Matrix3x3& matrix) {
    return static_cast<const RotationMatrix3D&>(matrix);
  }
}

So to break down the original question:

Upvotes: 2

Views: 113

Answers (3)

Jarod42
Jarod42

Reputation: 217775

Here, you hit the circle/ellipse problem:

Better to have to distinct classes and use composition:

class RotationMatrix {
public:

    static RotationMatrix getRotationAroundX(double angle) {
        return { Matrix(1,          0,           0,
                        0, cos(angle), -sin(angle),
                        0, sin(angle), cos(angle))};
     }

     RotationMatrix inverse() const {
        return {*Matrix::inverse().get()});
     }

     const Matrix3x3& AsMatrix() const { return matrix; }

 private:
     static RotationMatrix(const Matrix3x3& matrix) : matrix(matrix) {}
     Matrix3x3 matrix;
};

Upvotes: 3

Serge Ballesta
Serge Ballesta

Reputation: 149075

is there a way in C++ to prevent (or at least clearly signal to future developers) that the subclass MUST NOT introduce new state? Unfortunately the answer is NO. All the hierarchies in any OO languages use that as a common rule: a subclass can do all what its parent does and can add other methods and attributs

The better you can do if you have special requirements, is to write a documentation and clearly state them there.

Upvotes: 0

Peter K
Peter K

Reputation: 1382

These constraints should be enforced through the interface, in this case through the constructors and any potentially available mutators (setters). This is very much the pattern you describe.

To prevent future developers from messing with the internals of your class through inheritance, make the relevant data members, i.e. those you want to protect, inaccessible by declaring them private.

Since C++11 you can also declare your class final, so that nobody can inherit from it at all.

Upvotes: 0

Related Questions