2012-03-21 21 views
10

単純なユースケースのためのオブジェクト指向設計の基本的な演習を行っています。ブックには多くのタグを付けることができます。本とタグのオブジェクト指向の設計

私は多くの解決策がありますが、OODの原則とメンテナンスの面でより良い意見をお寄せください。 1

public class Book { 
    private String title; 
    //... other attributes 

    private List<Tag> tags; 
} 

オプションは私を悩ます事は、我々は、追加の分類や検索データをブックのコア属性を混入していることです。将来、特定の書籍にタグを付けることができないという要件があるかもしれません。私はより多くのresponsabilitiesを追加するときに将来的には、Bookクラスが肥大化することができますカテゴリ、それを読んでいるユーザーのリスト、評価...

オプション2

public class TaggedBook extends Book { 
    private Book book; 

    private List<Tag> tags; 
} 

は、私は、これはに似ていると思いますデコレータパターンですが、私は動作を拡張していないので、私はそれがここに収まるとは思わない。

comnpletelyオプション3

デカップリングブックやタグ、およびブック(各書籍は、一意の識別子を持っている与えられた)

List<Tag> TagService.getTags(Book book) 

からタグを取得するためにサービスを使用しかし、私はしないでくださいこのソリューションは非常にエレガントです(それは?)、私は2つのクエリを送信しなければならないかもしれません.1つは書籍を取得するもの、もう1つはタグです。私は他の要件への最高のオプションを適用することを計画しています

:今格付けを持っているが、ブックは、私もブックとタグのオブジェクトを格納するために、DMSを使用して計画してい...

を分類することができます。リレーションシップデータベースではないため、そのスキーマはクラス設計に対応している可能性があります。

は、私はそれらを完全に分離するために、第三のオプションを好むあなたに

+0

"タグ"はそれ自身のエンティティではないでしょうか?同様に、文字列だけではなく、説明やそのようなものを持つことができます。 – cHao

+0

はい、ありがとう、これは私のオリジナルデザインでしたが、ちょっと質問に含めるのを忘れました –

答えて

1

ありがとうございます。

書籍とタグにはマンツーマンの関係があり、それらを分けることで、「コンピュータサイエンス」によってタグ付けされた本のようなクエリを簡単に作成できます。

+0

ところで、 "タグ"も実体、そうですか? –

+0

はい、そうです。私は自分の質問を修正しました –

4

タグが付けられたモデルの中で書籍のみがエンティティでない場合。私は、このインターフェイスで行くよ:

public interface Taggeable { 
    public List<Tag> getTags();   
    public void setTags (List<Tag> tags) 
} 

そして

public class Book implements Taggeable { 
//Book attributes and methods 

だけタグを付けることができブックス/エンティティの種類は、このインタフェースを実装する必要があります。こうすることで、タグ付けを許可するブックオブジェクトとそうでないブックオブジェクトを持つことができます。また、タグ付けメカニズムは、モデルの他のオブジェクトと共に使用することもできます。

5

すべての3つのオプションは、クラス設計に有効で良い選択肢です。それはすべて完全なコンテキスト/要件に依存します。あなたが列挙した要件は、「正しい」決定を下すのに十分ではない可能性が非常に高いです。たとえば、アプリケーションがブック中心であり、タグが書籍とは独立して進化したり作成したりする必要がない場合、オプション3はおそらく不必要な複雑さをもたらすでしょう。あなたの実際のロジックの周りにファサードとして機能するパブリックAPIを設計する場合は、内部でBookTagの両方が完全に分離された集約ルートであっても、オプション1または2に進む可能性があります。スケーラビリティ、パフォーマンス、拡張性...これらは、バランスを取って設計に影響を与える可能性のあるすべての要件です。

エンタープライズアプリケーションのクラス設計に関する正式な方法論とガイダンスをお探しの場合は、Domain Driven Designを参照することをお勧めします。

未知の将来の要件については、クラスを設計しないでください。これはまた、無駄な複雑さを(コストと考える)追加します。新しい要件のためにリファクタリングする必要があるときに、あなたの背中を覆う単位テストが十分であることを確認してください。私はstrategy pattern はあなたを与えるだろうあなたがThis .IT上を見てみましょうその後、戦略パターンについて知らないcase.Ifより有用であなたに有効であると考え

+0

"未知の未来の要件については、クラスを設計しないでください。"これ、これ、これ!その日が来たら、リファクター。それまでは、それよりも複雑にしないでください。 – GalacticCowboy

7

Decoratorパターンの概念は、あなたのcase.Butによく適合しますあなたはより多くの提案が必要な場合や任意のクエリを持っている場合は、コメントで尋ねる。 ありがとうございました すべて最高です..

+0

こんにちは、戦略パターンのリンクをありがとう、私は今日も何か新しいことを学びました。しかし、なぜあなたはこのケースでは最良の選択だろうと思いますか? – MrTJ

+0

@ MrTJ:これは最善の選択ではないと言います。上記のコードの文脈は、戦略パターンが将来の拡張のための追加の分類、カテゴリ、検索データ、ユーザーのリストなどを満たすことができるようです。 –

2

私はより良い解決策のためにパターンを混在させる方が良いと思います。特定のパターンは1つの特定の問題のみを解決することを忘れないでください。

私は、さまざまなインターフェイスを分離し、それに応じて参加させることをお勧めします。基本クラスには、サポートされているインタフェースを問い合わせる機能が必要です。これにより、適切なインタフェース関数を呼び出すことができます。次の特定のインターフェイスを付属しています...

public interface QueryInterface { 
    public boolean isTaggable(); 
    public boolean isRatable(); 
} 

最初のインターフェイスは、クエリサポートされるインターフェイスです。

は、第1の特定のインタフェースがタグ付け可能であると仮定します...

public interface Taggable { 
    public Vector<Tag> getTags(); 
    public boolean addTag(Tag newTag); 
} 

... 2つ目はrateableある

public interface Rateable { 
    public Rating getRating(); 
    public void setRating(Rating newRating); 
} 

昔ながらの基底クラス自体::)

