LSP(リスコフ置換原則) 難しいお

正方形ってレベルじゃねー...

class Worker {
public:
    virtual void work() = 0;
};

class Programmer : public Worker {
public:
    virtual void work(const ProgrammingLanguage &lang)
    { cout << "Now I'm programming with " << string(lang) << endl; }
};

class Nurse : public Worker {
public:
    virtual void work(const Medicine &med)
    { cout << "悪い子には" << string(med) << "をお注射だからね" << endl; }
};

と... 書きたくなることが... 多い... と言うか... いつもそうだ... 正確に言うと... 10のサブクラスのうち1つだけ... 仮想関数に余分の引数がほしくなる... しかし... サブクラスは... 要求するところが親クラスより厳しくては... いけないのだった... むりぽ...
上の例だけ見ると ProgrammingLanguage と Medicine をインタフェース WorkersTool から派生させれば済む気がするが,まぁ現実だとむしろそれが不自然になったり.
いや違う違う! WorkersTool から派生させればコンパイルを通すことはできようが,そこからLiskovへの長い道が始まるんだ.Programmer は Medicine では仕事ができないし Nurse は ProgrammingLanguage を注射できない.だから依然としてサブクラスの方が要求する事前条件が強い(Programmer「work() の引数にはProgrammingLanguage を頼むよ」 Nurse「Medicineを頼むよ」)状況が残って,どうにもならん.Programmer と ProgrammgingLanguage は継承関係でこそ無関係だが「引数に取る」という密結合があるので両者は単一のモジュールに属する.Nurse と Medicine も同様.Liskov-正しくするには Programmer のctorに ProgrammingLanguage を,Nurse のctorに Medicine を与えて Worker#work() を無引数にするしかないんじゃないか.これらクラスの利用者が事前条件を満たすことがコンパイラに強制される.ただ,そうするとそのctorはプログラムの流れの中のどのタイミングで呼べばいいんだ? ctorの必要な引数と Worker#work() の必要な引数が全て揃ったときに呼べと.場合にもよるが,それだと非メンバ関数で済んじゃう気が.Worker のctor呼び出しと work() 呼び出しが遠く離れてる場合,結局 dynamic_cast に頼るしかない.これもC言語ちっくである... どんどんLSPから離れていく.モデリングが悪いのか? 現実世界での「犬は哺乳類である」なリンネ博物学的直感はOOPでのモデリングに必ずしも役立たないとはよく言われることだが.

継承構造が同型であることを明示できれば「引数に取る」という関係も型の言葉で記述できると思うんだが,C++はそうはできていない.うーん,型理論を勉強しないと.