4

クラス階層を設計しようとしていますが、この部分で「行き詰まり」ました。

次のクラスがあるとしましょう

abstract class Video 
{
    const TYPE_MOVIE = 1;
    const TYPE_SHOW  = 2;

    abstract public function getTitle();
    abstract public function getType();
}

class Movie extends Video 
{
    // ...

    public function getType() 
    {
        return self::TYPE_MOVIE;
    }
}

class Show extends Video 
{
    // ...

    public function getType() 
    {
        return self::TYPE_SHOW;
    }
}

システムの別の部分には、映画とショーのオブジェクトの作成をカプセル化し、obj を返す (Parser) クラスがあります。クライアントに。

質問: obj の型を取得する最良の方法は何ですか? クライアントが次のようなことができるように、パーサー/ファクトリークラスから返されます

$video = $parser->getVideo('Dumb and Dumber');

echo $video->getTitle();

// Way 1
if($video->getType == 'show') {
    echo $video->getNbOfSeasons();
}

// Way 2
if($video instanceof Show) {
    echo $video->getNbOfSeasons();
}

// Current way
if($video->getType == Video::TYPE_SHOW) {
    echo $video->getNbOfSeasons();
}

私の解決策よりも良い方法はありますか (読む: 私の解決策は悪いですか?)?

4

4 に答える 4

2

私は方法2に行きます。それはあなたがVideo追加したいと思うかもしれない場合に備えて別の定数を追加する必要性を抽象化しますclass SoapOpera extends Show(例えば)。

方法2では、定数への依存度が低くなります。ハードコーディングせずに取得できる情報が何であれ、拡張したい場合に将来発生する可能性のある問題が少なくなることを意味します。Tight aLooseCouplingについて読んでください。

于 2012-04-23T08:45:28.133 に答える
2

私の解決策よりも良い方法はありますか(私の解決策は吸うのですか?)?

あなたの解決策は、それ自体が悪いものではありません。ただし、誰かがサブタイプを決定して何らかのアクションを実行しようとするときはいつでも、私は疑問に思う傾向があります。なぜ?この答えは少し理論的で、おそらく少し衒学的でさえあるかもしれませんが、ここに行きます。

あなたは気にしないでください。親と子クラスの関係は、子クラスが親の動作を上書きすることです。親クラスは、どちらのクラスに関係なく、常にその子によって置換可能である必要があります。サブタイプをどのように判断すればよいかと自問している場合は、通常、「間違った」2つのことのいずれかを実行しています。

  1. サブタイプに基づいてアクションを実行しようとしています。通常、そのアクションをクラスの「外部」ではなく、クラス自体に移動することを選択します。これにより、コードも管理しやすくなります。

  2. 継承が保証されていない継承を使用して、自分で発生した問題を修正しようとしています。親があり、子があり、それぞれが異なる方法で使用され、それぞれが異なるメソッドを持っている場合は、継承の使用を停止します。それらは同じタイプではありません。映画はテレビシリーズと同じではなく、近くにさえありません。確かに、あなたはあなたのテレビで両方を見ることができます、しかし類似はそこで止まります。

問題番号2に遭遇している場合は、おそらくそれが理にかなっているからではなく、単にコードの重複を減らすために継承を使用しています。それ自体は良いことですが、そうしようとしている方法は最適ではない可能性があります。可能であれば、代わりにコンポジションを使用することもできますが、任意のゲッターとセッターを除いて、重複する動作がどこにあるのか疑問があります。

そうは言っても、コードが機能し、満足している場合は、それを実行してください。この答えはOOにアプローチする方法では正しいですが、アプリケーションの残りの部分については何も知らないので、答えは一般的です。

于 2012-04-23T09:10:10.030 に答える
1

instanceof を使用する 2 番目のオプションの方が優れていると思います。これは一般に、PHP だけでなく、すべての OOP 設計に共通です。

最初のオプションでは、基本クラスの派生クラスに関する詳細があるため、追加する新しい派生クラスごとに基本クラスを変更する必要がありますが、これは常に回避する必要があります。

新しい派生クラスを追加するときに基本クラスをそのままにしておくと、コードの再利用が促進されます。

于 2012-04-23T08:49:46.637 に答える
1

「正しい」方法があり、もちろんすべてがコーディングにおいて主観的である場合 (パフォーマンス/保守性に悪影響を与えない限り;))、「真実」と「ブレイディ」が指摘したように、それは2番目の方法です.

現在行っている方法 (abstract のクラス定数) の利点は、他の開発者と作業しているときに、abstract クラスがどのように相互作用すると予想されるかについてのヒントを提供できることです。

例えば:

$oKillerSharkFilm = Video::factory(Video::MOVIE, 'Jaws', 'Dundundundundundun');
$oKillerSharkDocumentary = Video::factory(Video::DOCUMENTARY, 'Jaws', 'A Discovery Shark Week Special');

もちろん、欠点は、抽象クラスで「許容される拡張機能」を維持する必要があることです。

質問で示されているようにメソッドを引き続き使用し、instanceof主にコントロール/タイプの修正のために、抽象で許容される拡張のリストを維持できます。

于 2012-04-23T09:06:22.300 に答える