public class Book implements QueryInterface { 
    private String title; 
    public boolean isTaggable() { 
     return false; 
    } 

    public boolean isRateable() { 
     return false; 
    } 
} 

タググラブルインターフェイスに準拠した特別な派生クラス:

public class TaggedBook extends Book implements Taggable { 
    private Vector<Tag> tags; 
    public Vector<Tag> getTags() { 
     return tags; 
    } 

    @override 
    public boolean isTaggable() { 
     return true; 
    } 

    public boolean addTag(Tag newTag) { 
     return tags.insert(newTag); 
    } 
} 

...とrateableだけで異なるブック:

public class RatedBook extends Book implements Rateable { 
    private Rating rating; 
    public Rating getRating() { 
     return rating; 
    } 
    public void setRating(Rating newRating) { 
     this.rating = newRating; 
    } 

    @override 
    public boolean isRateable() { 
     return true; 
    } 
} 

・ホープ、このことができます。 :)

+0

ありがとうAlois :) – fadedreamz

+0

同じ時間にタグを付けることができ、評価される本はどうですか?タグ付けされたタグは評価されるべきですか?あるいは、タグ付け可能で評価可能なクラスを実装する新しいクラスがあるはずですか? – Ivan

+0

taggableとrateableを実装する新しいクラスがあるはずです。 – fadedreamz

2

オプション1

最初の解決策はあり-関係の概念をサポートしています。私はこのデザインに何らかの欠点があるとは思わない。クラスに責任を負わせると、コードが肥大化する可能性がありますが、これは完全に別個の問題です(SOLIDのSを破る)。多くのメンバーを持つクラスは、本質的に悪いことではありません(何かが間違っていることを示すこともありますが、必ずしもそうではありません)。

もう1つの問題は、将来、BookTagがないことです。私は全体像を知らないので推測しているだけですが、私はこのBookBookTagであると強く主張します。

オプション3

私は、これは物事の非オブジェクト指向の方法だと思います。いくつかのIDの関連付けによってhas-a関係を実装します。私はそれがまったく気に入らない。

Bookに追加する追加のプロパティごとに、適切なServiceオブジェクトを作成し、多くの追加の不要な呼び出しを行う必要があります。これは、オプション1よりも利点がありません。

私がこれが好きではないもう一つの理由は、これはTagが書籍とhas-aの関係を持っていることを意味しています。私は彼らがそうするとは思わない。

オプション2

これは私の意見では良いではないですが、それは私がDecoratorパターンは、この種の状況で使用するために設計されていなかったと思うので、ほとんどで、あなたはおそらくに活用する必要がありますので、 rttiの結果として得られるオブジェクトを使用できるようにするか、基本クラスに多くの空のメソッドを実装します。

あなたの最初の解決策は圧倒的に最高だと思います。コードの膨張について心配している場合は、BookのメンバーとしてTagsオブジェクトを持つことを検討することができます(これはSOLIDのSに役立ちます)。Bookの追加プロパティについても同様です。 Bookにタグがない場合、Tagsは照会すると単にfalseを返し、Bookはそれをエコーし​​ます。このような単純な問題については

概要

、以上のことを考えていません。 OO設計の基本原則(has-a、is-a)が最も重要です。

+0

+1 ' IF(book1.isTaggable()){ '' 'TaggedBook tagBook =(TaggedBook)BOOK1;' ' tagBook.getTags()}。 –

1

書籍のタグの順序や書籍のタグが同じタグを2回持つことができない場合は、リストではなくセットにタグを保存する必要があります。

これを済ませたら、私は第3のオプションのようなものに行きます。書籍はタグを所有しておらず、タグは本を所有していないようです(実際は、どちらかといえばこれを見てみたいと思います)。後で書籍に他のものを関連付ける場合は(レビュー、評価、図書館など)、ブッククラスを変更せずに別の関連付けを作成することができます。

2

まず、デザイン上の問題として考えてから、コードでそのデザインを表現しようと思います。

私たちは、私たちが持っているクラス(エンティティ)を決定する必要があります。 Bookは、問題の中心であり、別個のインスタンスを持ち、おそらくいくつかの属性と操作を持つため、クラスです。 Tagは、値オブジェクトであってもクラスであってもよい。

