长期无安全方法链
所以我知道一些我所知道的不同方法,我想探索各种方法的优缺点,这些方法有以下几种:
- 可读性
- 性能
- 易于调试
- OO原则(低耦合和高内聚)
从主动支持中明确使用try方法
person.try(:pet).try(:name).try(:upcase)
使用救援无
person.pet.name.upcase rescue nil
使用&&运算符链
person && person.pet && person.pet.name && person.pet.name.upcase
猴子修补Object类,请参阅https://gist.github.com/thegrubbsian/3499234获取原始要点
class Object def try_all(*methods) values = [self] methods.each do |method| value = values.last.try(method) return nil if value.nil? values << value end values.last end end person.try_all(:pet, :name, :upcase)
不要使用nil安全代码,而是在调用代码之前validation数据
#not a good implementation by any means def person_has_pet_with_name? person begin return true if !person.pet.name.nil? rescue return false end end person.pet.name.upcase if person_has_pet_with_name?(person)
我个人对猴子修补的看法一般是不要做,除非没有别的办法(甚至比我三思而后,如果我真的想要修补补丁)。
除了Rails已经膨胀了很多物体。 所以我不建议定制腹胀物体。
为什么不通过经典的委托方法来避免违反德米特的规律:
class Person attr_accessor :pet delegate :name_upcased, to: :pet, prefix: true, allow_nil: true end class Pet attr_accessor :name def name_upcased @name.upcase if @name end end @person.try :pet_name_upcased
你也可以阅读德米特的法律, 不要违反得墨忒耳的法则! 和模块#委托 。
至少我不会坚持使用Object#try只要一个简单的条件解决它,因为查看’try’的来源它比条件更昂贵。
我会避免使用长链,因为它们明显违反了得墨忒耳法 :
person.try(:pet).try(:name).try(:upcase)
如果您想要测试这样的代码以及某种方式存根,那么您的测试将变得非常复杂。 我建议像下面这样的解决方案,其中这个链的复杂性在所涉及的类之间划分。 这是面向对象的方式。 在这里,没有一个类对另一个有太多了解。
class Person def upcased_pet_name pet.try(:upcased_name) end end class Pet def upcased_name name.try(:upcase) end end # Simple method call. nil checks are handled elsewhere person.try(:upcased_pet_name)
在您的测试中,现在对于存根person
更容易,并且您的代码更容易阅读。
Demeter法则的问题在于你的class级对彼此了解太多,使他们更加紧密耦合,这反过来又使他们更加错误,更难以测试。
根据我的经验,最常见的违反“得墨忒耳法”的行为是在意见中。 既然你试图将名称大写,我猜这就是这种情况。 SOOOO ….
你能使用视图助手吗? 对不起,Rails不太好,所以这是假的:
def upcased_name(entity_with_name) if entity_with_name != nil entity_with_name.name.try(:upcase) end end
然后在你看来,你只需要打电话
<% upcased_name(person.pet) %>
您可以通过向upcased_pet_name
注入不同的值来测试upcased_pet_name。
现在:
- 您的视图只知道可以访问“人员”,并且可以访问名为
upcased_name
的视图upcased_name
。 - 您的
Person
模型只知道它有Pet
方法。 - 您的viewhelper只知道它可以接收名称方法或
nil
的实体。
繁荣! 你的课程只知道他们的朋友,而不知道朋友的朋友。
- 两个ActiveSupport :: TimeWithZone对象之间的比较失败
- 匿名模块可以用于什么目的?
- Ruby没有找到新版本的OpenSSL
- NoMethodError:ActiveSupport的未定义方法`halt_callback_chains_on_return_false =’:模块
- ActiveSupport中mattr_accessor和cattr_accessor之间的区别?
- 如何使用Active Support核心扩展?
- 如何将TZInfo标识符转换为Rails TimeZone名称/密钥
- 如何在Ruby中将JSON转换为XML?
- 如何在Rails的开发模式下自动为每个请求重新加载gem代码?