檜山正幸のキマイラ飼育記 - クラス、オブジェクト、型; なんだか変じゃない? -の練習をやってみる。
「この継承は変な感じ」
気持ち悪い理由。…Point3D じゃなく Poit3D だから、というのは嘘で :D (以降勝手に修正して書きます)
Point3D extends Point2D としたとき、たとえば Point3D.moveTo() というメソッドは
class Point3D extends Point2D {
void moveTo (double x, double y, double z) {
...
}
}と、3引数を取るべきで、単純なオーバーライドではうまくいかなくなります。他の、Point2D オブジェクトを操作していたメソッドには、一様にこの問題が発生する気が。
ColoredPoint2D extends Point2D では、こういう問題は起こらないです。たぶん。
「こういう型をクラスで定義したいんだけど」
はて何だろう。
うまく定義できるか、という問いに答えてませんが、NonNegativeInteger を定義すると、引き算オペレータとかで結果が負になる(NonNegativeInteger で扱える範囲を超える)場合どうするんですかね。
「こういう型達の関係は」
unionなりvariant型なりが使えるなら、そっちを使うのが筋なんでしょうけど。
この場合は、number と handle の扱いは次のようにするとか。
- Number か Handle かのどちらかをとる列挙型 ID を持つメンバ which を加える
- ID型の1引数を取るコンストラクタを定義し、これを使って、そのインスタンスの which の値を決定する。
- アクセッサメソッドに which の判定を仕込む事で、セットの制限を行う。
UserNumber と UserHandle は、コンストラクタでbaseクラスのIDを自動的にセットするように仕込むようにして、後は普通に継承。
実際に、C#でコード書いてみました。重要なところだけ抜粋。
/// <summary>
/// number か Handleかを決定する列挙型
/// </summary>
enum ID { Number, Handle };
/// <summary>
/// UserID クラス
/// </summary>
class UserID
{
protected int number;
protected String handle;
protected ID which;
/// <summary>
/// number へのアクセッサメソッド
/// </summary>
public virtual int Number
{
get
{
if (which == ID.Number)
return number;
else
Error(); // Error() の中身は適当に
}
set
{
if (which == ID.Number)
number = value;
else
Error();
}
}
/// <summary>
/// handle へのアクセッサメソッド
/// </summary>
public virtual String Handle
{
get
{
if (which == ID.Handle)
return handle;
else
Error();
}
set
{
if (which == ID.Handle)
handle = value;
else
Error();
}
}
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="select">IDの設定</param>
public UserID(ID select)
{
which = select;
}
/// <summary>
/// IDを表示する。
/// </summary>
public void Show()
{
switch (which) {
case ID.Number:
Console.WriteLine("Number: " + number.ToString());
break;
case ID.Handle:
Console.WriteLine("Handle: " + handle);
break;
};
}
}
/// <summary>
/// UeerNumberクラス::コンストラクタの定義のみ。
/// </summary>
class UserNumber : UserID
{
public UserNumber() : base(ID.Number) { }
}
/// <summary>
/// UserHandleクラス::コンストラクタの定義のみ。
/// </summary>
class UserHandle : UserID
{
public UserHandle() : base(ID.Handle) { }
}
/// <summary>
/// Mainプログラム
/// </summary>
class Program
{
static void Main(string[] args)
{
UserHandle handleID = new UserHandle();
handleID.Handle = "Takeo";
UserNumber numberID = new UserNumber();
numberID.Number = 5;
handleID.Show();
numberID.Show();
}
}