2012-04-23 6 views
4

私はいくつかのクラス階層を設計しようとしています。抽象クラス - 子タイプ

は、私が 映画やショーオブジェクトの作成をカプセル化し、OBJを返す私が持っているシステム(パーサ)クラスの切り抜いた部分では、クラス

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; 
    } 
} 

を以下していると言うことができます。クライアントに送信します。

質問:オブジェクトの種類を取得する最適な方法は何ですか。私の解決策よりも良い方法はありクライアントが

$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(); 
} 

ような何かを行うことができるように(?:私の解決策を吸うんとして読んで)、パーサ/ファクトリクラスから返さ?

+2

+1質問を説明する完璧な例... –

答えて

2

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

あなたの解決策は、それ自体が吸うわけではありません。しかし、誰かが何らかのアクションを実行するためにサブタイプを決定しようとするときは、私は不思議に思う傾向があります。どうして?この答えは少し理論的かもしれないし、おそらく少しペタント的なものかもしれませんが、ここにはあります。

を気にする必要はありません。親クラスと子クラスの関係は、子クラスが親の動作を上書きすることです。 A parent class should always be substitutable by it's children, regardless which one。あなたが求めて自分自身を見つける場合:私は、サブタイプを決定しますどのように、あなたは通常、「間違った」の2つのうちの1つをやっている:あなたは、サブタイプに基づいてアクションを実行しようとしている

  1. 。通常、クラスの「外側」ではなく、そのアクションをクラス自体に移動することを選択します。これにより、さらに管理しやすいコードが作成されます。

  2. 継承を使用して自分自身で導入した問題を解決しようとしていますが、継承が保証されていません。親が存在し、それぞれが異なるメソッドを持つ異なる方法で使用される子がある場合は、継承の使用をやめてください。彼らは同じ種類ではありません。映画はテレビと同じではなく、近くにもありません。確かに、両方のテレビで見ることができますが、類似点がそこに停止します。あなたは問題番号2に実行している場合

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

しかし、あなたのコードがうまくいくなら、あなたはそれに満足していると言います。それに行きましょう。この答えはOOにアプローチする方法では正しいですが、私はあなたのアプリケーションの残りについて何も知らないので、答えは一般的です。

+0

私は全くあなたに同意します。 2.拡張と実装の違いを正しく指摘する。映画とセリは、共通のインターフェースに従ってテレビで再生できるようにすることができますが、何も共有しない場合もあります。 –

+0

@Berry Langerakこれは、クライアントが、ある映画の名前を後ろに検索/取得/解析し、右のobjを作成してデータを取り込むことによってビデオを要求するということです。それはフェッチされたデータ(それは映画、テレビショー、将来的には多少の追加タイプかもしれません)に依存して、私はどのタイプのobjでも気にしません。私は作成するが、オブジェクトを要求したクライアント。心配する。彼はどのような方法で呼び出すことができるかを知る必要があります。 –

+0

...映画とテレビ番組が同じ基本クラスを共有していなくても、依然としてクライアントはどのobjをチェックする必要がありますか? (ビデオの種類)彼が得た。たぶん私はファサードクラスですべてを包むでしょう。とにかく、あなたは私を止めて、最初から全部を考えました:) –

2

私は方法2と一緒に行くつもりです。Videoに別の定数を追加する必要があることを抽象化して、class SoapOpera extends Show(たとえば)を追加したい場合があります。

ウェイ#2では、あなたは定数にあまり依存しません。あなたがそれをハードコーディングせずに得ることができるどんな情報でも、あなたが拡大したい場合に将来的に起こる可能性の少ない問題を意味します。 Tight an Loose Couplingについて読む

+1

合意しました - 彼が今やっていることをやり遂げる唯一のメリットは、あなたがいないクライアント開発者がいるならば、抽象クラスを変更することはできません - 抽象クラスの定数を使用すると、他の開発者にどのようにしてベースを拡張することができるかを知らせることができます。 – CD001

1

私は第2の選択肢がinstanceofを使用する方が良いと思います。これは一般に、PHPだけでなく、すべてのOOP設計に共通しています。

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

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

1

「正しい」方法があり、すべてが(たとえ性能/保守性に悪影響を与えない限り)コーディングに主観的であるならば、それは「真実」と「Brady "指摘している。

あなたが今やっているやり方(アブストラクションのクラス定数)の優れた点は、他の開発者と一緒に作業しているときに、抽象クラスをどのように相互作用させるかについてのヒントを提供できることです。例えば

:もちろん

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

、欠点は、あなたが抽象クラスで「許容拡張子」を維持しなければならないということです。

質問に示されているように、まだinstanceofメソッドを使用し、制御/タイプの修正のための抽象的な許容拡張のリストを維持することができます。