Это краткое изложение 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, вы можете скучать по Даки!?