我最近正在开展一个项目,在这个项目中,我选择了两种获得相同结果的方法。这是类结构:
class Book < ApplicationRecord
belongs_to :author
end
class Author < ApplicationRecord
has_many :books
end
作者有姓,姓。我希望将给定书籍的作者全名作为实例方法。
在简单的活动记录术语中,由于书籍与作者相关联,我们可以按如下方式获取书籍的作者姓名:
例如在Book类中,我们有:
class Book < ApplicationRecord
belongs_to :author
def author_name
"#{author.first_name} #{author.last_name}"
end
end
我们得到了结果!
但是,根据最小化依赖关系(POODR Book),未来易于更改和更好的面向对象设计的目标,本书不应该知道作者的属性。它应该通过接口与作者对象进行交互。
因此,Book不应该是负责获取作者姓名的人。作者类应该。
class Book < ApplicationRecord
belongs_to :author
def author_name
get_author_name(self.author_id)
end
private
#minimizing class dependecies by providing private methods as external interfaces
def get_author_name(author_id)
Author.get_author_name_from_id(author_id)
end
end
class Author < ApplicationRecord
has_many :books
#class methods which provides a gate-way for other classes to communicate through interfaces, thus reducing coupling.
def self.get_author_name_from_id(id)
author = self.find_by_id(id)
author == nil ? "Author Record Not Found" : "#{author.first_name.titleize} #{author.last_name.titleize}"
end
end
现在,本书只是与作者和作者提供的公共界面进行交互,正在处理从其属性中获取全名的责任,这是一个更好的设计。
我尝试在控制台中将查询作为两个单独的方法运行:
class Book < ApplicationRecord
def author_name
get_author_name(self.author_id)
end
def author_name2
"#{author.last_name} + #{author.first_name}"
end
end
看起来两者都运行相同的查询。
我的问题是
答案 0 :(得分:1)
1)显然不是,它执行书籍的加入&amp;作者表。您所做的内容需要2个查询,而不是1 join
您将拥有book.find(id)
和author.find(book.author_id)
。
2)JOIN应该更快。
3)由于last_name
是一个公共接口,因此绝对不违反设计原则。如果您从外部访问作者的姓氏,那将违反原则:Book.find(1).author.last_name
- 这是一件坏事。正确的是:Book.find(1).authors_last_name
- 并在Model类中访问作者的姓名。
您提供的示例似乎过于复杂。
根据您分享的示例,您只想获得该书作者的全名。因此,分担责任的想法是正确的,但在Author
类中应该是简单的实例方法full_name
,如:
class Author < ApplicationRecord
has_many :books
def full_name
"#{author.first_name.titleize} #{author.last_name.titleize}"
end
end
class Book < ActiveRecord::Base
belongs_to :author
def author_name
author.full_name
end
end
请注意,此代码中没有直接查询。一旦你需要某个地方的作者姓名(在一个视图中,在api响应中等),Rails将使最优化的查询成为可能(但取决于你的用例,例如,如果你调用遍历书籍,它可能是无效的并在循环中调用author
答案 1 :(得分:1)
依赖于调用因子,就像在您的示例中一样,两者都将生成相同的查询。但是,如果您在获取图书/作者时有一个include \ join子句,则两者都会生成不同的查询。
根据rails约定,不建议使用Author.find_by_id(author_id).last_name,因为无论何时调用该方法,它总是会在数据库上触发查询。应该使用rails的关联接口来调用相关对象上的方法,该方法可以智能地从内存中识别对象,或者如果不在内存中则从数据库中获取它。
如果数据量更大,哪一个更有效?
author.last_name更好,因为如果使用它将处理连接,包含和memoization子句,并避免N + 1查询问题。
不从Book类调用author.last_name违反了设计原则吗?
否,您甚至可以使用@Steve建议的委托。