你好Rubyist,
想知道是否可以使用Ruby的memoization运算符||=
(即:a || a = b
,当编写a ||= b
时)可以用于应该遵循{的自定义普通旧ruby类{3}}
例如,假设我有一个类:
class NoThing
def status
:cancelled
end
def expires_on
0.days.from_now
end
def gateway
""
end
end
我在没有班级Thing
时使用。 Thing
在其公共接口中具有相同的status
,expires_on
和gateway
方法。
问题是,在@thing ||= Thing.new
为@thing
或nil
的情况下,如何撰写NoThing
之类的内容?
答案 0 :(得分:2)
您可以<{3}} 来自FalseClass并在NoThing
上设置相同的运算符方法。但出于多种原因,我会犹豫不决。
与其他一些语言不同,Ruby 非常明确表示有一组非常有限的错误,false
和nil
。弄乱这可能会导致混乱和错误,它可能不值得你寻找的方便。
此外,Null对象模式是关于返回一个对象,该对象具有与执行某些操作的对象相同的接口,但它什么都不做。使其显示false
会失败。写@thing ||= Thing.new
的愿望与Null对象的欲望发生冲突。即使@thing
返回Thing.new
,您总是希望设置NoThing
,这是Null对象的用途。如果使用Thing
或NoThing
,使用该类的代码并不在乎。
相反,对于那些想要区分Thing
和NoThing
的情况,我建议使用一些方法,例如#nothing?
。然后设置Thing#nothing?
以返回false
和NoThing#nothing?
以返回true
。这允许您通过询问而不是通过硬编码类名来刺穿封装来区分它们。
class NoThing
def status
:cancelled
end
def expires_on
0.days.from_now
end
def gateway
""
end
def nothing?
true
end
end
class Thing
attr_accessor :status, :expires_on, :gateway
def initialize(args={})
@status = args[:status]
@expires_on = args[:expires_on]
@gateway = args[:gateway]
end
def nothing?
false
end
end
此外,Thing.new
返回Thing
以外的任何内容都是错误的形式。这给应该是简单的构造函数增加了额外的复杂性。它不应该返回nil
,它应该抛出异常。
相反,请使用Factory Pattern保持Thing
和NoThing
纯粹而简单。将工作决定是在Thing
还是NoThing
中返回ThingBuilder
或ThingFactory
。然后,您拨打ThingFactory.new_thing
来获取Thing
或NoThing
。
class ThingFactory
def self.new_thing(arg)
# Just something arbitrary for example
if arg > 5
return Thing.new(
status: :allgood,
expires_on: Time.now + 12345,
gateway: :somewhere
)
else
return NoThing.new
end
end
end
puts ThingFactory.new_thing(4).nothing? # true
puts ThingFactory.new_thing(6).nothing? # false
然后,如果你真的需要它,工厂也可以有一个单独的类方法,它返回nil
而不是NoThing
,允许@thing ||= ThingFactory.new_thing_or_nil
。但你不应该需要它,因为那是Null对象模式的用途。如果您确实需要它,请使用#nothing?
和三元运算符。
thing = ThingFactory.new_thing(args)
@thing = thing.nothing? ? some_default : thing
答案 1 :(得分:2)
在Schwerns answer已经解释了为什么你不应该尝试编写一个自定义的错误类。我只是想给你一个快速而相对简单的选择。
您可以在评估实例的 Thing 和 NoThing 上添加方法:
class Thing
def thing?
true
end
end
class NoThing
def thing?
false
end
end
现在您可以通过以下方式分配@thing
:
@thing = Thing.new unless @thing&.thing?
这假定@thing
始终是 NilClass , Thing 或 NoThing 类。
或者,您也可以覆盖 NoThing 中的Object#itself方法。然而,如果那些不期待 #itself 的不同结果的人使用,这可能会产生不必要的结果。
class NoThing
def itself
nil
end
end
@thing = @thing.itself || Thing.new
# or
@thing.itself || @thing = Thing.new # exact ||= mimic