2012-03-21 177 views
10

我正在做一個簡單用例的面向對象設計的基本練習:書籍可以用很多標籤進行標記。書籍和標籤的OO設計

我有很多的解決方案,我希望你的意見能夠在OOD原則和可靠性方面更好。

選項1

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

    private List<Tag> tags; 
} 

困擾我的是,我們混合一書的核心屬性與附加的分類或搜索數據的事情。我將來可能會有某些書無法加標籤的要求。在未來,當我添加更多responsabilities書類可以變得臃腫:類別,即讀取它的用戶,評級列表...

選項2

public class TaggedBook extends Book { 
    private Book book; 

    private List<Tag> tags; 
} 

我認爲這是類似於裝飾者模式,但我不認爲它適合在這裏,因爲我不擴展行爲。

選項3

解耦書籍和標籤comnpletely,並使用一個服務從一本書中檢索標籤(給每本書都有一個唯一的標識符)

List<Tag> TagService.getTags(Book book) 

不過,我不知道找到這個解決方案非常優雅(是嗎?),我可能不得不發送兩個查詢:一個檢索該書,另一個檢索標籤。

我計劃申請其他要求的最佳選項:一本書有一個等級,一本書可以被分類...

我還計劃使用DMS存儲書籍和標籤的對象。由於它不是一個關係數據庫,它的模式可能會對應於類設計。

謝謝

+0

「標籤」不是自己的實體嗎?如在,不僅僅是一個字符串 - 它可能有一個描述等... – cHao 2012-03-21 06:21:37

+0

是的,謝謝,這是我的原創設計,但我忘了將它包括在問題 – 2012-03-21 06:24:59

回答

1

我更喜歡第三個選項,將它們完全分開。

書籍和標籤之間存在多對多的關係,通過將它們分開,您可以更容易地進行查詢,如「哪些書籍被'計算機科學'標記了」。

+0

順便說一句,「標籤」也是一個實體,對嗎? – 2012-03-21 06:24:07

+0

是的,是的。我糾正了我的問題 – 2012-03-22 06:08:49

4

如果書籍不是您模型中可以標記的唯一實體。我會用這個接口去:

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

而且

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

該種圖書/可標記實體只需要實現此接口。這樣,您可以讓Book對象允許標記,而其他對象則不允許。此外,標記機制可以與您的模型的其他對象一起使用。

5

所有三個選項都可以爲您的課堂設計提供有效且良好的選擇。這一切都取決於完整的上下文/要求。您列出的要求很可能不足以做出「正確」的決定。例如,如果您的應用程序相當以書爲中心,且標記無需獨立於書籍進行演變或編寫,則選項3可能會帶來不必要的複雜性。如果你設計一個公共API作爲實際邏輯的外觀,你仍然可以選擇1或2,即使內部BookTag都是完全去耦合的聚合根。可擴展性,性能和可擴展性......這些都是您需要平衡並且會影響您的設計的所有可能的要求。

如果您正在尋找一個良好的正式方法和企業應用程序的類設計指南,我建議你看看Domain Driven Design

此外,不要爲未知的未來需求設計類。這也會再次增加無用的複雜性(想想:成本)。只需確保在需要重新構建新需求時您有足夠的單元測試來覆蓋背部。

+0

「另外,不要爲未知的未來需求設計類。」這,這個!當那一天到來時,重構。在此之前,不要讓它比以前更復雜。 – GalacticCowboy 2012-03-28 16:47:45

7

Decorator模式的概念在你的case.But非常適合我覺得strategy pattern 是你更多的有用和有效case.If你不知道的戰略格局再拿上This。它一看就會給你一個如果您需要更多建議或有任何疑問,請發表評論。 謝謝 一切順利..

+0

嗨,感謝策略模式的鏈接,今天我也學到了一些新的東西。但爲什麼你認爲在這種情況下,這將是最好的選擇? – MrTJ 2012-03-28 09:09:20

+0

@MrTJ:我不認爲這是最好的選擇。上面給出的代碼的上下文似乎認爲,戰略模式可以通過額外的分類,類別,搜索數據,用戶列表等滿足他對未來擴展的需求。 。 – 2012-04-02 12:13:46

