Oop 面向对象的书籍和标签设计

Oop 面向对象的书籍和标签设计,oop,Oop,我正在为一个简单的用例做一个面向对象设计的基本练习:一本书可以用许多标记进行标记 我有很多解决方案,我希望您能就OOD原则和可维护性方面提供更好的建议 选项1 public class Book { private String title; //... other attributes private List<Tag> tags; } public class Book { private Collection<String> t

我正在为一个简单的用例做一个面向对象设计的基本练习:一本书可以用许多标记进行标记

我有很多解决方案,我希望您能就OOD原则和可维护性方面提供更好的建议


选项1

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

    private List<Tag> tags;
}
public class Book {
     private Collection<String> tags;

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

}
公共课堂教材{
私有字符串标题;
//…其他属性
私有列表标签;
}
困扰我的是,我们把一本书的核心属性与其他分类或搜索数据混合在一起。我可能在将来有一个要求,某些书不能被标记。将来,当我添加更多的职责时,Book类可能会变得臃肿:类别、阅读它的用户列表、评级

选项2

public class TaggedBook extends Book {
    private Book book;

    private List<Tag> tags;
}
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
    }
}
public类TaggedBook扩展了Book{
私人书籍;
私有列表标签;
}
我认为这类似于Decorator模式,但我认为它不适合这里,因为我没有扩展行为

选项3

将书籍和标签解耦,并使用服务从书籍中检索标签(假设每本书都有唯一的标识符)

List TagService.getTags(Book)
但是,我觉得这个解决方案不是很优雅(是吗?),我可能需要发送两个查询:一个用于检索书籍,另一个用于标记

我计划将最佳选项应用于其他需求:一本书有评级,一本书可以分类

我还计划使用DMS来存储书籍和标记对象。因为它不是关系数据库,所以它的模式可能与类设计相对应


谢谢

我更喜欢第三个选项,将它们完全分开


书籍和标记之间存在多对多关系,通过将它们分开,您可以更轻松地进行诸如“哪些书籍被“计算机科学”标记”之类的查询。

如果书籍不是模型中唯一可以标记的实体。我将使用此界面:

public interface Taggeable {
    public List<Tag> getTags();        
    public void setTags (List<Tag> tags)
}
public interface QueryInterface {
    public boolean isTaggable();
    public boolean isRatable();
}
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);
   }
}

可以标记的书籍/实体类型只需要实现此接口。这样,您就可以拥有允许标记的图书对象和其他不允许标记的图书对象。此外,标记机制还可以用于模型的其他对象。

这三个选项对于类设计来说都是有效的好选择。这完全取决于完整的上下文/需求。您列出的要求很可能不足以做出“正确”的决定。例如,如果您的应用程序相当以书籍为中心,并且标记不需要独立于书籍进行演化或编写,那么选项3可能会引入不必要的复杂性。如果您设计了一个公共API作为实际逻辑的门面,那么您仍然可以选择选项1或2,即使在内部
Book
Tag
都是完全解耦的聚合根。可扩展性、性能、可扩展性。。。这些都是您需要平衡的可能需求,它们将影响您的设计

如果您正在为企业应用程序的类设计寻找一种良好的形式化方法和指导,我建议您深入研究


另外,不要为未知的未来需求设计类。这也会再次增加无用的复杂性(想想:成本)。只要确保你有足够的单元测试,当你需要为新的需求重构时,就可以覆盖你的背部。

装饰模式的概念非常适合你的情况。但是我认为 在你的情况下更有用、更有效。如果你不知道战略模式,那么看看。它会给你一个关于战略模式的好主意。如果你需要更多的建议或有任何疑问,那么在评论中询问。 非常感谢。
最好的。

我认为最好是混合模式以获得更好的解决方案。记住,一个特定的模式只解决一个特定的问题

我的建议是隔离不同的接口并相应地连接它们。基类应该能够查询支持的接口,以便调用适当的接口函数

第一个接口是支持查询的接口:

public interface Taggeable {
    public List<Tag> getTags();        
    public void setTags (List<Tag> tags)
}
public interface QueryInterface {
    public boolean isTaggable();
    public boolean isRatable();
}
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 interface Taggable {
    public Vector<Tag> getTags();
    public boolean addTag(Tag newTag);
}
普通的旧基类本身:)

现在是符合标记接口的特殊派生类:

public interface Taggeable {
    public List<Tag> getTags();        
    public void setTags (List<Tag> tags)
}
public interface QueryInterface {
    public boolean isTaggable();
    public boolean isRatable();
}
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);
   }
}

希望这有帮助。:)

选项1

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

    private List<Tag> tags;
}
public class Book {
     private Collection<String> tags;

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

}
第一种解决方案支持has-a关系的概念。我看不出这种设计有任何缺点。您说在向类中添加职责时可能会出现代码膨胀,但是这是一个完全独立的问题(将S分解为实体)。一个拥有许多成员的类本身并不是一件坏事(它有时可能表示出了问题,但并非总是如此)

你提出的另一个问题是,将来你可能会有一本没有标签的
书。因为我不知道整个情况,所以我只是在猜测,但我强烈认为这本
将/可能只是一本
,带有0
标签
s

选项3

我认为这是一种非OO的做事方式。通过一些ID的关联实现has-a关系。我一点都不喜欢

对于要添加到
书籍中的每个附加属性
,您还需要创建一个适当的
服务
类型对象,并进行大量附加和不必要的调用,这样做对我看到的选项1没有好处

我不喜欢这一点的另一个原因是,这意味着
标签
与书籍之间存在着has-a关系。我认为他们不会

选项2

在我看来,这并不好,但这主要是因为我认为decorator模式不是为在这种情况下使用而设计的,并且因为您可能需要使用rtti才能使用生成的obj