Это краткое изложение Duck Typing в Практическом объектно-ориентированном проектировании: Agile Primer Using Ruby

Что такое утиная печать?

Согласно Википедии,

Утиная печать в компьютерном программировании — это применение утиного тестаЕсли она ходит как утка и крякает как утка, то это должна быть утка — чтобы определить, является ли объект может использоваться для определенной цели. При обычной типизации пригодность определяется типом объекта. В утиной типизации пригодность объекта определяется наличием определенных методов и свойств, а не типом самого объекта.

Ну, ВТФ...!?

Короче говоря, объект знает, как обращаться с методами. Итак, что бы это ни было, все, что вам нужно сделать, это дать метод объекту.

Конкретный пример

Давайте рассмотрим детали с классом, который обрабатывает расчет зарплаты. Этот класс обрабатывает, как рассчитать зарплату сотрудников с помощью case . В этом случае вы должны добавить логику случая при добавлении нового типа сотрудника, поэтому метод будет легко расширяться и его будет сложно поддерживать.

class Salary
  def calc_salaries(employees)
    employees.each do |employee|
      case employee
      # if employee is proper, use this logic
      when Proper
        employee.calc_insurance_fee(insurance)
      # if employee is part-timer, use this logic
      when PartTimer
        employee.calc_tax_reduction(working_hour)
      # if employee is outsourcing, use this logic
      when Outsourcing
        employee.calc_special_bonus(special_reward)
      end
    end
  end
end

Метод уже 10 строк. Если бы кто-то, кто знал Правила Санди Мец для разработчиков, увидел эту логику, он, возможно, разозлился бы. Конечно, я разозлюсь и сам перепишу эту логику.

С другой стороны,

Каждый объект сотрудника знает, к какому классу он принадлежит. Поэтому отправьте ему метод, который вычисляет его собственную зарплату (в этом случае давайте сделаем calc_salary), вместо использования case. И добавьте метод calc_salary к каждому классу сотрудников.

class Salary
  def calc_salaries(employees)
    employees.each do |employee|
      # you just call employee's calc_salary method
      employee.calc_salary
    end
  end
end
class Proper
  def calc_salary
    # write specific logic
  end
end
class PartTimer
  def calc_salary
    # write specific logic
  end
end
class Outsourcing
  def calc_salary
     # write specific logic
  end
end

Теперь метод calc_salaries состоит всего из 3 строк, и вам не нужно добавлять определенную логику в метод, когда у вас есть новые типы сотрудников!

Если вы хотите, чтобы у каждого сотрудника был метод calc_salary, вы можете написать так.

class Employee
  def calc_salary
    raise NotImplementedError.new("You must implement #{self.class}##{__method__}")
  end
end
class Proper < Employee
  def calc_salary
    # write specific logic
  end
end
class PartTimer < Employee
  def calc_salary
    # write specific logic
  end
end
class Outsourcing < Employee
  def calc_salary
     # write specific logic
  end
end

Используя наследование, вы можете указать, что реализовать в каждом подклассе.

Достоинство Duck Typing

Вы можете хранить код просто, понятно и прозрачно.
Вам не нужно делить логику в методе по типам объектов, поэтому легко добавлять новые классы и меньше риск получить деградацию.

Недостатки утиного набора текста

В принципе недостатков нет. Если я должен что-то сказать, то нужно иметь навык ООП (но это совсем не недостаток!!!!)

Как найти утиную печать

Если разделить логику по падежам, kind_of?, is_a? или responses_to, вы можете скучать по Даки!?