2

我認爲這將是更好的混合模式更好的解決方案。記住一個特定的模式只能解決一個特定的問題。

我的建議是隔離不同的接口並相應地加入它們。基類應該能夠查詢支持的接口,以便它可以調用適當的接口函數。

第一界面是查詢支持的接口:

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

...其次是特定的接口。

假設第一特定接口是可加標籤:

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

...和第二個是差餉...

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

...和不同的書,只是應課差餉租:

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

謝謝阿洛伊斯:) – fadedreamz 2012-03-28 19:35:10

+0

那本書可以在同一時間進行標記和評分?標籤應該延長額定值,反之亦然?或者應該有一個實現標籤和評級的新類? – Ivan 2012-04-03 12:38:42

+0

應該有一個實現標籤和評分的新類。 – fadedreamz 2012-04-04 16:42:41

2

選項1

第一解決方案支持具有-的關係的概念。我沒有看到這個設計有什麼缺點。你說在向類中添加職責時,代碼可能會膨脹,但這是一個完全獨立的問題(在SOLID中打破S)。一個有許多成員的班級本質上並不是一件壞事(有時候可能表明出了問題,但並非總是如此)。

您提出的另一個問題是,將來您可能會有一個Book而沒有Tag s。由於我不知道整個情況,所以我只能猜測,但我會強烈認爲,這個Book會/可能只是一個Book,0 Tag s。

選項3

我認爲這是做事的非面向對象的方式。通過某個ID的關聯實現一個has-a關係。我根本不喜歡它。

對於您想要添加到Book的每個附加屬性,您還需要創建一個合適的Service類型對象,並進行大量額外的和不必要的調用,而不會因爲我可以看到的選項1而獲益。

我不喜歡這個的另一個原因是,這意味着Tag與書有關係。我不認爲他們這樣做。

選項2

這不是在我看來不錯,但主要是因爲我覺得Decorator模式是不適合在這種情況下使用,因爲你可能會需要利用rtti能夠使用你的結果對象,或者在你的基類中實現很多空方法。

我認爲你的第一個解決方案絕對是最好的。如果您擔心代碼膨脹,您可能會考慮將Tags對象作爲Book的成員,該對象負責搜索自身(這對SOLID中的S也有幫助),對於Book的任何其他屬性也是如此。如果Book沒有標籤,那麼Tags只會在查詢時返回false,而Book會回顯。

摘要

對於這樣一個簡單的問題,不好好考慮一下。面向對象設計的基本原則(has-a,is-a)是最重要的。

+0

1爲_DO不overthink_。 – 2012-04-02 12:27:37

1

除非書上的標籤順序很重要,否則書本可以有相同的標籤兩次,否則應該將標籤存儲在一個集合中,而不是一個列表中。

一旦你這樣做了,我會像第三種選擇一樣。在我看來,這些書不擁有這些標籤,並且這些標籤不擁有這些書(實際上,您可能希望以任何方式查看這些書)。之後,如果您希望將其他內容與您的圖書相關聯(,例如評論,評分,圖書館),則可以在不修改圖書類的情況下創建另一個關聯。

2

我會建議先考慮它作爲設計問題,然後嘗試在代碼中表達設計。

所以,我們必須決定我們有什麼類(實體)。 Book是一個類,因爲它是問題的中心,具有不同的實例,可能還有幾個屬性和操作。 Tag可能既是一個值對象又是一個類。

讓我們考慮第一個選項。它可以是一個值對象,因爲它沒有內部結構,沒有任何操作,它的實例可能不是獨特的。因此Tag可以被認爲是String標記。這樣,Book就有一個屬性,比如tags,它包含一組值tag。可以添加和刪除值,不受任何限制。書籍可以通過標籤按價值提供的標籤進行搜索。很難得到完整的標籤列表或獲取特定標籤的所有書籍。

現在,第二個選項。 Tag也可以是一個類,因爲它與另一個類(Book)相關,其實例可能不同。然後我們有兩個類: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"); 
}