Надявам се кореспонденцията ми със Стефан относно константите и символите да е полезна, особено на хората, които са решили да пишат игри и са се замисляли върху това.
Запитване
Пиша игра и имам клас, който представлява обект, който се движи постоянно в една посока. Тази посока се задава като аргумент(direction
) на конструктора. Посоката може да е или left, или right. По-принцип, ако пиша на друг език със сигурност бих имал константи, било то в enum или не, DIRECTION.LEFT, DIRECTION.RIGHT, които да използвам, за да задам посоката. Но в Ruby има символи и мога да подходя по няколко начина.
class MovingObject
def initialize(direction)
...
end
end
Тук direction
ще се очаква да е символ :left
или :right
, като това ще е описано в документацията. Това, което малко ме притеснява тук е, че :left
и :right
малко ми приличат на тези "магически" числа/стрингове в игрите, които заместваме с константи. От една страна не се разбира какво точно :left
и какво точно :right
, но от друга direction
подсказва, че е посока, а и в документацията ще е описано.
class MovingObject
module Directions
LEFT = :left # може и -1 например
RIGHT = :right # тук +1
end
def initialize(direction)
...
end
end
Тук създаването на обект ще става така: MovingObject.new(MovingObject::Directions::LEFT)
. Малко дълго, но все още се вижда ползата от експресивността. Може вместо модул да имаме константа, която съдържа хеш и тогава указване на посока да става MovingObject::DIRECTIONS[:left]
.
Общо взето се чудя дали да е символ или някаква константа, като се замислям върху варианта със символа само заради приликите със стринг, а ако използвам стринг със сигурност бих го сложил в константа.
Предполагам, че по-идиоматично е да се използва символ, но все пак да попитам.
Отговор
Този Java подход е ужасен. Подавай символ. Следва кратка аргументация.
Първо, следния абзац:
Това, което малко ме притеснява тук е, че
:left
и:right
малко ми приличат на тези "магически" числа/стрингове в игрите, които заместваме с константи.
Кои магически числа и константи? 3
е магическо число, което заместваме
с константата INITIAL_NUMBER_OF_LIVES
. Печелим (1) четимост и (2)
възможност за промяна.
Какво печелим като заместим :left
с MovingObject::Directions::LEFT
?
Нищо. :left
е също толкова четимо колкото и LEFT
, само дето няма нужда
от квалификация с пълен път. Няма и за какво да го променяме. Лявото
винаги ще си остане ляво (контрастирай с промяната "Началния брой животи
трябва да се промени на 5
").
Ако държиш да спазваш правила сляпо, то в този случай :left не е
"магическа" константа, за разлика от 3
.
Впрочем, нещо, което би спечелил, е че MovingObject::Direction::LEFT
би
могло да бъде ламбда вместо символ (масив с делта на координати – [1,
0]), докато :left
винаги е символ. Но дори този проблем може да се реши
(и то по-добре) като mapping-а между :left
и ламдбата (масив с делти)
става в MovingObject (или там където).
От гледна точка на клиента на този клас, подаването на :left
е напълно
разумен и адекватен интерфейс.