Ruby памятка и шаблон нулевого объекта - PullRequest
0 голосов
/ 25 мая 2018

Здравствуйте, Rubyist,

Интересно, можно ли использовать оператор запоминания в Ruby ||= (то есть: a || a = b при написании a ||= b) можно использовать в пользовательских старых классах ruby, которыедолжен следовать за шаблоном нулевого объекта .

Например, скажем, у меня есть такой класс:

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?

Ответы [ 2 ]

0 голосов
/ 26 мая 2018

В ответ Швернса уже объясняет, почему вы не должны пытаться написать собственный класс фальши.Я просто хочу передать вам быструю и относительно простую альтернативу.

Вы можете добавить метод как 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 class.


В качестве альтернативы вы также можете переопределить сам метод Object # в 1034 * НИЧТО *.Это, однако, может привести к нежелательным результатам, если его используют люди, которые не ожидают отличия от результата # .

class NoThing
  def itself
    nil
  end
end

@thing = @thing.itself || Thing.new
# or
@thing.itself || @thing = Thing.new # exact ||= mimic
0 голосов
/ 25 мая 2018

Вы могли бы шпаргалка из FalseClass и установить те же методы оператора на NoThing.Но я бы не решился сделать это по ряду причин.

В отличие от некоторых других языков, Ruby очень ясно, что существует очень ограниченный набор ложных вещей, false и nil.Неразбериха с этим, вероятно, приведет к путанице и ошибкам в будущем, вероятно, это не стоит того удобства, которое вы ищете.

Кроме того, Null Object Pattern - это возвращение объекта с тем же интерфейсом.как объект, который что-то делает, но ничего не делает.Его появление false победило бы это.Желание написать @thing ||= Thing.new противоречит желанию обнулить объект.Вы всегда хотите установить @thing, даже если Thing.new вернет NoThing, вот для чего нужны нулевые объекты.Код, использующий класс, не заботится, использует ли он 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.Но вам это не нужно, потому что это то, для чего предназначен шаблон нулевого объекта.Если вам это действительно нужно, используйте #nothing? и троичный оператор.

thing = ThingFactory.new_thing(args)
@thing = thing.nothing? ? some_default : thing
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...