10 приемов, разрушающих хрупкую красоту кода

    Статья приводится в сокращении из-за ограничения на объем материала.

    Для меня программирование — это не только технология, но и, во многом — искусство. И, поэтому, большое значение имеет красота кода.

    Последние несколько лет я собирал приемы программирования, разрушающие в программном коде его утонченную красоту:
    1. Объявление всех переменных в начале программы;
    2. Возврат результата функции через ее параметр;
    3. Отсутствие локальных функций;
    4. Отсутствие else if;
    5. Использование параллельных массивов;
    6. Хранение размера массива в отдельной переменной;
    7. Доступ к свойствам объекта через obj.getProperty() и obj.setProperty(value);
    8. Использование рекурсии для вычисления факториалов и Чисел Фибоначчи;
    9. Отсутствие именованных параметров функции;
    10. Невозможность объявления объектов «на лету».
    Наверняка, в некоторых местах вы будете со мной несогласны — ведь чувство красоты у всех разное.


    Объявление всех переменных в начале программы


    В двух словах:

    Переменные должны объявляться в начале логического блока, в котором они используются, а НЕ в начале функции или программы.


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

    Данные, относящиеся к тому или иному модулю программы, принято объявлять в начале этого модуля. Локальные переменные объявляются в начале функции; свойства, относящиеся ко всему классу, объявляются в начале определения класса и т.д.

    Однако функции не являются последним уровнем в иерархии программы. Любая нетривиальная функция состоит из блоков, реализующих отдельны шаги выполнения алгоритма. Тех самых блоков, которые никак не обособляются в коде, разве что отделяются друг от друга парочкой пустых строк.

    Однако эти блоки — полноценные элементы в иерархии программы. И они тоже имеют право на собственные «локальные» переменные! Которые объявляются в начале этого блока и используются только в его пределах.

    Объявление всех переменных в начале функции — страшное зло[1].
    Это приводит к смешению переменных, относящихся ко всей функции, с переменными, относящимися только к ее отдельному блоку.

    Это разрывает блок на две части: объявления данных (в начале функции) и использования этих данных (в самом блоке).

    Это усложняет комментирование блока: в одном месте мы комментируем переменные, но не знаем, как их использовать; в другом месте мы комментируем алгоритм, но не знаем, с какими данными он работает.

    Пример:

    Вы только представьте: У нас есть функция в 300 строк кода [2]. Где-нибудь на 200-й строке нам надо поменять две переменные местами. Для этого мы лезем на 200 сток выше в начало функции, объявляем переменную temp, которая не имеет никакого отношения ко всей функции, а используется только один раз в одном месте, потом опять возвращаемся к 200-й строке и меняем переменные местами… По-моему, это просто кошмар.

    Хуже всего, что существуют языки, которые считают себя умнее разработчика и заставляют объявлять все переменные в начале функции. Например, такой уважаемый язык как Pascal/Delphi. Чего я ему простить не могу…


    Возврат результата функции через ее параметр


    В двух словах:

    Функция должна возвращать результат, зависящий от ее параметров, а НЕ принимать результат в качестве аргумента.


    Понятие функции (как в математике, так и в программировании) имеет четкий смысл: вычисление результата, зависящего от аргументов.

    В нормальном программном коде ясно видно, что является результатом, а что аргументами: результат = функция (аргумент1, аргумент2).

    Однако часто встречается прием, при котором возвращаемое значение передается в качестве аргумента функции: функция (аргумент1, аргумент2, &результат).

    Этот прием ужасен. При его использовании не видно, от чего функция зависит, а что возвращает.

    Чаще всего, в применении этого приема виноваты не сами разработчики, а языки программирования.

    Существуют две основные причины, по которым языки заставляют нас прибегать к этому приему.

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

    Пример:

    Предположим, что мы хотим на C++ написать функцию, перемножающую две матрицы и возвращающую получившуюся матрицу в качестве результата. Матрицы мы решили представлять в виде двумерного массива.

    Но мы не можем объявить в функции результирующий двумерный массив, а затем вернуть его:

    int mtxResult [10][10] = mult (mtxA, mtxB);<br><br>Поэтому нам придется сначала вне функции объявить результирующий массив, а затем вызвать функцию перемножения, передав результат в качестве аргумента:

    mult (mtxA, mtxB, mtxResult);

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

    Пример:

    Можно хранить матрицу не в виде двумерного массива, а в виде структуры или объекта:

    Matrix mtxResult = mult (mtxA, mtxB);

    Код станет менее лаконичным (из-за объявления дополнительных структур), но, зато, гораздо более красивым.

    Вторая причина состоит в том, что в большинстве языков программирования функция не может возвращать несколько значений.

    Пример:

    Предположим, что мы хотим на C++ написать функцию, решающую квадратное уравнение. Функция принимает в качестве аргумента коэффициенты a, b, c и возвращает три результата: число корней, x1 и x2.

    Однако вернуть сразу три значения в C++ невозможно:

    intRootsCount, numX1, numX2 = quadraticEquation (numA, numb, numC)<br><br>Поэтому нам придется часть результатов выполнения функции передать через указатель в качестве аргументов:

    intRootsCount = quadraticEquation (numA, numB, numC, &numX1, &numX2);

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

    Пример:

    Можно возвращать результаты решения квадратного уравнения в виде структуры с тремя свойствами[3]:

    QuadrEqResult qerResult = quadraticEquation (numA, numB, numC);<br>intRootsCount = qerResult.count;<br>numX1 = qerResult.x1;  <br>numX2 = qerResult.x2;

    Однако и в первом, и во втором случае, при использовании структур или объектов, мы можем столкнуться с еще одной проблемой: необходимостью отдельно описывать эти структуры или классы.

    Причем во многих языках, например на C++, мы не можем описать структуру или класс внутри самой функции (см. следующий раздел). Нам придется описывать их отдельно от функции, делая их неподчиненными функции сущностями, что уродует иерархию программы.

    Слава богу, в других языках классы можно описывать прямо внутри функции, а, например в JavaScript можно просто возвратить объект, нигде отдельно не описывая его структуру.

    Пример:

    function quadraticEquation (numA, numb, numC)<br> {<br> //...<br> return ({<br>     count: intRootsCount,<br>     x1: intX1,<br>     x2: intX2<br>     });<br> }<br><br>var objResult = quadraticEquation (numA, numB, numC);<br>intRootsCount = objResult.count;<br>numX1 = objResult.x1;  <br>numX2 = objResult.x2;  <br>

    Вот это настоящая красота!


    Отсутствие локальных функций


    В двух словах:


    Как уже говорилось, программные системы (как объектно-ориентированные, так и процедурные) иерархичны и делятся на вложенные друг в друга модули (впрочем, это очевидно).

    Каждый из модулей располагает своими локальными ресурсами, используемыми только в рамках этого модуля.

    Например, локальные переменные являются ресурсами модуля функции.

    Однако ресурсами функции являются не только переменные! Подфункции, классы, структуры и т.д. также являются полноправными ресурсами функции, подчиненными ей, и используемыми только в ее рамках.

    Объявление функций, структур и т.д. вне функции, которой они иерархически подчиняются — очень плохой прием.
    Но многие языки программирования, например C++, не поддерживают локальные функции, классы, структуры и т.д.

    Пример:

     

    Язык Pascal/Delphi поддерживает локальные функции, но, заставляет их объявлять только в начале функции. Это не так страшно, как объявлять только в начале все переменные, но тоже, иногда, бывает достаточно некрасиво.

    Пример:

    Предположим, у нас есть функция из нескольких блоков кода, каждый из которых выполняет отдельный шаг всего алгоритма.

    Мы решили переписать один их блоков в рекурсивной форме.

    Для этого мы переделали его в рекурсивную локальную функцию… после чего вынуждены перетащить этот блок кода (ставший теперь локальной функцией) с того места, где он используется, в начало основной функции.

    program main ();
      function block3 (param: integer): integer;   {      (4)--¬           }
      begin                                        {       ^   v           }
        Рекурсивный блок кода #3                   {       ^   v           }
      end;                                         {       ^   v           }
      {}                                           {       ^   v           }
      procedure block5 (param: integer);           {       ^   v  (7)--¬   }
      begin                                        {       ^   v   ^   v   }
        Рекурсивный блок кода #5                   {       ^   v   ^   v   }
      end;                                         {       ^   v   ^   v   }
      {}                                           {       ^   v   ^   v   }
     begin                                         {       ^   v   ^   v   }
        блок кода #1                               {  (1)  ^   v   ^   v   }
        {}                                         {   v   ^   v   ^   v   }
        блок кода #2                               {  (2)  ^   v   ^   v   }
        {}                                         {   v   ^   v   ^   v   }
        Прокручиваем на самый верх                 {   v   ^   v   ^   v   }
        и находим код рекурсивной функции block3   {   v   ^   v   ^   v   }
        block3 (param);                            {  (3)---   v   ^   v   }
        {}                                         {           v   ^   v   }
        блок кода #4                               {          (5)  ^   v   }
        {}                                         {           v   ^   v   }
        Прокручиваем на самый верх                 {           v   ^   v   }
        и находим код рекурсивной функции block5   {           v   ^   v   }
        block5 (param);                            {          (6)---   v   }
        {}                                         {                   v   }
        блок кода #6                               {                  (8)  }
      end.
    

    Как теперь прикажите читать эту функцию? Первый блок кода, второй, третий, пока, все нормально и понятно. Вдруг, хлоп, вызов рекурсивной подфункции, для чтения которой прокручиваем код к самому началу. Затем опять возвращаемся, назад и продолжаем читать обычные блоки. Не намного лучше, чем читать спагетти-код с goto.

    К счастью, поддержка локальных функций есть почти во всех «новых» языках, как динамических (JavaScript, Python), так и классических (Java). И надо только воспользоваться этой возможностью.


    Отсутствие else if


    В двух словах:

    Уровень вложенности блока должен соответствовать его иерархическому положению в программе.


    Блоки кода, имеющие одинаковый уровень вложенности, относятся к одному уровню иерархии программы. Соответственно, блоки кода с большим уровнем вложенности являются подчиненными по отношению к блокам с меньшей вложенностью.

    Вроде бы, это очевидный факт.

    Тем не менее, иногда, особенно в учебной литературе, я встречаю нарушение этого правила:

    Когда блоки, логически находящиеся на одном уровне иерархии в программе, тем не менее, имеют разный уровень вложенности. Или наоборот, имеют разный уровень иерархии, но одинаковый уровень вложенности.
    Пример 1:

    Предположим, что нам надо приветствовать пользователя, в зависимости от времени суток. Приветствия «Спокойной ночи», «Доброе утро», «Добрый день», и «Добрый вечер» совершенно равноправны и относятся к одному уровню иерархии программы.

    Поэтому этот код, где приветствия имеют разный уровень вложенности, страшно уродлив:

    if (numHour >= 0 && numHour < 6)<br>  {<br>  print ("Спокойной ночи!");<br>  }<br>else<br>  {<br>  if (numHour >= 6 && numHour < 12)<br>    {<br>    print ("Доброе утро!");<br>    }<br>  else<br>    {<br>    if (numHour >= 12 && numHour < 18)<br>      {<br>      print ("Добрый день!");<br>      }<br>    else<br>      {<br>      print ("Добрый вечер!");<br>      }<br>    }<br>  }<br><br>Его надо переписать так, чтобы равноправные блоки имели равный уровень вложенности:

    if (numHour >= 0 && numHour < 6)<br>  {<br>  print ("Спокойной ночи ");<br>  }<br>else if (numHour >= 6 && numHour < 12)<br>  {<br>  print ("Доброе утро!");<br>  }<br>else if (numHour >= 12 && numHour < 18)<br>  {<br>  print ("Добрый день!");<br>  }<br>else<br>  {<br>  print ("Добрый вечер!");<br>  }

    Пример 2:

    Предположим теперь, что мы должны проверить, зарегистрирован ли пользователь в системе, и, если зарегистрирован, то поприветствовать, а если нет — послать вон.

    В данном случае, блоки кода приветствия имеют подчиненный уровень по отношению к блоку «зарегистрирован», а блок «пошел вон» — подчиненное к блоку «не зарегистрирован». Поэтому уродлив код, в котором все блоки имеют одинаковый уровень вложенности:

    if (!isRegistred ())<br>  {<br>  print ("Вы не зарегистрированы в системе. Идите вон!");<br>  }<br>else if (numHour >= 0 && numHour < 6)<br>  {<br>  print ("Спокойной ночи ");<br>  }<br>else if (numHour >= 6 && numHour < 12)<br>  {<br>  print ("Доброе утро!");<br>  }<br>else if (numHour >= 12 && numHour < 18)<br>  {<br>  print ("Добрый день!");<br>  }<br>else<br>  {<br>  print ("Добрый вечер!");<br>  }<br>

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

    if (!isRegistred ())<br>  {<br>  /*<br>  Не зарегистрирован<br>  */<br>  print ("Вы не зарегистрированы в системе. Идите вон!");<br>  }<br>else<br>  {<br>  /*<br>  Зарегистрирован<br>  */<br>  if (numHour >= 0 && numHour < 6)<br>    {<br>    print ("Спокойной ночи!");<br>    }<br>  else if (numHour >= 6 && numHour < 12)<br>    {<br>    print ("Доброе утро!");<br>    }<br>  else if (numHour >= 12 && numHour < 18)<br>    {<br>    print ("Добрый день!");<br>    }<br>  else<br>    {<br>    print ("Добрый вечер!");<br>    }<br>  }<br>

    В некоторых старых языках программирования (по-моему, в каких-то древних версиях Паскаля или что-то в этом роде) были операторы if и else, но отсутствовал оператор else if. Они навязывали прием, при котором каждый последующий блок в цепочке сравнений имел все больший уровень вложенности (как в примере №1).

    Но сейчас эти языки вымерли как динозавры, и все нормальные языки поддерживают else if.

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


    Использование параллельных массивов


    В двух словах:

    При работе с вложенными данными следует соблюдать правила иерархии: свойства должны хранится внутри объекта,а НЕобъект — внутри свойства.


    При работе с вложенными данными следует соблюдать правила иерархии.

    Во-первых, связанные данные должны иметь общий контейнер.

    Благодаря этому объект выглядит именно как объект, и не распадается на множество независимых свойств.

    Также, мы можем работать с объектом как с единым целым, например, передавать его в качестве параметра функции и т.д.

    Пример:

    В данном примере видно, что lastName, firstName, sirName относятся к одному объекту: objPeople1.

    objPeople1.lastName = "Пупкин";<br>objPeople1.firstName = "Василий";<br>objPeople1.sirName  = "Иванович";<br><br><br>Мы можем работать с этим объектом не как с набором свойств, а как с единым целым:

    doSomething (objPeople1);<br>

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

    Пример:

    За время написания статьи Путин успел переехать:

    World.Russia.Moscow./*Kremlin*/WhiteHouse.Putin<br>

    Пример:

    А в данном примере только с помощью иерархического порядка можно определить, что

    синий пакет находится в красном:

    redBag.blueBag.myThing<br><br>…или же красный в синем:

    blueBag.redBag.myThing

    В программах на объектно-ориентированных языках это правила иерархии почти всегда соблюдается.

    Однако соблюдать правила иерархии при обращении к данным надо не только в объектно-ориентированных языках! Тем не менее, в программах на процедурных языках это правило нередко нарушается.

    Одним их вопиющих примеров уродского обращения к данным является использование так называемых параллельных массивов.
    Пример:

    Предположим нам надо сохранить данные о советских лидерах, содержащие следующие свойства: фамилию, имя и отчество.

    Каждое из этих свойств хранится в отдельном массиве:

    char *lastNames  [3] = {"Ленин",    "Сталин",        "Хрущев"};<br>char *firstNames [3] = {"Владимир", "Иосиф",         "Никита"};<br>char *sirNames   [3] = {"Ильич",    "Виссарионович", "Сергеевич"};<br>const lenin = 0, stalin = 1, khrushchev = 2;

    И обращение к данным происходит следующим образом:

    //Печатаем: "Никита Хрущев"<br>cout<<firstNames[khrushchev]<<" "<<lastNames[khrushchev];

    При использовании параллельных массивов нарушаются все возможные правила работы с иерархичными данными.

    Во-первых, записи распадаются на множество несвязанных полей. Мы не можем работать с записью как с единым объектом.

    Пример:

    Вместо работы с объектом как с единым целым, нам приходится работать со множеством полей:

    doSomething(lastNames [lenin], lastNames [lenin], sirNames [lenin]);

    Во-вторых, обращение к вложенным данным происходит «задом-наперед».

    Пример:

    Запись

    firstNames[khrushchev]

    означает не Хрущева, хранящего свойство firstName, а, наоборот, свойство, хранящее внутри себя Хрущева!

    <Текст сокращен из-за ограничений хабрапарсера...>

    Разумеется, самым правильным и красивым решением является создание массива объектов или структур.

    Пример:

    People *leaders [7] = <br>    {<br>      new People ("Ленин",    "Владимир",   "Ильич"),<br>      new People ("Сталин",   "Иосиф",      "Виссарионович"),<br>      new People ("Хрущев",   "Никита",     "Сергеевич"),<br>      new People ("Брежнев",  "Леонид",     "Ильич"),<br>      new People ("Андропов", "Юрий",       "Владимирович"),<br>      new People ("Черненко", "Константин", "Устинович"),<br>      new People ("Горбачев", "Михаил",     "Сергеевич")<br>    };<br><br>//Эти константы – только для удобства чтения примера. В реальном коде их не будет<br>const lenin = 0, stalin = 1, brezhnev = 2, gorbachev = 6;<br><br><br>//Печатаем: "Владимир Ленин"<br>cout<<leaders [lenin].firstName<<" "<< leaders [lenin].lastName;<br><br>//Печатаем: "Леонид Брежнев"<br>cout<<leaders [brezhnev].firstName<<" "<< leaders [brezhnev].lastName;<br><br>//Печатаем: "Михаил Горбачев"<br>cout<<leaders [gorbachev].firstName<<" "<< leaders [gorbachev].lastName;<br>


    Доступ к свойствам объекта через
    object.getProperty () и object.setProperty (value)


    В двух словах:

    Для доступа к данным должны использоваться свойства, а НЕ методы.


    У полей и методов объектов есть свое четкое предназначение:

    Поля — хранят данные;

    Методы — реализуют поведение объекта.

    В нормальном коде ясно видно, где идет работа с данными, а где реализуется логика поведения объекта:

    Работа с данными: objObject.property1 = "value1";

    Поведение объекта: objObject.doSomething (param1, param2);

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

    Нарушается естественная конструкция для доступа к данным через оператор присваивания. Оператор присваивания самой своей сутью подразумевает присваивание.

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

    Во-вторых, чтение и запись свойства реализуется по-разному, что противоречит сути поля.

    Пример 1:

    При использовании свойств, для доступа к полю, как для чтения, так и для записи мы используем одну конструкцию: objObject.property1

    intA = objObject.property1; //Чтение<br>objObject.property1 = intB; //Запись<br>

    При использовании методов, для чтения и для записи поля используются разные конструкции:objObject.getProperty1 () и objObject.setProperty1 ():

    intA = objObject.getProperty1 ();//Чтение<br>objObject.setProperty1 (intB);  //Запись<br>

    К полю не будет возможности применять стандартные операторы работы с данными, такие как ++, += и др.

    Пример 2:

    Мы хотим увеличить значение свойства на 1.

    При использовании свойств мы можем воспользоваться естественным для этого действия оператором инкрементации:

    objObject.property1++;

    При использовании методов, для выполнения элементарного действия приходится писать такую кашу:

    objObject.setProperty1 (objObject.getProperty1 () + 1);

    Для обращения к защищенным полям объекта как к данным, используются свойства.

    Пример 3:

    Класс Person хранит поле _money, доступ к которому осуществляется через свойство money:

    class Person<br>  {<br>  private long _money;<br><br>  public long money<br>    {<br>    get<br>      {<br>      return (_money);<br>      }<br>    set <br>      {<br>      _money = value;<br>      }<br>    }<br>  }

    Теперь мы можем нормально работать с данными:

    Person psnBillGates = new Person ();
    lngOldRiches = psnBillGates.money; //Чтение
    psnBillGates.money = lngNewRiches; //Запись
    psnBillGates.money += 1000000000; //Инкрементация

    Свойства поддерживает большое количество современных языков: Delphi, C#, Python, Ruby и др.

    Однако немало языков свойства не поддерживают: C++, Java и даже гибкий и красивый JavaScript[4]

    Знаете, есть две вещи, которые обязательно надо добавить в JavaScript. Но это не классы и строготипизированные переменные, как думают многие. Отсутствие классов и строгих типов — это не баг, а фича, дающая JavaScript такую гибкость.

    Две возможности, которых действительно не хватает в JavaScript — это перегрузка операторов и поддержка свойств[5].


    Использование рекурсии
    для вычисления факториалов и Чисел Фибоначчи


    В двух словах:

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


    Здесь, в отличие от предыдущих разделов, я не буду столь категоричен.

    Ибо рекурсия, или даже философия рекурсии, штука не такая простая.

    И вопрос, когда следует (вернее, когда красиво) применять рекурсию, а когда нет, не столь однозначен.

    Ну, в функциональных языках (таких как Lisp или Haskell) все понятно: рекурсия применяется всегда, когда надо выполнить любые повторяющиеся действия. Там даже сумма элементов массива (там он называется списком) определяется рекурсивно как сумма первого элемента + сумма оставшейся части. В этих языках такой подход гармонирует с философией языка и, потому, красив.

    В императивных же языках все сложнее.

    Мне кажется, что в этих языках смысл рекурсии состоит в разбиении задачи на несколько подобных подзадач более низкого ранга. А тех в свою очередь — на насколько под-подзадач, и так, в геометрической прогрессии, пока мы не дойдем до тривиального случая.

    Пример 1:

    При рекурсивном обходе дерева, мы разбиваем дерево на несколько поддеревьев (ветвей), каждое из поддеревьев на под-подеревья, и так, пока не дойдем до листов.

    Пример 2:

    При вычислении определителя матрицы, мы находим определители нескольких подматриц меньшего порядка (миноры), для нахождения же миноров, мы разбиваем каждую из подматриц на под-подматрицы и т.д. пока не дойдем до матриц 2*2.

    Пример 3:

    При сортировке массива слиянием мы сортируем несколько (обычно два) подмассивов этого массива. Для сортировки каждого подмассива мы сортируем их под-подмассивы, пока не дойдем до подпод…подмассивов длины 2.

    Поскольку задача разбивается именно на несколько подобных подзадач, то количество данных (локальных переменных) на каждом шаге рекурсии увеличивается. Из-за этого заранее красиво объявить все эти локальные переменные в итеративном алгоритме не получится. Можно, конечно, вручную организовать стек, хранящий состояния каждой «итерации», но это уже будет замаскированная рекурсия, пусть и в обычном цикле без вызова функций. Что нелаконично, малопонятно и не очень красиво. И, соответственно лаконично и красиво использовать рекурсию.

     

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

    Самым вопиющим примером уродского применения рекурсии является ее использование при вычислении факториала и чисел Фибоначчи.
    Я даже не говорю, что это страшно не эффективно. Я говорю, что вычисление факториала и чисел Фибоначчи — чисто итерационная задача, и рекурсивное ее решение — это извращение самого смысла, самой сути рекурсии в императивных языках [6].

    Как ни странно, эти задачи часто приводят в учебной литературе, причем в самом начале обучения рекурсии, в качестве первого примера. Быть может, рекурсия считается столь сложным для обучения приемом, именно из-за того, что ее объяснение ведется на совершенно противоестественных ее сути примерах…


    Отсутствие именованных параметров функции


    В двух словах:

    Параметры любой нетривиальной функции должны задаваться по осмысленному имени, а НЕ положению в списке аргументов.


    Никто не будет спорить с тем, что имена должны отражать суть перемененных. И что использование имен переменных вроде a0, a1, a2 — не самый понятный и красивый прием.

    Точнее, не иметь осмысленного имени могут переменные, не являющиеся уникальными, входящие в состав коллекции, обрабатываемые не каждая отдельно, а вместе, в цикле.

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

    Пример:

    Программа, выводящая имя продукта, его кодовое имя и список глюков:

    println ("Имя продукта: "+ objWinVista.name); //Windows Vista<br>println ("Кодовое имя: "+ objWinVista.codename); //Longhorn<br>println ("Число глюков: "+ objWinVista.bugsCount); //1 000 000 000 :-)<br>println ("Список глюков:");<br>for (long numBugNumber = 0; numBugNumber < objWinVista.bugsCount; numBugNumber++)<br>  {<br>  println (objWinVista.bugs [numBugNumber]);<br>  }

    В данном примере name, codename и bugsCount являются уникальными данными и обрабатываются отдельно, поэтому имеют осмысленные имена.

    Каждый же из глюков bugs [i] уникальным не является, поэтому имеет не осмысленное имя, а просто номер.

    Параметры функции являются такими же полноценными переменными. Однако при вызове функции мы задаем параметр не по его осмысленному имени, а по положению в списке параметров, т.е. по номеру. Это еще хуже, чем переменные a0, a1, a2.

    Пример:

    Вот примеры из официальной документации к Java 2D:

    GradientPaint gp = new GradientPaint (50.0f, 50.0f, Color.blue, 50.0f, 250.0f, Color.green);

    или

    RotationInterpolator rotator = new RotationInterpolator (<br>  new Alpha (-1, Alpha.DECREASING_ENABLE, 0, 0, 8000, 0, 0, 0, 0, 0),<br>  xformGroup, axis, 0.0f, (float)Math.PI*2.0f);

    Что означают эти параметры: -1, Alpha.DECREASING_ENABLE, 0, 0, 8000, 0, 0, 0, 0, 0?

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

    Первый случай, это когда параметров немного (не больше 3), и их предназначение очевидно.

    Пример:

    Math.pow (2, 5) вряд ли можно интерпретировать иначе как 25. Ну, разве что, как 52 :-)

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

    Пример:

    Функция, суммирующая числа: Math.summ (3, 7, 18, -2, 11, 2.3)

    Во всех остальных случаях параметры надо задавать по осмысленным именам.
    К сожалению, лишь немногие языки поддерживают именованные параметры функций (я могу вспомнить только динамические Perl, Python и Ruby, может быть есть еще).

    Пример (perl):

    Функция перевода текста.

    $strResult = translate
      text       => "Hello, world!",
      from       => $lngEnglish,
      to         => $lngRussian,
      vocabulary => $vcblrGeneral,
      quality    => 10;
    

    Что же делать в остальных языках?

    В процедурных языках (вроде C или Pascal) проблема вызова функций с большим количеством малопонятных параметров стоит особенно остро.

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

    Пример:

    //rectangle1 – структура для хранения параметров функции
    Rectangle rectangle1;
    
    //Задаем параметры функции
      rectangle1.x               = 80;     //x
      rectangle1.y               = 25;     //y
      rectangle1.width           = 50;     //ширина
      rectangle1.height          = 75;     //высота
      rectangle1.rotation        = 30;     //угол наклона
      rectangle1.borderWeight    = 2;      //толщина контура
      rectangle1.borderColor     = "red";  //цвет контура
      rectangle1.backgroundColor = "blue"; //цвет заливки
      rectangle1.alpha           = 20;     //процент прозрачности
    
    //Вызываем функцию, передавая ей структуру с параметрами
    drawRectangle (rectangle1);
    

    Гораздо больше кода, но
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 411

      +5
      Заголовок помягче бы, видимо вас это ооочень сильно достало =)
      • НЛО прилетело и опубликовало эту надпись здесь
          +3
          Пункт 7 — java-стиль. Считается что в случае необходимости изменения механизма присваивания/получения этот способ является более гибким.
          В .NET он уже не требуется — там за обычным присваиванием/получением можно скрыть произвольную реализацию (методы get/set).
            +6
            Проблема в том что в случае дотнета теряется осознание разработчика использующего объект, что он работает с методами а не полями. Из-за этого вылезают ошибки. Как минимум.)
            • НЛО прилетело и опубликовало эту надпись здесь
                +3
                Заменять стилем кодирования ограничения языка — очень, очень плохой стиль. =)

                Есть правила синтаксиса а есть авторские запятые.

                А вообще… Вспомним для чего была создана концепция JavaBeans изначально? Для работы с компонентами UI. Теперь представим себе изменяемое свойство… Дальше понятно.

                И к слову. Если мы говори о свойствах полях и методах. Когда разработчик пишет obj.a из этого кода он не может понять изменяемое это поле или нет. Рефакторинг и вообще изучение кода усложняется. Как это реализовано в дотенете? Возможно ли понять в месте использования свойства изменяемое ли это свойство?
                • НЛО прилетело и опубликовало эту надпись здесь
                    +1
                    Хм. Стиль может быть понятен или не понятен другим разработчикам. Поддерживающим проект после команды. Команды меняются код остается… Это известные принципы. Время вхождения все дела…

                    На тему студии. Мы же говорим не только о дотнете разве нет? ;) Если только то статья должна называться по другому, и речь должна идти о стиле работы с дотнетом исключительно. Я очень рад что в дотнете есть свойства объектов и программистам удобно их использовать вместо сеттеров и геттеров. =)
                    +1
                    Решарпер вам может подсказать, когда вы хотите readonly свойству присвоить значение. Либо комментарии.
                      +1
                      Да и не только Решарпер — и компилятор ругается гадскими словами :)
                      И кроме того, программист сам должен бы знать, какое свойство он использует (привет тем, кто не документирует API).
                      Ну и не забываем про средства — все таки не в блокноте пишут на C# — в Студии отлично работает IntelliSense, показывающий типы свойств в виде иконок.
                  • НЛО прилетело и опубликовало эту надпись здесь
                      0
                      Почему это свойства — нарушения инкапсуляции?
                        –2
                        Публичные свойства это нарушение инкапсуляции. Потому что дают внутренне пользователю представление черного ящика, вместо интерфейса взаимодействия с ним.
                          +1
                          Хм… Свойства и поля — это совсем разные вещи. Хотя да, есть ЯП, где поля принято называть свойствами, что не совсем верно, полагаю
                            +2
                            Согласен, если свойства считать синтаксическим сахаром призванным заменить теже гетеры, сетеры, к в as3 к примеру. То тогда о нарушении инкапсуляции говорить не приходится. Но я как явист, сторонник подхода что первичен интерфейс. И гетеры естественно вписываются в этот подход в отличии от свойств.
                              +1
                              Ну, дык, чего тогда спорить? Собственно, автор статьи про это и говорит, а не призывает вытаскивать наружу данные.
                                +2
                                Ж) как обычно спор свелся к синхорнизации терминов и понятию что все правы. что радует.

                                Противоречие у автора на мой взгляд в том что он видит абстрактный язык без учета его идеологии и истории развития. И где-то свойства прекрасно вписываются, как в php as3 python, то в java или c++ они будут смотреться чужеродно и уродливо.
                            • НЛО прилетело и опубликовало эту надпись здесь
                            0
                            Свойство может быть частью интефейса, а интерфейс инкапсулирует объект вчистую.

                            Открытие внутреннего поля — нарушение инкапсуляции.
                  +1
                  Потому что это классика ООП, а то что советует автор нарушает классические правила ООП.
                    0
                    Почему свойства не являются классикой ООП?

                    Они используются почти во всех ООП языках: C#, Delphi, Pethon, Ruby…

                    Поддержки свойств нет только в C++, Java и, к сожалению, в Javascript.

                    Только в этих языках приходится либо:

                    использовать открытые поля — что совсем плохо;

                    либо явно вызывать методы доступа к данным: obj.getProperty () и obj.setProperty ().
                      –2
                      Свойства есть не во всех. public методы есть во всех… Вывод?=)
                        +2
                        > Вывод?=)
                        Использовать свойства там, где они есть, и методы, там где нет свойств. То, что не во всех языках есть свойства, не означает, что их не надо использовать.
                          +1
                          Тогда почему статья столь нетерпима? =) Удобное использование свойств есть еще в меньшем подмножестве языков чем чем то в котором свойства есть вообще. Я уже очертил в комментариях проблему, когда разработчику придется либо лезть в чужой код либо компилить в ожидании ошибки либо лезть в документацию исключительно что бы узнать изменяем свойство или нет.
                        +2
                        Вроде бы в JAVA7 обещали.
                          0
                          Не будет
                            0
                            очень жаль :(
                            а где можно прочитать про отказ и его причины?
                        0
                        >к сожалению, в Javascript.
                        А можно поподробнее? У меня есть объект Х я не могу получить доступ к свойству Y этого объекта?!
                          +2
                          Вы можете получить доступ к полю, а не к свойству.
                            –3
                            Видимо я что-то пропустил, либо не так мы друг друга не так понимаем. Что есть поле и главное как выглядит его создание для объекта?
                              0
                              Поле — это то, что вы называете свойством.
                              Настоящие же свойства позволяют писать код, который будет вызываться при чтении или записи поля. Этот код, например, может проверять, что устанавливаемое значение входит в нужный диапазон, и выбрасывать исключение, если это не так.
                                0
                                Странная у вас какая терминология… В JS есть объект, есть его методы и свойства. От того, что мы через метод объекта производим/не_производим изменения его свойства вовсе не значит, что у объекта есть какие то там «поля».

                                Если я где-то ошибся, то прошу ссылку на спецификацию/любой_другой_нормативный_ документ в котором бы говорилось, что у объекта есть поле и разьясняется значение данного термина.
                                  +2
                                  Я согласен, что в терминологии Javascript поле называется свойством, но в других языках обычно используется другая терминология. Например, поле versus свойство в Википедии.
                                    –1
                                    Я задал вопрос в контексте Javascript. Задал я его Alik_Kirillovich-у. Задал потому что достаточно хорошо знаю Javascript.

                                    Если вы на это не обратили внимание, зачем было лезть и минусовать? Я не лезу в рассуждения о полях/свойствах того же Delphi потому с ним не работаю и тем более не владею используемой в нем терминологией.

                                    ЗЫ. Поля/свойства/методы, все это на самом деле не имеет значение. А нормативное значение имеет англоязычная терминология. Так что стоило бы приводить именно терминологии данного языка.
                                      +1
                                      > Задал потому что достаточно хорошо знаю Javascript.
                                      Похоже, что это единственный язык программирования, который вы знаете :)

                                      >Если вы на это не обратили внимание, зачем было лезть и минусовать?
                                      Почему вы решили, что это именно я вас минусовал? Минусы я ставлю только если вижу неадекватные сообщения. А если просто не согласен с кем-то, то обычно пытаюсь объяснить, как в этом случае. Видимо зря.

                                      >Я задал вопрос в контексте Javascript.
                                      А статья не была привязана к какому-либо языку программирования. Допустим, в одной деревне в хозяйстве никто не держит коров, но держат коз. И коз в этой деревне почему-то называют коровами. Разве это даёт право говорить, что в этой деревне есть коровы?

                                      >ЗЫ. Поля/свойства/методы, все это на самом деле не имеет значение. А нормативное значение имеет англоязычная терминология. Так что стоило бы приводить именно терминологии данного языка.

                                      Ну хорошо, вот в этой статье говорится, что в Javascript нет property getter-ов и setter-ов.
                                        0
                                        >Похоже, что это единственный язык программирования,
                                        > который вы знаете :)
                                        Да вам бы в кадровом ахэнствэ работать, с таким наметанным глазом. Пару постов увидел и уже бросаемся выводами. На будущее. Для начала стоило хотя бы посмотреть профайл ;)

                                        >А если просто не согласен с кем-то, то обычно пытаюсь объяснить,
                                        Я собственно это и пытался сделать.

                                        >А статья не была привязана к какому-либо языку программирования.
                                        А причем тут статья? Обращаю особое внимание, что над каждым постом есть такая кнопочка которая называется «Ответ на». Я некомментировал статью. Мой камент относился к сообщению habrahabr.ru/blogs/development/59570/#comment_1617768 и то не со всему, а к некоторой его части. К сожалению Alik_Kirillovich от диалога как то устранился.

                                        Впреть же надеюсь, что вы будите более внимательны и не столь поспешны на расправу выводы и не придется потом писать это:

                                        > Я знаю, для чего нужны свойства. Дело не в этом.
                                        Понятно, сорри за мой пост.


                                        В данной ситуации меня радует только одно. Ваша явная адекватность и готовность к конструктивному диалогу.
                                          0
                                          Извиняюсь за жирный, но хабр так отформатировал.
                                  +1
                                  Вопрос от нуба (яп Delphi): выходит, свойство — это property (и пара его методов get и set), а поле — любая var переменная (public, protected или private)?
                                    +3
                                    Да
                              +2
                              Прошу прощения за задержку с ответом.

                              В классической терминологии ООП «свойством» называют интерфейс для доступа к данным объекта.

                              При использовании которого вызываются методы, но вызываются они «прозрачно» для разработчика.

                              Например, при вызове array.length--, не только уменьшится значение поля length, но и «прозрачно» вызовется метод, который удалит последний элемент массива.

                              То, что есть в Javascript, в классической терминологии ООП принято называть «публичным полем».

                              Однако, в терминологии Javascript эти публичные почему-то поля называются «свойствами». Вот цитата из официальной спецификации Ecma-262:
                              An Object is an unordered collection of properties.
                              Так что, здесь Вы правы.
                                0
                                Ну слабо богу разобрались. А то исходя из топика выше складывается впечатление, что в Javascript свойств нет. Имхо не стоит вносить в этот еще большую неразбериху, у 4 человек прочевших эту ветку мозги в этом аспекте и так клинит.
                                  0
                                  Лол! Чем то, что написал Alik_Kirillovich отличается от того, что писал я чуть выше?
                                    0
                                    А ты не понимаешь? Вопрос был в контексте Javascript, ты же ударился в общие рассуждения. Он же четко обрисовал оба контекста (Javascript контекст и не-Javascript).
                              +2
                              в javascript есть watch.
                                +17
                                Pethon это падонковский Питон?)
                                  +2
                                  Для C++ особо рьяные авторы рекомендуют прокси объекты, а менее рьяные — несколько упрощённый подход:

                                  private: int age;
                                  public: void Age(int newage) { age = newage; }
                                  int Age() { return age; }

                                  Записи вида o.Age(10); и a = o.Age() всё же выглядят не так уродско, как гет/сет.
                                    +2
                                    Методы как и функции принято начинать с глагола, т.к. метод и функция ДЕЛАЮТ (ПОЛУЧАЮТ-get/УСТАНАВЛИВАЮТ — set). Потому такой подход ничем не красив.
                                      0
                                      Я бы сказал, что в данном случае как раз глагольный подход некрасив. Давайте сведём к очевидному.
                                      x = 15;
                                      лучше, чем
                                      set(x, 15);

                                      Если бы это было не так, мы бы все и писали — set(x, 15)

                                      Если я хочу присвоить некое значение некому полю, почему синтаксис должен отличаться от «x = 15»? Нам говорят, что это нарушает инкапсуляцию и предлагают гет/сет. Но ведь авторы и Делфи, и C# явно тоже не любят гет-сет — и неслучайно ведь не любят, и неслучайно придумали properties? Или будете говорить, что это всё ненужные выдумки?

                                      Так вот, если уж в используемом языке нет properties я думаю, что как раз запись o.Age(10) стоит стилистически посередине между o.Age = 10 и o.setAge(10).
                                        0
                                        «Так вот, если уж в используемом языке нет properties я думаю, что как раз запись o.Age(10) стоит стилистически посередине между o.Age = 10 и o.setAge(10).»
                                        Не согласен, если уж в языке нет properties, то лучше get\set. Если o.Age() похоже на геттер, то o.Age(10) — в общем случае может делать все что угодно, в то время как o.setAge(10) явно говорит об изменении внутреннего поля.
                                          0
                                          На самом деле идеального подхода нет.

                                          Вот ещё один (я его тоже всерьёз рассматриваю):

                                          int Age() { return age; }
                                          int& rAge() { return age; }

                                          Тогда можно писать
                                          o.rAge() = 10;

                                          При этом у нас всё равно rAge() — полноценная функция, в её теле можно следать какие-либо внутренние действия.
                                            0
                                            Честно говоря не очень хорошо помню С++.
                                            Однако если это и работает, то выглядит не совсем обычно «o.rAge() = 10;».
                                            Но и это не главное, насколько я понимаю чтобы вы в rAge не сделали, то все равно после вызова age = 10, а это уже мало чем отличается от раскрытия внутренних полей объекта(присвоить можно все что хочешь).

                                            PS: Поправьте, если я что-то не так понял, последний раз на с++ писал в университете.
                                              +1
                                              Да, присвоить можно всё, что хочешь. Там, где я читал об этой идее, утверждалось, что не так уж и часто нужно ограничивать присваивание — чаще нужно как-то отреагировать на сам факт присваивания, а возможность это сделать у нас есть.

                                              В С++ можно сэмулировать почти что полноценные properties, но это довольно хитрая методика, и применять её направо и налево я бы не стал.

                                              Допустим, нам нужно написать геттер-сеттер на объект класса T. Создаётся новый класс TProxy, для которого есть перегруженный оператор = и операция приведения к типу Т. Далее это всё описывается таким образом, чтобы при попытке присваивания объекту вызывался оператор = (вот он наш сеттер), а при попытке считывания — приведение к типу (вот геттер).

                                              Т.е. в принципе это всё работает, но на каждом шагу эту методику использовать слишком сложно.
                                                0
                                                В MVC++ есть declspec(propget) и declspec(propset) или вроде того.
                                                  0
                                                  Это не в c++, это в непонятной поделке, под названием managed c++.
                                                    0
                                                    Прежде чем говорить — у нормальных людей принято проверять.

                                                    msdn.microsoft.com/en-us/library/yhfk0thd(VS.80).aspx
                                                      0
                                                      Как насчет ссылочки на стандарт?

                                                      Я с таким же успехом могу Вам написать про свойства в Qt…
                                                        0
                                                        Какой нафиг стандарт?

                                                        Я же русским языком написал — не в С++, а в MVC++. Microsoft Visual C++.

                                                        И в MSDN кстати тоже это же написано — это все Microsoft specific.
                                                          0
                                                          Вот именно! ;)
                                                            0
                                                            Я все равно не понимаю.

                                                            То, что это — Microsoft specific не мешает применять данный метод в реальных приложениях, которые не требуют кроссплатформенности.
                                                              0
                                                              Да, простите. Я вечно забываю, что кроссплатформенность важна не для всех продуктов :-[

                                                              В любом случае, я считаю, что навешивать на и без того сложный и перегруженный С++ конструкции типа
                                                              __declspec(property(get = getprop, put = putprop)) int the_prop;
                                                              ради сомнительной пользы да и еще с потерей кроссплатформенности — это перебор.

                                                              Мне нравятся свойства в C#, потому как это изначально встроенная фича языка, но в С++ можно прекрасно обходиться и без этого.
                                                                0
                                                                Нормальная, к слову сказать, конструкция, и ее очень удобно использовать, когда пишешь ActiveX. Все аксесоры и мутаторы уже определены, вешается declspec и вуаля — доступ к плюсовым свойствам из плюсов же облегчается.
                                        • НЛО прилетело и опубликовало эту надпись здесь
                                            +1
                                            Для справок: у Delphi и C# автор по большому счету один — Андерс Хейлсберг.
                                            Делаем выводы.
                                              +1
                                              Первый, который напрашивается — C#, как и Delphi, надолго поселится в умах и сердцах большого количества разработчиков. Так же, как php и vb
                                                0
                                                (простите, случайно нажал минус, не хотел!)

                                                Вывод один — человек умный :)
                                                По сути считывание-установка — это просто перегруженная операция =.
                                                В С++ есть определённый дефект: нельзя простым способом с помощью перегруженной операции указать один алгоритм для считывания, а другой — для присваивания, считывают объект или присваивают. Можно проблему решить с помощью прокси-объектов, но это сложно. А вот пропертис — разумное решение.
                                                +1
                                                >Но ведь авторы и Делфи, и C# явно тоже не любят гет-сет
                                                множественное число тут не уместно, это один и тот же человек
                                        0
                                        Я предлагаю вместо obj.getProperty() и obj.setProperty(value); использовать свойства.

                                        Не открытые поля, а именно свойства.

                                        Т.к. при обращении к данным будет вызываться метод, но происходить это будет прозрачно для программиста:

                                        class Person
                                         {
                                         private long _money;
                                         public long money
                                          {
                                          get
                                           {
                                           /*
                                           !!!!!!!!!!!!!
                                           Метод для получения данных
                                           */
                                           return (_money);
                                           }
                                          set
                                           {
                                           /*
                                           !!!!!!!!!!!!!
                                           Метод для записи данных
                                           */
                                           _money = value;
                                           }
                                          }
                                         }

                                        /*
                                        Вызов методов происходит прозрачно
                                        */
                                        Person psnBillGates = new Person ();
                                        lngOldRiches = psnBillGates.money; //Чтение
                                        psnBillGates.money = lngNewRiches; //Запись
                                        psnBillGates.money += 1000000000; //Инкрементация


                                          0
                                          Понимает ли программист исследующий точки использование свойств изменяемые ли они или нет? =)
                                            0
                                            Честно говоря, ни разу не возникало такой проблемы на практике.
                                            В самом крайнем случае, компилятор и API documentation говорят об этом.
                                              +2
                                              Хотелось бы услышать грамотный контр-аргумент. И то, какие альтернативные методы позволяют делать тоже самое, но еще проще и «со всеми нужными фичами». Ибо, сдается мне, эти свойства (а также, например, в C#, еще и неявное определение стоящих за ними приватных переменных) не просто так придумали.
                                                +1
                                                Безусловно не просто так, и я собственно не спорю с тем что использование свойств в дотнете в окружении VS удобно. Но использование свойств в других случаях и на других IDE, не обязательно будет удобно. Это простейший контр аргумент.

                                                Компилировать большой класс исключительно что бы узнать есть ли ошибка в коде или нет это слегка так php-стиль, вам не кажется? А API documentation есть не всегда. В случае если другая часть проектной команды разрабатывала класс? =)
                                            +2
                                            И чем в вашем примере это лучше, чем использование открытых полей?

                                            Я встречал и такой маразм, как классы, к которым к каждому свойству (а их там было около 50) делался геттер/сеттер. Это говорит о том, что люди не понимают, зачем нужны свойства.

                                            Апофеозом свойств является такая реализация метода get, которая возвращает новый объект при каждом обращении, так что в коде

                                            someObj.SomeProp == someObj.SomeProp

                                            вычисляется в false!
                                              0
                                              геттер/сеттер сгенерить — дело пары минут. Зато если понадобится что-то править — заготовки уже есть. :) В С++ тоже можно сделать данные-члены открытыми — однако так никто (надеюсь) не делает.
                                                –4
                                                В с# автоматические get-тер / set-тер. И если надо что-то менять, их все равно придется сильно-сильно править. Так что я считаю, что ценность свойств весьма завышена.
                                                  +2
                                                  Свойства, даже тривиальные, нужны, чтобы можно была возможность в будущем их изменять, не меняя интерфейс класса. Если вы будете использовать поля вместо свойств, а потом вам потребуется добавить дополнительную логику при установке или чтении какого-то поля, то вам придётся переделывать его в свойство. Даже если имя нового свойства будет совпадать со старым именем поля, то всё равно придётся перекомпилировать весь код, который его использует.
                                                    0
                                                    Я знаю, для чего нужны свойства. Дело не в этом.

                                                    Прочтите, пожалуйста, внимательно, мой исходный вопрос.
                                                    «чем в вашем примере это лучше, чем использование открытых полей?»
                                                      0
                                                      > Я знаю, для чего нужны свойства. Дело не в этом.
                                                      Понятно, сорри за мой пост.

                                                      Согласен, что если класс Person и его поле money используются только в пределах одной сборки, то нет особого смысла делать его свойством. Но если он используется во внешнем интерфейсе сборки, т.е. может вызываться из других сборок, то Money IMHO должно быть свойством.
                                                +2
                                                Если поле не является immutable, то его копированние в геттере является стандартной практикой.

                                                А оператор == можно переопределить.
                                                  0
                                                  >>И чем в вашем примере это лучше, чем использование открытых полей?
                                                    +1
                                                    >>И чем в вашем примере это лучше, чем использование открытых полей?
                                                    В данном примере ничем, а в более общем случае setter (как и getter) может попутно много сего ещё делать, например, записывать в базу данных или перещитывать зависимые от этого свойства другие поля и т.п.
                                                      +1
                                                      Прежде всего, прочтите мой ответ выше (http://habrahabr.ru/blogs/development/59570/#comment_1618086). Я вас уверяю — я знаю, зачем они нужны.

                                                      Во-первых, пример был неудачен, и я на него указал, только и всего.

                                                      Во-вторых, ваш пример с БД также неудачен — я первым оторву руки тому, кто в сеттере или геттере напишет код доступа к данным в БД. Свойства имитируют поля, поэтому им следует копировать и семантику полей, а именно:

                                                      — они не должны обладать серьезными side-эффектами, типа запись чего-то в БД или отправка email на Марс;
                                                      — должно выполняться равенство (obj.SomeProperty == obj.SomeProperty), в противном случае очень легко нарваться на долгие ночные бдения под отладчиком.

                                                      Говорю вам как архитектор/техлидер проекта (C# + Spring.NET + NHibernate)
                                                        0
                                                        Вы оба правы:

                                                        В некоторых случаях лучше использовать свойства.

                                                        В некоторых — явные методы (например, как правильно заметил sse, при наличие сильных побочных эффектов при доступе к данным).

                                                        Плохо только, когда язык вообще не предоставляет возможности использовать свойства, даже там, где они оправданы. Вот это уже очень некрасиво и об этом, как раз, я и пишу в статье.
                                                          0
                                                          А, так вы про этот конкретный пример. На данном этапе — ничем. А потом вдруг понадобится добавить какое-нибудь действие, и придётся править все случаи обращения к этому полю-свойству.
                                                          Вот и получается, если свойств нет, то нужно или писать двадцать пар геттеров-сеттеров, или оставить их открытыми полями — я делаю геттеры/сеттеры. Если же язык поддерживает свойства, то сперва можно сделать открытое поле, а если вдруг понадобится добавить некую функциональность, то можно переделать его в свойство.

                                                          Пример с БД, конечно-же, неудачен — мне лень было придумывать удачный пример ночью, а все исходники на работе :)
                                                            0
                                                            Можно немного переделать ваш пример с БД — допустим, в сеттере свойства у объекта выставляется признак IsDirty, который говорит о том, что объект был изменен и теперь отличается от того, который счас лежит в БД. Не суть. Ответ я получил :)
                                                          • НЛО прилетело и опубликовало эту надпись здесь
                                                              0
                                                              Я бы сказал — недопустимо.
                                                              Для такой семантики лучше сделать AddDays(), AddMoths() и т.д.
                                                              • НЛО прилетело и опубликовало эту надпись здесь
                                                                  +1
                                                                  Ну отличный пример — свойство Visible.
                                                                  Когда оно меняется, до хрена всего возможно надо сделать :)
                                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                                      0
                                                                      Да, так.

                                                                      Day — хорошее свойство, но только для чтения.
                                                                      Как длина строки (Length).
                                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                                          0
                                                                          Опять-таки, чтобы подчеркнуть простоту и дешевизну «узнать свойство» по сравнению с «получить результат метода».
                                                                          Т.е. String.getReversedString() и String.Length — имеют разницу :)

                                                                          Опять-таки имеет ли смысл для caller вводить временную переменную:
                                                                          string reversed = string1.getReversedString()
                                                                          foo1(reversed1);
                                                                          foo2(reversed2);
                                                                          А с «дешевым свойством» так делать не надо:
                                                                          bar1(string1.Length);
                                                                          bar2(string1.Length);
                                                                          • НЛО прилетело и опубликовало эту надпись здесь
                                                                            0
                                                                            Потому что операция AddDays логически изменяет не только это свойство, но и весь объект в целом. Клиент объекта со свойством ожидает, что методы могут менять состояние объекта как угодно, а операции над полем — только это поле. Не стоит его разочаровывать :)
                                                                            • НЛО прилетело и опубликовало эту надпись здесь
                                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                                      0
                                                                      Если вам нужно значительно изменять состояние объекта, лучше это сделать методом. Если же нужно изменить только это свойство, и никаких side-эффектов то лучше — свойством.
                                                        +28
                                                        Вы только представьте: У нас есть функция в 300 строк кода [2]. Где-нибудь на 200-й строке нам надо поменять две переменные местами. Для этого мы лезем на 200 сток выше в начало функции, объявляем переменную temp, которая не имеет никакого отношения ко всей функции, а используется только один раз в одном месте, потом опять возвращаемся к 200-й строке и меняем переменные местами… По-моему, это просто кошмар.


                                                        Т.е. 200 строк пролистать в поисках переменной — это плохо, а пролистать всю статью в поисках второй сноски и найти её вообще на другом сайте — это нормально? ;)
                                                          +5
                                                          К сожалению, на Хабре есть какое-то непонятное ограничение на объем статьи: поэтому сноски не уместились.

                                                          Я написал в техподдержку, после решения проблемы статья будет выложена в полном виде.

                                                          Прошу прощения за доставленные неудобства (возникшие по независящей от меня причине).
                                                          • НЛО прилетело и опубликовало эту надпись здесь
                                                              +7
                                                              ага, особенно если учесть, что размер всей этой страницы (включая камменты) 46,16 КБ

                                                              К.О. намекает, что существуют еще MEDIUMTEXT(MEDIUMBLOB) и LONGTEXT(LONGBLOB)
                                                                +1
                                                                В действительности размер страницы на порядок больше. GZip.
                                                                  +2
                                                                  да, не могу не согласится. мое упущение. но все равно, ожидаем перехода с TEXT на LONGTEXT.

                                                                  P.S. (на правах рекламы) Антикризисное предложение: включите gzip и экономьте деньги на трафике!
                                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                                  –1
                                                                  пусть переходят на postgres
                                                                  +2
                                                                  Кста, а функция в 300 строк кода — это нормально? ;) Обычно в учебниках пишут, что если функция вылезает за одну-две страницы — уже пора задуматься.
                                                                +13
                                                                А писать функцию в 300 строк — это хорошо?
                                                                  +2
                                                                  Спросите у автора этой функции :)
                                                                    +4
                                                                    за 300 строк фунции нада по рукам бить. или очень убедительно обосновать такое решение.
                                                                      +1
                                                                      >А писать функцию в 300 строк — это хорошо?

                                                                      Да, Вы правы.

                                                                      В сноске, которая, к сожалению, не уместилась из-за ограничений на объем статьи, я написал:
                                                                      Вообще, надо стараться избегать длинных функций и методов. Одно из правил рефакторинга: «если текст метода не умещается на экране без прокрутки — это уже повод разбить его на несколько более коротких методов».

                                                                      Однако, это далеко не всегда удается, например при генерации отчетов или при реализации сложных вычисления.
                                                                        0
                                                                        С другой стороны в примерах выделенных в сноске, вся функция будет единым блоком кода.
                                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                                          0
                                                                          Особенность языка, к сожалению. Функция в 2000 строк выполнится быстрее чем 10 в 200
                                                                            –1
                                                                            Это особенность плохого оптимизатора. Который написан на C. А на C (читай выше) особенно не развернёшься.
                                                                      +3
                                                                      Насчёт пункта 6. В PHP, например, при использовании циклов нужно хранить размер массива в переменной, а не использовать count() в условии, т.к. он вычисляется на каждой итерации.

                                                                      Насчёт пункта 7. Опять на PHP. В классе A объявляется protected свойство x. Класс B наследуется от класса A. Задача получить значение свойства x решается через объявление в классе B public метода getX(). Думаю не нужно объяснять, для чего это применяется.
                                                                        0
                                                                        > Думаю не нужно объяснять, для чего это применяется.
                                                                        объясните мне, пожалуйста.
                                                                          0
                                                                          Читайте про инкапсуляцию и доступ к данным.
                                                                            0
                                                                            А расскажите задачу в которой требуется такая структура? Правда интересно зачем создавать геттер только в B если он отсутствует в А.
                                                                              0
                                                                              Это, вообще говоря, сильно упрощенный пример, чтобы не растекаться мыслью по древу, так сказать.

                                                                              Но, допустим, есть базовый класс — сборщик неких данных. Нужно дать интерфейс для получения этих данных без возможности их изменения.

                                                                              <?php
                                                                              interface AB
                                                                              {
                                                                              public function getX();
                                                                              }
                                                                              class A
                                                                              {
                                                                              private $x = 1;
                                                                              protected function getX()
                                                                              {
                                                                              return( $this->x );
                                                                              }
                                                                              }
                                                                              class B extends A implements AB
                                                                              {
                                                                              public function getX()
                                                                              {
                                                                              return( parent::getX() );
                                                                              }
                                                                              }
                                                                              $b = new B();
                                                                              echo( $b->getX() );
                                                                              ?>
                                                                                0
                                                                                Упс, простите за форматирование. Здесь как-то можно красиво вставлять код?
                                                                                  0
                                                                                  Ну реализация и так понятна. Но по каким причинам запрещен прямой вызов getX в базовом классе?
                                                                                    0
                                                                                    А он и не должен просто возвращать данные. Возможно, я хочу как-то фильтровать их для клиента. Возможно, по какому-то параметру. Так что я ограничиваю доступ к исходнику и выдаю только то, что считаю нужным.
                                                                                      +1
                                                                                      О теперь понятно. Например набор классов-потомков с различными фильтрами. Спасибо. Такое действительно может встретится и достаточно часто.
                                                                                      0
                                                                                      А в базовом классе метода getX() может не быть. Ну, не нужен он там.
                                                                                        0
                                                                                        Кстати, нет. Если я не объявлю этот метод, то не смогу получить доступ к private x напрямую. Если же я объявлю его как protected — смогу изменить из потомка.
                                                                                          0
                                                                                          Да, пожалуй ты прав.
                                                                                      0
                                                                                      Интерфейс получения данных без их изменения сильно удобнее реализовать с помощью паттерна Proxy.
                                                                                        0
                                                                                        Разница, вобщем-то, не сильно велика.
                                                                                    –3
                                                                                    это вы лучше почитайте про инкапсуляцию и доступ данных, приведенный вами пример часто упоминается под названием Паблик Морозов. подобная конструкция никогда не нужна в нормально спроектированных системах.
                                                                                      0
                                                                                      Читайте пример выше.
                                                                                        –4
                                                                                        это про него
                                                                                          0
                                                                                          Тогда читайте ещё раз и внимательнее, попробуйте найти отличия от примера по Вашей ссылке в моих комментариях. Больше я Вам, увы, ничем помочь не могу.
                                                                                  +4
                                                                                  Согласен на все 100% в ЯП, где нету свойств для доступа к переменным класса, использование напрямую переменных класса (что значит вывод их в область действия public является страшным злом и нарушением инкасуляции)
                                                                                +1
                                                                                пронумеруйте примеры пожалуйста
                                                                                  +3
                                                                                  т.е. не примеры, а приёмы
                                                                                  0
                                                                                  Пункт 3, кстати, очевидно не относится к объектно-ориентированным языкам.
                                                                                    –2
                                                                                    Пункт 3 [Отсутствие локальных функций], кстати, очевидно не относится к объектно-ориентированным языкам.
                                                                                    В основном да, но не совсем: например в C++ нет полноценных локальных функций.
                                                                                      +1
                                                                                      А разве это не решается классами и пространствами имён? Я уже давно не силён в C++, если что.
                                                                                        0
                                                                                        В C++ нельзя объявить функцию в теле другой функции, например, как в Javascript:

                                                                                        function main ()
                                                                                         {
                                                                                         //...
                                                                                         function sub1 ()
                                                                                          {
                                                                                          /*
                                                                                          Вот это локальная функция sub1, объявленная в теле функции main
                                                                                          */
                                                                                          }
                                                                                         function sub2 ()
                                                                                          {
                                                                                          }
                                                                                         }
                                                                                          0
                                                                                          Так вопрос не в том, можно ли принципиально их использовать или нет, а в том, чтобы создать правильную структуру модулей. Классы для этого и нужны.
                                                                                            0
                                                                                            потому что в C++ нет замыканий и функции внутри функций просто не нужны, вот и все. Хоть этим не стали язык перегружать)
                                                                                              +3
                                                                                              этим=функциями внутри функций, за замыкания я обеими руками за
                                                                                      –6
                                                                                      В избранное! Надо подробно разобраться с этой темой на досуге! спасиббо!
                                                                                        +27
                                                                                        По части пунктов выходит, что, если я программирую на Си, то я жуткий быдлокодер. :-)
                                                                                          +4
                                                                                          Вы не правы :)…
                                                                                          Просто Автор не программирует на Си ;)
                                                                                          +2
                                                                                          Насчет функций-аксессоров — момент спорный. Если аксессор только возвращает значение внутренней переменной (то есть нужен исключительно для инкапсуляции), то он может быть свойством. Если же он делает какую-то дополнительную работу — можно и нередко нужно определить его как метод.
                                                                                            0
                                                                                            Если же он делает какую-то дополнительную работу — можно и нередко нужно определить его как метод.

                                                                                            фокус в том, что метод это неудобно и на замену ему предлашаются get/set методы реализующие необходимый функционал но внешне выглядящие как свойства. это есть в c#, в AS2/3(!) и до сих пор нет в Java :(
                                                                                              +3
                                                                                              Что такое удобно в данном контексте? Человеку работающему со сторонним фреймворком зачастую полезно знать что он вызывает метод.
                                                                                                +6
                                                                                                Человеку работающему со сторонним фреймворком зачастую полезно знать что он вызывает метод.

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

                                                                                                и ваообще речь тут идет об удобном синтаксическом сахаре, не более того
                                                                                                  +3
                                                                                                  Это в идеале. Например, стандарт библиотеки STL — указывать для каждой функции возможную асимптотическую сложность в документации — так пока и не стал повсеместным.

                                                                                                  Представьте ситуацию, когда я пишу
                                                                                                  for i = 1 to 100000 do a[i].field = i; next i

                                                                                                  а потом оказывается, что при за присваиванием скрывается хитрая функция, которая всё тормозит. Обидно :(
                                                                                                  А главное — поди сообрази.
                                                                                                    0
                                                                                                    Самое обидное, если это всё ещё и выдаёт exception где-нибудь в глубине методов, которые вызываются в во время этого присваивания.
                                                                                                –2
                                                                                                Да, именно об этом я и говорю.

                                                                                                Т.е. в C#, Delphi, Pethon, Ruby мы можем писать:

                                                                                                Person psnBillGates = new Person ();
                                                                                                lngOldRiches = psnBillGates.money; //Чтение
                                                                                                psnBillGates.money = lngNewRiches; //Запись
                                                                                                psnBillGates.money += 1000000000; //Инкрементация


                                                                                                А методы доступа к данным вызываются сами, «прозрачно» для разработчика.
                                                                                                  +8
                                                                                                  А в случае, когда psnBillGates.money лежит в БД и каждое изменение должно выполняться транзакционно в реальном времени?

                                                                                                  Тут начинаются проблемы — разработчик, видя свойство, думает, что оно будет работать быстро и красиво. А оно, мало того, что тормозит, так еще и выкидывает DbConnectionNotAvalibleException.
                                                                                                    +6
                                                                                                    За написание Pethon, этот самый Питон вас загипнотизирует, удушит и съест.
                                                                                                    0
                                                                                                    var a = someInstance.GetField();
                                                                                                    var a = someInstance.Field;

                                                                                                    someInstance.SetField(value);
                                                                                                    someInstance.Field = value;

                                                                                                    Разницы в удобстве не вижу.
                                                                                                  0
                                                                                                  Пример №2 с авторизацией косячный — часть кода из другого места вставлена.
                                                                                                    +2
                                                                                                    Щит, всё там верно. Неверно прочитал код как раз из-за описываемой проблемы и неправильной иерархией (((-:
                                                                                                    0
                                                                                                    Всегда удивляет когда при написании «правил» люди сами их нарушают: "… страшное зло[1]..." и далее по тексту, в первую очередь отсутствие «разъяснения» сносок в блоках к которым они относятся (что следует из первого вашего правила по аналогии), во-вторых отсутствие их в принципе… копипаст?
                                                                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                        +4
                                                                                                        Да, но не того почему сноски не находились в блоке к которому они относятся, что следует из первого правила его топика. Из его ответа следует что «сноски» не вместились соответственно они были в конце о чем дополнительно свидетельствует их нумерация, об этом мой и коммент…
                                                                                                      +5
                                                                                                      Возврат результата функции через ее параметр
                                                                                                      Цитата:
                                                                                                      Слава богу, в других языках классы можно описывать прямо внутри функции, а, например в Javascript можно просто возвратить объект, нигде отдельно не описывая его структуру.

                                                                                                      Вот это настоящая красота!

                                                                                                      Готов поспорить, если последнее предложение про красоту не есть шутка.
                                                                                                      С помощью такого приема нарушить «хрупкую красоту кода» проще некуда!

                                                                                                      Понять, объект какого класса функция вернет без инспекции кода самой функции невозможно.
                                                                                                        +1
                                                                                                        если хотите понять структуру, вам в любом случае нужно проводить инспекцию — в функции или где-то в отдельном месте. Но в большинстве случаев это и не нужно — по описанию или названию функции понятно что она возвращает.
                                                                                                          +10
                                                                                                          Не, «хрупкая красота кода» так не нарушается. Все красиво. Легко нарушается «хрупкая работоспособность кода»
                                                                                                            +1
                                                                                                            Нет, тут я думаю автор имел ввиду динамический созданный объект в функции пример:

                                                                                                            function _Math (a, b) {

                                                                                                            return {
                                                                                                            sum = a + b,
                                                                                                            dub = a — b,
                                                                                                            div = a / b
                                                                                                            }

                                                                                                            }

                                                                                                            Этот способ на самом деле очень удобный и красивый. Результат от функции разберет любой дебагер и даже alert(_Math(5,1).toSoure()) в FF
                                                                                                              0
                                                                                                              Да, но автор забраковал С++ по причине того, что там такого нет. Не думаю, что в C++ такой механизм был бы уместен.
                                                                                                                0
                                                                                                                это красиво для яваскрипта. В нормальных же ОО языках программисты будут матюкаться, получая на выходе объект без интерфейса…
                                                                                                                  0
                                                                                                                  Для нормальных языков результат от функции лекго отслеживаем по дебагеру, а иногда легче заглянуть в дебагер чем в многотомные доки :). Но в целом я согласен — абстрактные объекты тоже зло, особенно в типизируемых азыках. Но на каждый случай есть исключение.
                                                                                                                    +1
                                                                                                                    Приплели сюда зачем-то «нормальные ОО языки», как будто javascript ущербный какой-то.
                                                                                                                    ОО тут вообще не при чем, важно то, допускает ли язык динамическую типизацию.
                                                                                                                    Извините, если вас не так понял.
                                                                                                                +5
                                                                                                                Всё верно кроме:

                                                                                                                «Доступ к свойствам объекта через obj.getProperty() и obj.setProperty(value);»
                                                                                                                — негодую! Это как раз правильный подход, особенно, если программа многопоточная и эта проперти неатомарная.

                                                                                                                Для себя я решил — доступ к полям напрямую допустим только если это
                                                                                                                1) Структура атомарных данных при невозможности испортить их из разных потоков одновременно.
                                                                                                                2) Обращение идёт из самого объекта и поле нестатическое.

                                                                                                                  –4
                                                                                                                  я бы сказал «Это правильный подход _только_ если программа многопоточная и это проперти неатомарное и используется между потоками...». Во всех остальных случаях это куча ненужных букоф.
                                                                                                                    +1
                                                                                                                    А как же органичения, ленивая загрузка и прочие прелести?
                                                                                                                      0
                                                                                                                      Если производятся какие-то дополнительные действия — это уже по логике вызов функции, а не просто узнавание параметра.
                                                                                                                      Хотя например на Питоне можно сделать так что функция будет вызываться при обращении к аттрибуту.
                                                                                                                        0
                                                                                                                        в Java например нельзя.
                                                                                                                        И как быть если неожиданно понадобилось добавить логику?
                                                                                                                          0
                                                                                                                          Капитан Очевидность сказал бы что нужно добавить функцию, и соответственно её вызов. ;)
                                                                                                                            +4
                                                                                                                            Ну к этому и вернулись.

                                                                                                                            Можно ещё переформулировать вопрос позаковырестее, кстати:
                                                                                                                            «И как быть если неожиданно понадобилось добавить логику классу, производному от класса С ЗАКРЫТЫМИ ИСХОДНИКАМИ в котором объявлено это поле?»
                                                                                                                              0
                                                                                                                              Если вы берете свойство, то должно браться только свойство, и ничего больше. Если происходит что-то другое, нужна функция с другим названием в любом случае. Я еще могу понять сеттер, в котором какие-то проверки, но тогда они должны быть сразу сделаны в исходном классе. Конечно можно представить себе задачи в котором без геттера и сеттера не обойтись, но я говорю об общем случае.
                                                                                                                                0
                                                                                                                                Если уж на то пошло, в общем случае не всегда понятно, нужна ли будет многопоточность в будущем. Хотя бы поэтому стоит сделать пару методов доступа.
                                                                                                                              +1
                                                                                                                              Во все сто тысяч мест откуда идет обращение к этому полю, ага.
                                                                                                                                0
                                                                                                                                А в чем проблема? Страх перед рефакторингом — прямой путь к некрасивому коду. Всё равно с самого начала всего не предусмотришь и что-то придется менять.
                                                                                                                                  0
                                                                                                                                  А ещё не следует бояться рефакторинга кода третьих лиц, который строится на основе вашего кода, так что-ли?
                                                                                                                                    0
                                                                                                                                    зачем, мы же не нарушаем обратную совместимость. А если «третьи лица» хотят новые фичи — пусть тоже меняют код.
                                                                                                                                      0
                                                                                                                                      Они не смогут изменить наш код.

                                                                                                                                      Например:

                                                                                                                                      class A (наш — исходник сокрыт) {
                                                                                                                                      public getX() { return m_x; }
                                                                                                                                      }

                                                                                                                                      class B (чужой) extends A {
                                                                                                                                      public getX() { return m_x + 1; }
                                                                                                                                      }

                                                                                                                                      и тогда можно будет сохранить вызов x = obj.getX() во всех случаях.

                                                                                                                                      Если же в классе А нет метода getX(), то придётся делать так:
                                                                                                                                      if (obj instanceof B) {
                                                                                                                                      x = obj.getX();
                                                                                                                                      } else {
                                                                                                                                      x = obj.m_x;
                                                                                                                                      }

                                                                                                                                      И так каждый раз при необходимости получить m_x
                                                                                                                                      Нарушается принцип ООП.
                                                                                                                                        –1
                                                                                                                                        я прекрасно понял о чем вы. Просто я считаю что перегружать getX само по себе некрасиво, если оно призвано просто брать поле класса. Если предполагается что может браться что-то еще — тогда конечно другое дело.
                                                                                                                                    +2
                                                                                                                                    представляю радость человека, который ревьюит результаты вашего рефакторинга
                                                                                                                          +1
                                                                                                                          Это куча букв только в случаях «objObject.setProperty (objObject.getProperty () + 1);»
                                                                                                                          Иначе objObject.getProperty() не на много длиннее objObject.property.

                                                                                                                          «objObject.property1++;»
                                                                                                                          Конечно так удобнее, но тут уж от языка зависит — в конечном счете, это все равно геттер и сеттер, если это не public поле.
                                                                                                                            0
                                                                                                                            Только вот эта операция выглядит, как атомарная, но на самом деле таковой не является.

                                                                                                                            Например, если property напрямую вынуждено обращаться к базе данных.

                                                                                                                            В случае, если блокировка на таблицу была наложена между операцией получения свойства и операцией установки нового свойства — вы получите плавающую ошибку, которую невероятно трудно обнаружить.
                                                                                                                              0
                                                                                                                              Имеется ввиду objObject.property1++; конечно.
                                                                                                                                0
                                                                                                                                Это может быть поле быза данных, ширина окна или просто любое целое число.
                                                                                                                                Соглашение о геттерах и сеттерах отлично работает и достаточно удобно.
                                                                                                                                Однако тут все зависит от объекта, если сразу видно, что над полем будут производиться операции ++, --, +=х, то не вижу ничего плохого при использовании свойств (за которыми реально стоят get и set), если язык это позволяет и не нарушает логику работы. Как впрочем и в отсутсвии свойств не вижу трагедии.
                                                                                                                                  +3
                                                                                                                                  Я не против свойств, я против подхода «никаких аксессоров — только свойства».
                                                                                                                                  0
                                                                                                                                  А кто вам гарантирует, что i++ атомарная? Это может быть не так даже в случае встроенного типа. Например, у нас 32битный процессор и мы делаем ++ 64 битное переменной.
                                                                                                                                    0
                                                                                                                                    Существует гарантия, что i++ для int, например, выполнится всегда, не выдавая ошибку переполнения стека, отсутствия памяти, отсутствия соединения с БД и прочее.
                                                                                                                                      0
                                                                                                                                      Есть.
                                                                                                                                      От нормальной проперти мы тоже такого же поведения ожидаем.
                                                                                                                              0
                                                                                                                              Я в этом месте окунул голову в ведро с валидолом:-)
                                                                                                                              –4
                                                                                                                              Несколько return-ов у функции.

                                                                                                                              function Fa(a, b)
                                                                                                                              {
                                                                                                                              if (a < b){
                                                                                                                              return 1;
                                                                                                                              }else
                                                                                                                              {
                                                                                                                              return 2;
                                                                                                                              }
                                                                                                                              }

                                                                                                                              Красивее так
                                                                                                                              function Fa(a, b)
                                                                                                                              {
                                                                                                                              if (a < b){
                                                                                                                              result = 1;
                                                                                                                              }else
                                                                                                                              {
                                                                                                                              result = 2;
                                                                                                                              }
                                                                                                                              return result;
                                                                                                                              }

                                                                                                                                +8
                                                                                                                                Спорно. Если уж говорить о приведенном примере, то лучше return a < b? 1: 2. А вообще иногда вовремя вставленная точка возврата — это возможность избежать пары if'ов размером почти со всю функцию.
                                                                                                                                  0
                                                                                                                                  Пример был дан для краткости. Конечно его можно написать и более простым способом.
                                                                                                                                  0
                                                                                                                                  return (a
                                                                                                                                    0
                                                                                                                                    Да, я не умею писать & lt; вместо знака меньше. И теперь не знаю, как можно прочитать ответ. Даже в коде страницы не нашел.

                                                                                                                                    НЛО, опубликуй вместо моей писанины надпись, пожалуйста.
                                                                                                                                      +4
                                                                                                                                      парсер, несомненно, лох.
                                                                                                                                      но предпросмотр рулит.
                                                                                                                                    +1