Об именах методов

июля 21, 2012  |  Published in Ruby, Основы  |  2 Comments

rubyДавайте выделим несколько наиболее выразительных групп методов:

  1. Методы проверки, они возвращают true или false (методы — предикаты).
  2. Методы копирующие объект и возвращающие его копию предварительно выполнив на ней определенные действия.
  3. Методы преобразования, они изменяют объект для которого применяются (bang!-методы, методы — мутаторы).
  4. Методы основной смысл которых — что-то передать, (я их называю методы — ссылки, к ним относятся, например аксессоры) (методы доступа).
  5. Методы, которые устанавливают значения свойств/переменных (setter’ы)

Чтобы было понятнее о чем я, представляю примеры всех 4 типов методов:

1. Методы проверки (предикаты)

[1, 2, 3].empty? # => false
nil.nil? # => true

2. Методы выполняющие действия на копии

a = [3, 5, 1, 4, 2]
b = a.sort

a.object_id # => 69698220
b.object_id # => 69657420

3. Bang! — методы (мутаторы)

a = [3, 5, 1, 4, 2]

b = a.sort!

a.object_id # => 68743880
b.object_id # => 68743880

4. Методы — «ссылки» (getter’ы, методы доступа)

class Hero
  attr_accessor :name, :last_name, :full_name

  def initialize(name, last_name)
    @name = name
    @last_name = last_name
    @full_name = "#{name} #{last_name}"
  end
end

john = Hero.new("John", "Wayne")

john.name # => "John"
john.last_name # => "Wayne"
john.full_name # => "John Wayne"

5. Методы — «установщики» (setter’ы)

john.name = "Jonathan" # @name = "Jonathan"
john.allow_kill_cowboys # @can_kill_cowboys = true
john.set_gun("Colt Python") # @gun = "Colt Python"

В этой статье я хочу дать несколько рекомендаций касательно того, как правильно называть методы в Ruby. Большая часть информации будет полезна и для разработки на других языках, например Python, C, Java, JavaScript и т.д.

Собственно вот краткий перечень рекомендаций:

1. Имена методов проверки должны заканчиваться на ?, иногда могут иметь префикс «is_», «can_» и т.д. Например: can_push_to_master?, empty?, present?

2. Методы работающие с копией или порождающие новый объект другого типа должны иметь имена действий или намекать на дейсвие, например: sort, join, split, to_s, to_a и т.д.

3. Имена методов изменяющих сам объект (Bang! методы) должны быть названиями действий и заканчиваться на !. Например: allow_to_push_to_master! — изменяет состояние объекта добавляя новые привилегии; sort! — сортирует текущий массив, а не его копию. ! — намекает на «опасность» метода так как входные данные после его применения теряются (например, мы получаем отсортированный массив вместо беспорядочного, который поступил на вход).

В примере выше метод allow_to_push_to_master! может иметь имя без !. Это зависит от того, как сильно меняются внутренности объекта, например, если просто меняется значение переменной @can_push_to_master = true, то это, вероятно, ерунда, а вот если меняется роль пользователя или еще какие-либо серьезные модификации объекта происходят, то следует добавить «!» в конец имени метода. Также «!» добавляется в имена т.с. forсe-методов, которые выполняют какое-то действие напрямую, например минуя валидацию или вызов коллбеков.

Не все имена методов, которые меняют объект следует называть именами с «!» в конце, например Array#shift удаляет первый элемент в массиве и возвращает его значение. Несмотря на то, что массив изменяется метод не имеет «!» в конце. В некоторых случаях предполагается, что изменение объекта методом очевидно, потому «!» в названии метода опускается (я так думаю).

4. Методы основная задача которых заключается в возврате какого-либо значения должны иметь название возвращаемого значения, могут иметь префиксы «read_», «get_», «to_» и д.р.. Часто этот тип объединен со 2 типом. Примеры: to_s — возвращает строковое представление объекта, to_a — представление объекта в виде массива. В этих примерах мы одновременно видем то, что метод выполняется действие «to string» и что его основная задача вернуть значение «string» — строковое представление объекта. Еще один пример — метод read_attribute в моделях, аналогично методу to_s он показывает и действие «read» и то, что возвращается — «attribute». Синонимом для read_attribute(:attribute_name) в Rails являются методы генерируемые динамически, которые носят имя того, что нам необходимо получить — attribute_name. К таким методам относятся еще и scope’ы из ActiveRecord, которые позволяют формировать запрос на выбор некоторых записей, например:

scope :deleted, where(:is_deleted => true)
scope :active, where(:is_deleted => false)

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

5. Методы устанавливающие значение должны заканчиваться на «=», что свойственно set-аксессорам (setter’ам), например:

user.name = "Vladimir"
user.role = "admin"

Иногда эти методы могут иметь префикс «set_», «write_», «allow_» если метод не принимает аргументов и устанавливает значение какой-то переменной в true, в таких случаях «=» в конце названий методов не используется, например:

user.set_name("Vladimir")

6. Общее правило звучит так: Имена методов должны быть говорящими, из них должно быть понятно, что они делают и что они возвращают. Одно только неправильное именование методов превращает ваш код в говнокод.

Tags: , ,

Responses

  1. says:

    июля 21, 2012 at 14:46 (#)

    Про бэнг методы хорошо написано в :

    The names of potentially «dangerous» methods (i.e. methods that modify self or the arguments, exit! (doesn’t run the finalizers like exit does), etc.) should end with an exclamation mark if there exists a safe version of that dangerous method.

    Define the non-bang (safe) method in terms of the bang (dangerous) one if possible.

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

  2. says:

    июля 21, 2012 at 20:38 (#)

    Насчет префиксов get_, set_ и подобных — это какие-то пережитки явы, имхо. Вполне достаточно названий, генерируемых attr_reader/attr_writer, и самописных методов с названиями в том же стиле:

    class User
      def name
        @name
      end
    
      def name=(new_name)
        @name = new_name
      end
    end
    
    user = User.new
    user.name = 'Вася'
    user.name # => 'Вася'
    

Leave a Response

Для подсветки кода используйте BB - коды: [language]...[/language], где language может быть: ruby, javascript, css, html.