CRUD方法应该是对象还是服务层的一部分?

时间:2017-07-23 20:39:19

标签: java oop object-oriented-analysis

我正在经历一些面向对象的设计问题以便学习,我遇到了这个问题,其中书籍目录中需要Book对象。这是在问题的解决方案中建议的Book对象。

public class Book {

    private long ID;
    private String details; 
    private static Set<Book> books;

    public Book(long iD, String details) { ... }    

    public static void addBook(long iD, String details){
        books.add(new Book(iD, details));
    }

    public void update() { }

    public static void delete(Book b) { books.remove(b); }

    public static Book find(long id){
        for (Book b : books)        
            if(b.getID() == id) return b;   
        return null;

    }
}

在某种程度上,这个Book对象对我来说非常好,因为它包含了修改/获取有关book对象的信息以及有关该书的数据所需的所有方法。因此,当使用OOP的定义时,这看起来很棒,因为这就是对象应该是什么。

但是,在我1 - 2年的编程生涯中,我一直在做的事情,我总是认为创建,删除。修改对象应该通过服务层完成,本质上是一个BookService类,它包含使用不包含这些CRUD方法的Book对象创建Books,更新书籍和从数据库中删除书籍的方法。

第一种方法在理论上看起来很棒,而下一种方法在实践中很棒,因为我从任何经验中都知道。第二种方法是否存在缺陷/陷阱?应该首选哪种方法?

PS:我不确定这些问题是否被接受,我很乐意删除/编辑它们,如果它们不是,但我找不到更好的地方来得到答案:(< / em>的

2 个答案:

答案 0 :(得分:2)

如果您正在实现具有学习目的的控制台应用程序,如果您将CRUD逻辑实现到模型中则不是什么大问题。但我认为不是这种情况。

您实施的此模型书必须只包含对象属性以及getter和setter。您实现的其他CRUD方法必须位于外部层。外部层可能是SERVICE或DAO,它取决于..但你必须知道,如果你像现在这样在模型类中编写一些额外的逻辑,这不是一个好习惯。

答案 1 :(得分:2)

您的Book对象是一个所谓的&#34;域对象&#34;。它唯一的责任是提供所谓的商业逻辑&#34;。例如:它有一个由类成员定义的状态,它可以包含与状态交互的类方法(用于计算等)。没有其他对象应该知道内部业务逻辑实现。

现在,域对象也被称为&#34; models&#34;。但这可能有点令人困惑。为什么?因为&#34;模型&#34;实际上是一个层。它由三个子层组成:

  • domain layer (domain model) ,由域组成 对象。通过他们的结构和相互依存, 它们是现实世界的抽象(商业) 对象/实体。该图层也可以包含类似的结构 域对象的集合。
  • 存储层,由负责的类组成 将域对象传入/传出底层存储 系统(可能是RDBMS,文件系统等):repositories(data) mappersadapters,数据访问抽象类等。 使用这些结构也可以达到制作域的目的 对象(完全)不可知,不知道存储类型和 它被解决的方式。
  • service layer 是根据类(例如服务)构建的 执行涉及上两个结构的操作 子层。应该进行用户与程序的交互 只能通过服务。

因此,在您的情况下,域对象Book将如下所示:

public class Book {

    private long ID;
    private String details;

    public Book() { ... }

    // Setters/getters...
}

然后您还有一个数据映射器(BookDataMapper):

public class BookDataMapper {

    private DbAdapter adapter;
    private Set<Book> books;

    public BookDataMapper(DbAdapter adapter) {
        // Assign DbAdapter object to the adapter class member.
    }    

    public void select(long bookId) {
        // 1. Fetch book record from db by bookId and using the injected db adapter.
        // 2. Map fetched db record to a Book object using mapBook().
        // 3. Add Book object to books using addBook().
    }
    public void insert(Book book) {
        // 1. Read the class members of object book.
        // 2. Inject the values in an INSERT SQL statement as parameters.
        // 3. Run the INSERT query and return last insert id.
        // 4. Assign the last insert id to book's ID class member. 
        // 4. Return book.
    }
    public void update(Book book) { ... }
    public void delete(Book book) { ... }

    public void mapBook(array bookRecord){
        // 1. Create a plain Book object.
        // 2. Read bookRecord array and map each field to the corresponding 
        //    class member of the Book object.
        // 3. Return mapped Book object.
    }

    public void addBook(Book book){
        // Add book to books collection.
    }

}

您也可以为数据访问定义更高的抽象层,例如:一个BookRepository。您也可以/应该将书籍集(代码)移到其中:

public class BookRepository {

    private BookDataMapper bookMapper;

    public BookRepository(BookDataMapper bookMapper) {
        // Assign BookDataMapper object to the bookMapper class member.
    }    

    public void find(long bookId) {
        // 1. Use bookMapper to fetch book record from the storage by bookId.
        //    Notice that I said storage, not db: per definition, a repository 
        //    hides the details regarding the storage type. The user (client) 
        //    knows only that the book is placed... somewhere.
        // 2. Return the fetched book object.
    }
    public void store(Book book) {
        // 1. Use bookMapper to store the book.
        // 2. Return the book (with last insert id in it).
    }
    public void update(Book book) { ... }
    public void remove(Book book) { ... }

}

最后,当用户想要从图书馆借书时,定义一个服务(BookBorrowingService)来管理图书借阅过程:

public class BookBorrowingService {

    private UserCardRepository userCardRepository;
    private BookRepository bookRepository;

    public BookBorrowingService(UserCardRepository userCardRepository, BookRepository bookRepository) {
        // 1. Assign UserCardRepository object to the userCardRepository class member.
        // 2. Assign BookRepository object to the bookRepository class member.
    }    

    public void borrowBook(long userCardId, long bookId) {
        // 1. Use userCardRepository and the given card id to find the user card.
        // 2. Validate the card based on its details. If successfull go further.
        //    If not, then return corresonding response to user.
        // 3. Use bookRepository and the given book id to find the book.
        // 4. Return the fetched book object.
    }

}

然后,在主要部分将所有部分绑定在一起:

// Create and share db connection(s).
// Create and share adapter(s).
// Create mappers.
// Create repositories.
BookBorrowingService bookBorrowingService = new BookBorrowingService(userCardRepository, bookRepository);
Book book = borrowBook(123, 4567890);

回答问题

以这种方式构造代码的优点是,每个类都有很好的分隔职责,符合Single Responsibility Principle。例如,域对象的职责应该只是业务逻辑,而不是数据访问。简而言之,这个SOLID原则与你的第一种方法相矛盾。你也可以说separation of concerns发生了。{/ p>

使用所述方法的唯一缺点是只需要编写更多代码。

备注:

  • 我不是用Java编程的。这就是为什么我没有实现更多代码的原因。
  • 使用依赖注入容器。
  • 忘记静态,全局,单身。
  • 使用接口而不是具体实现。
祝你好运!