我正在经历一些面向对象的设计问题以便学习,我遇到了这个问题,其中书籍目录中需要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>的
答案 0 :(得分:2)
如果您正在实现具有学习目的的控制台应用程序,如果您将CRUD逻辑实现到模型中则不是什么大问题。但我认为不是这种情况。
您实施的此模型书必须只包含对象属性以及getter和setter。您实现的其他CRUD方法必须位于外部层。外部层可能是SERVICE或DAO,它取决于..但你必须知道,如果你像现在这样在模型类中编写一些额外的逻辑,这不是一个好习惯。
答案 1 :(得分:2)
您的Book
对象是一个所谓的&#34;域对象&#34;。它唯一的责任是提供所谓的商业逻辑&#34;。例如:它有一个由类成员定义的状态,它可以包含与状态交互的类方法(用于计算等)。没有其他对象应该知道内部业务逻辑实现。
现在,域对象也被称为&#34; models&#34;。但这可能有点令人困惑。为什么?因为&#34;模型&#34;实际上是一个层。它由三个子层组成:
因此,在您的情况下,域对象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>
使用所述方法的唯一缺点是只需要编写更多代码。
备注:强>