最初のオプションを考えてみましょう。内部構造、操作、およびインスタンスが区別されない可能性があるため、値オブジェクトにすることができます。従って、Tagは、Stringマーカーと考えることができる。このようにして、Bookには、tag値のコレクションを含む属性、たとえばtagsがあります。値は何の制限もなく追加および削除できます。タグは値によって提供されるタグで書籍を検索できます。タグの完全なリストを取得したり、特定のタグのすべての書籍を取得することは困難です。

今、2番目のオプションです。 Tagは、別のクラス(Book)に関連しており、そのインスタンスが異なる可能性があるため、クラスにすることもできます。次に、我々は2つのクラスを持っています:BookTag、それらの間の「多対多」の関係 - TaggedWith。ご存じのように、関連は、関係の他にクラスそのものの一種です。 TaggedWithのインスタンス(リンク)は、BookTagのインスタンスを接続します。次に、BookTagの間の通信の管理(作成、読み取り、参照、破棄、更新など)を担当するクラスを決定する必要があります。ここでの最も自然な選択は、この責任を協会TaggedWithに割り当てることです。

いくつかのコードを書いてください。

オプション1

public class Book { 
    private Collection<String> tags; 

    /* methods to work with tags, e.g. */ 
    public void addTag(String tag) {...} 
    public String[] getAllTags() {...} 
    ... 

} 

それは複雑に見えるかもしれませんが、実際には同様のコードは、単にマウスクリックのカップルに設計記述から生成することができます。一方、DBを使用すると、ここで多くのコードがSQLクエリになります。私はそれが全く異なるだろう

オプション2

public class Tag { 
    /* we may wish to define a readable unique id for Tag instances */ 
    @Id 
    private String name; 

    /* if you need navigation from tags to books */ 
    private Collection<Book> taggedBooks; 
    public Collection<Book> getTaggedBooks() {...} 
    public void addBook(Book book) {...} // calls TaggedWith.create(this, book) 
    public void _addBook(Book book) {...} // adds book to taggedBooks 
    .... 
    /* I think you get the idea */ 


    /* methods to work with tags */ 
    public String getName() {...} 
    ... 

    /* Tags cannot be created without id (i.e. primary key...) */ 
    public Tag(String name) {...} 


    /* if you'd like to know all tags in the system, 
     you have to implement 'lookup' methods. 
     For this simple case, they may be put here. 
     We implement Factory Method and Singleton patterns here. 
     Also, change constructor visibility to private/protected. 
    */ 
    protected static HashMap<String, Tag> tags = ...; // you may wish to use a DB table instead 
    public static Tag getInstance(String name) {...} // this would transform to DAO for DB 
} 

public class Book { 
    /* if we need an id */ 
    @Id // made up 
    private String bookId; 

    /* constructors and lookup the same as for Tag 
     If you wish to use a database, consider introducing data access layer or use ORM 
    */ 

    /* if you need navigation from Book to Tag */ 
    private Collection<Tag> tags; 
    public Collection<Tag> getTags() {...} 
    ... 

} 

public TaggedWith { 
    /* constructor and lookup the same as for Tag and Book (!) */ 

    /* manage ends of the association */ 
    private Book book; 
    private Tag tag; 
    public Book getBook() {...} 
    public Tag getTag() {...} 

    protected TaggedWith(Book book, Tag tag) {  
      this.book = book; 
      this.tag = tag; 
      book._addTag(tag); // if you need navigation from books to tags 
      tag._addBook(book); // if you need navigation from tags to books 
    } 


    /* if you need to search tags by books and books by tags */ 
    private static Collection<TaggedWith> tagsBooks = ...; 
    public static TaggedWith create(Tag tag, Book book) { 
      // create new TaggedWith and add it to tagsBooks 
    } 
} 
1

。 Gmailのラベルのようなものを考えれば、実際に特定のタグを持つ本を探すのではなく、そのタグがその本にあるかどうかを調べるほうが簡単です。言い換えれば、タグは、本を見つけるためのフィルタとして機能し、他の方法ではありません。

public interface ITaggable 
{ 
    string Name { get; } 
} 

public class Book : ITaggable 
{ 
} 

public class Tag 
{ 
    private List<ITaggable> objects; 
    private String name; 

    public void AddObject() {} 
    public void RemoveObject() {} 
    public void HasObject() {} 
} 

public class TagManager 
{ 
    private List<Tag> tags; 

    private void InitFromDatabase() {} 
    public void GetTagsForObject(o: ITaggable) {} 
    public void GetObjectsForTag(objectName: String) {} // 
    public void GetObjectsForTag(t: Tag) {} // 
    public void GetObjectsForTag(tagName: String) {} // 
    public void GetAllTags(); 
} 

... somewhere else ... 

public void SearchForTag(tag: Tag) 
{ 
    TagManager tagManager = new TagManager(); 
    // Give me all tags with 
    List<ITaggable> books = tagManager.GetObjectsForTag("History"); 
} 
関連する問題