Здесь — полная авторская версия статьи, опубликованной в журнале «Хабрахабр» в сокращённом виде из-за ограничений на размер материала.
Тема красоты кода для меня очень важна, поэтому буду благодарен всем, кто поставит ссылочку, и за любой другой «пиар» статьи. Если интересно — можете подписаться на RSS.
Для меня программирование — это не только технология, но и, во многом — искусство. И, поэтому, большое значение имеет красота кода.
Последние несколько лет я собирал приёмы программирования, разрушающие в программном коде его утончённую красоту:
Обязательное хранение размера массива в отдельной переменной;
Доступ к свойствам объекта через obj.getProperty() и obj.setProperty(value);
Использование рекурсии для вычисления факториалов и Чисел Фибоначчи;
Переменные должны объявляться в начале логического блока, в котором они используются, а НЕ в начале функции или программы.
Все программные системы иерархичны. Программы делятся на пакеты, пакеты — на классы, классы разбиваются на отдельные функции.
Данные, относящиеся к тому или иному модулю программы, принято объявлять в начале этого модуля. Локальные переменные объявляются в начале функции; свойства, относящиеся ко всему классу, объявляются в начале определения класса и т.д.
Однако функции не являются последним уровнем в иерархии программы. Любая нетривиальная функция состоит из блоков, реализующих отдельны шаги выполнения алгоритма. Тех самых блоков, которые никак не обособляются в коде, разве что отделяются друг от друга парочкой пустых строк.
Однако эти блоки — полноценные элементы в иерархии программы. И они тоже имеют право на собственные «локальные» переменные! Которые объявляются в начале этого блока и используются только в его пределах.
Предположим, нам надо написать функцию сортировки массива методом слияния.
Алгоритм сортировки слиянием состоит из следующих шагов:
просто меняем местами эти два элемента в случае неправильного их расположения.
разбиваем массив на две половины: левый и правый подмассивы;
отдельно сортируем каждый подмассив (рекурсивно);
сливаем эти два отсортированных подмассива в один отсортированный результирующий массив.
Блоки кода, реализующие шаги этого алгоритма, являются неотъемлемыми частями функции, и не имеют отдельного самостоятельного смысла (может быть, за исключением, блока слияния, который, в принципе, можно преобразовать в отдельную функцию).
Однако, они являются полноправными элементами иерархии программы, и решают свои собственные маленькие подзадачи.
Поэтому:
В начале функции объявляются переменные, относящиеся ко всей функции, например:
intLeftIndex, intRightIndex, intLength.
А переменные, относящиеся к отдельным её блокам, объявляются в начале соответствующего блока, например:
elTemp объявляется в начале блока сортировки массива из 2-х элментов;
intCenterIndex — в начале блока разбиения;
arSortedSubarray и intLeftElIndex — в начале блока слияния.
/*
Эта функция реализует сортировку слиянием
*/
function mergeSort (arArryay, intLeftIndex, intRightIndex)
{
/*
Здесь объявляются переменные, относящиеся ко ВСЕЙ ФУНКЦИИ.
Переменные, относящиеся к отдельным её блокам,
объявляются в начале соответствующего блока.
*/
intLeftIndex = intLeftIndex || 0;
intRightIndex = intRightIndex || arArryay.length-1;
var intLength = intRightIndex - intLeftIndex + 1;
if (intLength == 2)
{
/*
БЛОК КОДА: сортировка массива из 2-х элементов
*/
if (arArryay [intRightIndex] < arArryay [intLeftIndex])
{
var elTemp = arArryay [intRightIndex];
arArryay [intRightIndex] = arArryay [intLeftIndex];
arArryay [intLeftIndex] = elTemp;
}
}
else if (intLength > 2)
{
/*
БЛОК КОДА: разбиение массива на два подмассива
*/
var intCenterIndex = intLeftIndex + Math.ceil (intLength / 2) - 1;
var intLeftSubarrayLength = intCenterIndex - intLeftIndex + 1;
var intRightSubarrayLength = intRightIndex - intCenterIndex;
/*
БЛОК КОДА: рекурсивная сортировка каждого подмассива
*/
mergeSort (arArryay, intLeftIndex, intCenterIndex);
mergeSort (arArryay, intCenterIndex+1, intRightIndex);
/*
БЛОК КОДА: слияние
*/
var arSortedSubarray = [];
var intLeftElIndex = intLeftIndex;
var intRihtElIndex = intCenterIndex+1;
while (intLeftElIndex <= intCenterIndex || intRihtElIndex <= intRightIndex)
{
if (intRihtElIndex <= intRightIndex && (arArryay [intRihtElIndex] <= arArryay [intLeftElIndex] || intLeftElIndex > intCenterIndex))
{
arSortedSubarray [arSortedSubarray.length] = arArryay [intRihtElIndex];
intRihtElIndex++;
}
if (intLeftElIndex <= intCenterIndex && (arArryay [intLeftElIndex] <= arArryay [intRihtElIndex] || intRihtElIndex > intRightIndex))
{
arSortedSubarray [arSortedSubarray.length] = arArryay [intLeftElIndex];
intLeftElIndex++;
}
}
for (var i = 0; i < intLength; i++)
{
arArryay [intLeftIndex + i] = arSortedSubarray [i];
}
}
}
И поэтому:
Объявление всех переменных в начале функции — страшное зло [1].
Это приводит к смешению переменных, относящихся ко всей функции, с переменными, относящимися только к её отдельному блоку.
Это разрывает блок на две части: объявления данных (в начале функции) и использования этих данных (в самом блоке).
Это усложняет комментирование блока: в одном месте мы комментируем переменные, но не знаем, как их использовать; в другом месте мы комментируем алгоритм, но не знаем, с какими данными он работает.
В случае объявления всех переменных в начале функции, переменные, относящиеся ко всей функции, перемешаются с переменными, имеющими смысл только внутри отдельных её блоков.
function mergeSort (arArryay, intLeftIndex, intRightIndex)
{
/*
Угадайте: какие из этих переменных относятся ко всей функции,
а какие — к отдельным её блокам?
*/
var intLength,
elTemp,
intCenterIndex,
intLeftSubarrayLength,
intRightSubarrayLength,
arSortedSubarray,
intLeftElIndex,
intRihtElIndex,
i;
intLeftIndex = intLeftIndex || 0;
intRightIndex = intRightIndex || arArryay.length-1;
intLength = intRightIndex - intLeftIndex + 1;
if (intLength == 2)
{
if (arArryay [intRightIndex] < arArryay [intLeftIndex])
{
elTemp = arArryay [intRightIndex];
arArryay [intRightIndex] = arArryay [intLeftIndex];
arArryay [intLeftIndex] = elTemp;
}
}
else if (intLength > 2)
{
intCenterIndex = intLeftIndex + Math.ceil (intLength / 2) - 1;
intLeftSubarrayLength = intCenterIndex - intLeftIndex + 1;
intRightSubarrayLength = intRightIndex - intCenterIndex;
mergeSort (arArryay, intLeftIndex, intCenterIndex);
mergeSort (arArryay, intCenterIndex+1, intRightIndex);
arSortedSubarray = [];
intLeftElIndex = intLeftIndex;
intRihtElIndex = intCenterIndex+1;
//...код сокращён...
}
}
Вы только представьте:
У нас есть функция в 300 строк кода
[2].
Где-нибудь на 200-й строке нам надо поменять две переменные местами.
Для этого мы лезем на 200 сток выше в начало функции,
объявляем переменную temp, которая не имеет никакого отношения ко всей функции,
а используется только один раз в одном месте,
потом опять возвращаемся к 200-й строке и меняем переменные местами…
По-моему, это просто кошмар.
Хуже всего, что существуют языки, которые считают себя умнее разработчика и заставляют объявлять все переменные в начале функции. Например, такой уважаемый язык как Pascal/Delphi. Чего я ему простить не могу…
Функция должна возвращать результат, зависящий от её параметров, а НЕ принимать результат в качестве аргумента.
Понятие функции (как в математике, так и в программировании) имеет чёткий смысл: вычисление результата, зависящего от аргументов.
В нормальном программном коде ясно видно, что является результатом, а что аргументами:
результат = функция (аргумент1, аргумент2).
Однако часто встречается приём, при котором возвращаемое значение передаётся в качестве аргумента функции:
функция (аргумент1, аргумент2, &результат).
Этот приём ужасен. При его использовании не видно, от чего функция зависит, а что возвращает.
Чаще всего, в применении этого приёма виноваты не сами разработчики, а языки программирования.
Существуют две основные причины, по которым языки заставляют нас прибегать к этому приёму.
Первая причина состоит в том, что в некоторых языках функции не могут создавать и возвращать сложные объекты.
Предположим, что мы хотим на C++ написать функцию, перемножающую две матрицы и возвращающую получившуюся матрицу в качестве результата. Матрицы мы решили представлять в виде двумерного массива.
Но мы не можем объявить в функции результирующий двумерный массив, а затем вернуть его:
int mtxResult [10][10] = mult (mtxA, mtxB);
Поэтому нам придётся сначала вне функции объявить результирующий массив, а затем вызвать функцию перемножения, передав результат в качестве аргумента:
mult (mtxA, mtxB, mtxResult);
В данном случае от использования этого приёма можно избавиться, храня результат в том виде, который функция может возвратить.
Можно хранить матрицу не в виде двумерного массива, а в виде структуры или объекта:
Matrix mtxResult = mult (mtxA, mtxB);
Код станет менее лаконичным (из-за объявления дополнительных структур), но, зато, гораздо более красивым.
Вторая причина состоит в том, что в большинстве языков программирования функция не может возвращать несколько значений.
Предположим, что мы хотим на C++ написать функцию, решающую квадратное уравнение.
Функция принимает в качестве аргумента коэффициенты
a, b, c и возвращает три результата:
число корней, x1 и x2.
Однако вернуть сразу три значения в C++ невозможно:
intRootsCount, numX1, numX2 = quadraticEquation (numA, numb, numC)
Поэтому нам придётся часть результатов выполнения функции передать через указатель в качестве аргументов:
intRootsCount = quadraticEquation (numA, numB, numC, &numX1, &numX2);
Здесь, опять же, от этого приёма можно избавиться, возвращая объект или структуру, хранящую результаты выполнения функции в виде полей.
Можно возвращать результаты решения квадратного уравнения в виде структуры с тремя свойствами [3]:
QuadrEqResult qerResult = quadraticEquation (numA, numB, numC);
intRootsCount = qerResult.count;
numX1 = qerResult.x1;
numX2 = qerResult.x2;
Однако и в первом, и во втором случае, при использовании структур или объектов, мы можем столкнуться с ещё одной проблемой: необходимостью отдельно описывать эти структуры или классы.
Причём во многих языках, например на C++, мы не можем описать структуру или класс внутри самой функции (см. следующий раздел). Нам придётся описывать их отдельно от функции, делая их неподчиненными функции сущностями, что уродует иерархию программы.
Слава богу, в других языках классы можно описывать прямо внутри функции, а, например в JavaScript можно просто возвратить объект, нигде отдельно не описывая его структуру.
function quadraticEquation (numA, numb, numC)
{
//...
return ({
count: intRootsCount,
x1: intX1,
x2: intX2
});
}
var objResult = quadraticEquation (numA, numB, numC);
intRootsCount = objResult.count;
numX1 = objResult.x1;
numX2 = objResult.x2;
Вот это настоящая красота!
Локальная функция должна объявляться внутри функции, которой она логически подчиняется, а НЕ в глобальном контексте.
Как уже говорилось, программные системы (как объектно-ориентированные, так и процедурные) иерархичны и делятся на вложенные друг в друга модули (впрочем, это очевидно).
Каждый из модулей располагает своими локальными ресурсами, используемыми только в рамках этого модуля.
Например, локальные переменные являются ресурсами модуля функции.
Однако ресурсами функции являются не только переменные! Подфункции, классы, структуры и т.д. также являются полноправными ресурсами функции, подчинёнными ей, и используемыми только в её рамках.
Предположим, нам надо написать функцию printDossier, печатающую досье.
Сверху и снизу досье должен находиться его номер,
выровненный по центру и обрамлённый декоративными свасти звёздочками:
****************** 666 ******************
Имя: Макс Отто фон Штирлиц
Звание: штандартенфюрер
Национальность: истинный ариец
Характер: нордический, выдержанный
****************** 666 ******************
Чтобы не дублировать код, печатающий верхний, и печатающий нижний номер,
мы решили вынести его в отдельную функцию printNumber.
Функция, печатающая номер досье, является подчинённой по отношению к функции, печатающей всё досье,
используется только в её рамках и не имеет самостоятельного смысла.
Поэтому, функция printNumber должна быть локальной по отношению к printDossier,
и объявляться в её теле.
#
#Эта функция печатает досье:
#
def printDossier (people):
#
#Эта локальная функция печатает номер досье:
#
def printNumber ():
#Вычисляем количество звёздочек слева и справа, так, чтобы номер оказался по центру
stringLength = 32
numberLength = len (people.number)
starsCount = (stringLength - numberLength) / 2
#Печатаем звёздочки слева, номер досье, и звёздочки справа
print ('*' * starsCount) + people.number + ('*' * starsCount)
#
#Печатаем досье
#
#Печатаем номер сверху
printNumber ()
#Печатаем: имя, звание, национальность и характер
print 'Имя: '+ people.name
print 'Звание: '+ people.rank
print 'Национальность: '+ people.race
print 'Характер: '+ people.character
#Печатаем номер снизу
printNumber ()
Поэтому:
Это уродует иерархию программы, делая подчинённую функцию независимой, и иерархически неподчиненной родительской функции сущностью.
Это нарушает принцип сокрытия информации, вынося наружу детали внутренней реализации родительской функции — подчинённую функцию.
Это затрудняет понимание кода, засоряя глобальный контекст лишними сущностями.
Это приводит к возникновению ошибок, делая возможным случайный вызов подчинённой функции не в родительском, а в глобальном контексте.
Но многие языки программирования, например C++, не поддерживают локальные функции, классы, структуры и т.д.
Поскольку C++ не поддерживает локальные функции, функцию, печатающую номер досье, нам придётся сделать глобальной, независимой и иерархически неподчинающейся функции, печатающей всё досье:
/*
Эта функция печатает номер досье.
Мы вынуждены сделать её глобальной,
хотя она является деталью внутренней реализации функции printDossier
и не имеет самостоятельного смысла.
*/
void printNumber ()
{
//Вычисляем количество звёздочек слева и справа, так, чтобы номер оказался по центру
int stringLength = 32;
int numberLength = strlen (people.number);
int starsCount = (stringLength - numberLength) / 2;
//Печатаем звёздочки слева, номер досье, и звёздочки справа
for (int i = 0; i < starsCount; i++) cout<<"*";
cout<<people.number;
for (int i = 0; i < starsCount; i++) cout<<"*";
}
/*
Эта функция печатает досье
*/
void printDossier (People people)
{
/*
Печатаем досье
*/
//Печатаем номер сверху
printNumber ();
//Печатаем: имя, звание, национальность и характер
cout<< "Имя: " << people.name;
cout<< "Звание: " << people.rank;
cout<< "Национальность: " << people.race;
cout<< "Характер: " << people.character;
//Печатаем номер снизу
printNumber ();
}
Язык Pascal/Delphi поддерживает локальные функции, но, заставляет их объявлять только в начале функции. Это не так страшно, как объявлять только в начале все переменные, но тоже, иногда, бывает достаточно некрасиво.
Предположим, у нас есть функция из нескольких блоков кода, каждый из которых выполняет отдельный шаг всего алгоритма.
Мы решили переписать один их блоков в рекурсивной форме.
Для этого мы переделали его в рекурсивную локальную функцию… после чего вынуждены перетащить этот блок кода (ставший теперь локальной функцией) с того места, где он используется, в начало основной функции.
program main ();
function block3 (param: integer): integer; // (4)──┐
begin // ↑ ↓
Рекурсивный блок кода #3 // ↑ ↓
end; // ↑ ↓
{} // ↑ ↓
procedure block5 (param: integer); // ↑ ↓ (7)──┐
begin // ↑ ↓ ↑ ↓
Рекурсивный блок кода #5 // ↑ ↓ ↑ ↓
end; // ↑ ↓ ↑ ↓
{} // ↑ ↓ ↑ ↓
begin // ↑ ↓ ↑ ↓
блок кода #1 // (1) ↑ ↓ ↑ ↓
{} // ↓ ↑ ↓ ↑ ↓
блок кода #2 // (2) ↑ ↓ ↑ ↓
{} // ↓ ↑ ↓ ↑ ↓
{Прокручиваем на самый верх // ↓ ↑ ↓ ↑ ↓
и находим код рекурсивной функции block3} // ↓ ↑ ↓ ↑ ↓
block3 (param); // (3)──┘ ↓ ↑ ↓
{} // ↓ ↑ ↓
блок кода #4 // (5) ↑ ↓
{} // ↓ ↑ ↓
{Прокручиваем на самый верх // ↓ ↑ ↓
и находим код рекурсивной функции block5} // ↓ ↑ ↓
block5 (param); // (6)──┘ ↓
{} // ↓
блок кода #6 // (8)
end.
Как теперь прикажите читать эту функцию?
Первый блок кода, второй, третий, пока, всё нормально и понятно.
Вдруг, хлоп, вызов рекурсивной подфункции, для чтения которой прокручиваем код к самому началу.
Затем опять возвращаемся, назад и продолжаем читать обычные блоки.
Не намного лучше, чем читать спагетти-код с goto.
К счастью, поддержка локальных функций есть почти во всех «новых» языках, как динамических (JavaScript, Python), так и классических (Java). И надо только воспользоваться этой возможностью.
else if
Уровень вложенности блока должен соответствовать его иерархическому положению в программе.
Блоки кода, имеющие одинаковый уровень вложенности, относятся к одному уровню иерархии программы. Соответственно, блоки кода с большим уровнем вложенности являются подчинёнными по отношению к блокам с меньшей вложенностью.
Вроде бы, это очевидный факт.
Тем не менее, иногда, особенно в учебной литературе, я встречаю нарушение этого правила:
Когда блоки, логически находящиеся на одном уровне иерархии в программе, тем не менее, имеют разный уровень вложенности. Или наоборот, имеют разный уровень иерархии, но одинаковый уровень вложенности.
Предположим, что нам надо приветствовать пользователя, в зависимости от времени суток. Приветствия «Спокойной ночи», «Доброе утро», «Добрый день», и «Добрый вечер» совершенно равноправны и относятся к одному уровню иерархии программы.
Поэтому этот код, где приветствия имеют разный уровень вложенности, страшно уродлив:
if (numHour >= 0 && numHour < 6)
{
print ("Спокойной ночи!");
}
else
{
if (numHour >= 6 && numHour < 12)
{
print ("Доброе утро!");
}
else
{
if (numHour >= 12 && numHour < 18)
{
print ("Добрый день!");
}
else
{
print ("Добрый вечер!");
}
}
}
Его надо переписать так, чтобы равноправные блоки имели равный уровень вложенности:
if (numHour >= 0 && numHour < 6)
{
print ("Спокойной ночи ");
}
else if (numHour >= 6 && numHour < 12)
{
print ("Доброе утро!");
}
else if (numHour >= 12 && numHour < 18)
{
print ("Добрый день!");
}
else
{
print ("Добрый вечер!");
}
Предположим теперь, что мы должны проверить, зарегистрирован ли пользователь в системе, и, если зарегистрирован, то поприветствовать, а если нет — послать вон.
В данном случае, блоки кода приветствия имеют подчинённый уровень по отношению к блоку «зарегистрирован», а блок «пошёл вон» — подчинённое к блоку «не зарегистрирован». Поэтому уродлив код, в котором все блоки имеют одинаковый уровень вложенности:
if (!isRegistred ())
{
print ("Вы не зарегистрированы в системе. Идите вон!");
}
else if (numHour >= 0 && numHour < 6)
{
print ("Спокойной ночи ");
}
else if (numHour >= 6 && numHour < 12)
{
print ("Доброе утро!");
}
else if (numHour >= 12 && numHour < 18)
{
print ("Добрый день!");
}
else
{
print ("Добрый вечер!");
}
Его надо переписать так, чтобы уровень вложенности блоков соответствовал их иерархическому положению:
if (!isRegistred ())
{
/*
Не зарегистрирован
*/
print ("Вы не зарегистрированы в системе. Идите вон!");
}
else
{
/*
Зарегистрирован
*/
if (numHour >= 0 && numHour < 6)
{
print ("Спокойной ночи!");
}
else if (numHour >= 6 && numHour < 12)
{
print ("Доброе утро!");
}
else if (numHour >= 12 && numHour < 18)
{
print ("Добрый день!");
}
else
{
print ("Добрый вечер!");
}
}
В некоторых старых языках программирования (по-моему, в каких-то древних версиях Паскаля или что-то в этом роде)
были операторы if и else, но отсутствовал оператор else if.
Они навязывали приём, при котором каждый последующий блок в цепочке сравнений
имел всё больший уровень вложенности (как в примере №1).
Но сейчас эти языки вымерли как динозавры, и все нормальные языки поддерживают else if.
Поэтому надо писать код, в котором уровень вложенности соответствует уровню иерархии блока, а также не поддаваться на провокации всяких вредных учебников.
При работе с вложенными данными следует соблюдать правила иерархии: свойства должны храниться внутри объекта, а НЕ объект — внутри свойства.
При работе с вложенными данными следует соблюдать правила иерархии.
Во-первых, связанные данные должны иметь общий контейнер.
Благодаря этому объект выглядит именно как объект, и не распадается на множество независимых свойств.
Также, мы можем работать с объектом как с единым целым, например, передавать его в качестве параметра функции и т.д.
В данном примере видно, что lastName, firstName, surName
относятся к одному объекту: objPeople1.
objPeople1.lastName = "Пупкин";
objPeople1.firstName = "Василий";
objPeople1.surName = "Иванович";
Мы можем работать с этим объектом не как с набором свойств, а как с единым целым:
doSomething (objPeople1);
Во-вторых, при обращении к вложенным данным следует соблюдать иерархический порядок: сначала обращаться к корневому элементу, затем к вложенному в него элементу, затем к элементу с ещё большей вложенностью и т.д.
За время написания статьи Путин успел переехать:
World.Russia.Moscow./*Kremlin*/WhiteHouse.Putin
А в данном примере только с помощью иерархического порядка можно определить, что
синий пакет находится в красном:
redBag.blueBag.myThing
…или же красный — в синем:
blueBag.redBag.myThing
В программах на объектно-ориентированных языках это правила иерархии почти всегда соблюдается.
Однако соблюдать правила иерархии при обращении к данным надо не только в объектно-ориентированных языках! Тем не менее, в программах на процедурных языках это правило нередко нарушается.
Одним их вопиющих примеров уродского обращения к данным является использование так называемых параллельных массивов.
Предположим нам надо сохранить данные о советских лидерах, содержащие следующие свойства: фамилию, имя и отчество.
Каждое из этих свойств хранится в отдельном массиве:
char *lastNames [3] = {"Ленин", "Сталин", "Хрущёв"};
char *firstNames [3] = {"Владимир", "Иосиф", "Никита"};
char *surNames [3] = {"Ильич", "Виссарионович", "Сергеевич"};
const lenin = 0, stalin = 1, khrushchev = 2;
И обращение к данным происходит следующим образом:
//Печатаем: "Никита Хрущёв"
cout<<firstNames[khrushchev]<<" "<<lastNames[khrushchev];
При использовании параллельных массивов нарушаются все возможные правила работы с иерархичными данными.
Во-первых, записи распадаются на множество несвязанных полей. Мы не можем работать с записью как с единым объектом.
Вместо работы с объектом как с единым целым, нам приходится работать со множеством полей:
doSomething(lastNames [lenin], lastNames [lenin], surNames [lenin]);
Во-вторых, обращение к вложенным данным происходит «задом-наперёд».
Запись
firstNames[khrushchev]
означает не Хрущёва, хранящего свойство firstName,
а, наоборот, свойство, хранящее внутри себя Хрущёва!
Мне не раз приходилось встречаться с ещё одним приёмом, очень похожим на параллельные массивы: хранение данных в многомерном массиве, где первый индекс отвечает за номер свойства, а второй — за номер записи.
char *leaders [3][3] =
{
{"Ленин", "Сталин", "Хрущёв"},
{"Владимир", "Иосиф", "Никита"},
{"Ильич", "Виссарионович", "Сергеевич"}
}
const lenin = 0, stalin = 1, khrushchev = 2;
const lastName = 0, firstName = 1, surName = 2;
Обращение к данным происходит следующим образом:
leaders [propertyNumber][peopleNumber]
Например:
//Печатаем: "Владимир Ленин"
cout<< leaders [firstName][lenin]<<" "<< leaders [lastName][lenin];
//Печатаем: "Никита Хрущёв"
cout<< leaders [firstName][khrushchev]<<" "<< leaders [lastName][khrushchev];
//Печатаем: "Иосиф Сталин"
cout<< leaders [firstName][stalin]<<" "<< leaders [lastName][stalin];
Этот приём имеет только одно преимущество по сравнению с параллельными массивами — список всех записей объединён в единый объект. Однако все остальные недостатки параллельных массивов никуда не деваются:
Во-первых, записи, по прежнему, распадаются на множество несвязанных полей.
Вместо работы с объектом как с единым целым, нам опять приходится работать со множеством полей:
doSomething(leaders [lastName][lenin], leaders [firstName][lenin], leaders [surName][lenin]);
Во-вторых, обращение к вложенным данным происходит «задом-наперёд».
Запись
leaders [propertyNumber][peopleNumber]
означает
не человека peopleNumber хранящего свойство propertyNumber,
а, наоборот, свойство, propertyNumber хранящее внутри себя человека!
Использовать красивые приёмы работы с данными не так сложно.
Вместо параллельных массивов можно даже воспользоваться предыдущим приёмом с многомерными массивами, просто переставив индексы местами, так чтобы: сначала шёл номер записи, а затем — номер свойства.
char *leaders [7][3] =
{
{"Ленин", "Владимир", "Ильич"},
{"Сталин", "Иосиф", "Виссарионович"},
{"Хрущёв", "Никита", "Сергеевич"},
{"Брежнев", "Леонид", "Ильич"},
{"Андропов", "Юрий", "Владимирович"},
{"Черненко", "Константин", "Устинович"},
{"Горбачёв", "Михаил", "Сергеевич"},
};
const lenin = 0, stalin = 1, brezhnev = 2, gorbachev = 6;
const lastName = 0, firstName = 1, surName = 2;
//Печатаем: "Владимир Ленин"
cout<<leaders [lenin][firstName]<<" "<< leaders [lenin][lastName];
//Печатаем: "Леонид Брежнев"
cout<<leaders [brezhnev][firstName]<<" "<< leaders [brezhnev][lastName];
//Печатаем: "Михаил Горбачёв"
cout<<leaders [gorbachev][firstName]<<" "<<leaders[gorbachev][lastName];
Этот способ уже имеет ряд преимуществ перед параллельными массивами.
Во-первых, «свойства» «объекта» объединены в единый контейнер. Мы можем работать с «объектом», как с единым целым.
Делаем что-то с Хрущёвым:
doSomething (leaders [khrushchev]);
Во-вторых, обращение к данным идёт в иерархическом порядке.
Все советские лидеры:
leaders
Лидер — Хрущёв:
leaders
[khrushchev]
Имя Хрущёва:
leaders
[khrushchev]
[firstName]
Однако, использование многомерных массивов имеет и ряд серьёзных недостатков.
Во-первых, обращение к «свойствам» происходит не через осмысленное имя:
leaders[2].firstName, а через номер свойства: leaders[2][1].
Для нормального обращения через осмысленные имена приходится объявлять лишние константы, что тоже достаточно уродливо.
Во-вторых, «объект» не может иметь «свойства» разных типов. Пожалуй, это самый серьёзный недостаток многомерных массивов.
Разумеется, самым правильным и красивым решением является создание массива объектов или структур.
People *leaders [7] =
{
new People ("Ленин", "Владимир", "Ильич"),
new People ("Сталин", "Иосиф", "Виссарионович"),
new People ("Хрущёв", "Никита", "Сергеевич"),
new People ("Брежнев", "Леонид", "Ильич"),
new People ("Андропов", "Юрий", "Владимирович"),
new People ("Черненко", "Константин", "Устинович"),
new People ("Горбачёв", "Михаил", "Сергеевич")
};
//Эти константы – только для удобства чтения примера. В реальном коде их не будет
const lenin = 0, stalin = 1, brezhnev = 2, gorbachev = 6;
//Печатаем: "Владимир Ленин"
cout<<leaders [lenin].firstName<<" "<< leaders [lenin].lastName;
//Печатаем: "Леонид Брежнев"
cout<<leaders [brezhnev].firstName<<" "<< leaders [brezhnev].lastName;
//Печатаем: "Михаил Горбачёв"
cout<<leaders [gorbachev].firstName<<" "<< leaders [gorbachev].lastName;
Размер массива должен быть доступен через его свойство, а НЕ только из отдельной переменной.
Итак, при работе с вложенными данными следует соблюдать правила иерархии: связанные данные должны иметь общий контейнер; и при обращении к ним следует соблюдать иерархический порядок.
В процедурных языках эти правила нередко нарушаются, причём не только при использовании параллельных массивов.
Ещё одним примером некрасивой работы с данными является обязательное хранение размера массива в отдельной переменной.
Предположим, нам надо считать в массив фамилии из текстового файла.
Поскольку число фамилий нам заранее неизвестно,
мы объявляем массив names с заведомо большим количеством элементов (например, 1000),
а число реально используемых элементов храним в отдельной переменной length.
var
//names - массив фамилий с заведомо большим количеством элементов
names : array [0..1000] of string;
//length - число реально использованных элементов
length: integer;
begin
assign (input, 'input.txt'); reset (input);
{/*
Считываем данные из файла в массив
*/}
length := 0;
while not SeekEof (input) do
begin
length := length + 1;
read (names [length]);
end;
end.
При этом, так же, нарушаются все возможные правила работы с иерархичными данными:
Во-первых, массив перестаёт быть единым объектом и распадается на две независимые сущности: собственно данные и размер массива.
Вместо работы с массивом как с единым целым, нам приходится передавать его по частям:
собственно данные — names и размер — length.
doSomething (names, length);
Во-вторых, нарушается иерархический порядок работы с данными.
Размер массива должен быть доступен через его свойство length
(или, в крайнем случае, через функцию len (array), как в Python, например).
Благодаря этому:
Во-первых, мы можем работать с массивом как с единым объектом.
Делаем что-то с массивом:
doSomething (names);
Во-вторых, обращение к свойству массива идёт в иерархическом порядке.
Весь массив:
array;
Длина массива:
array.length;
Разумеется, нет ничего плохого в том, чтобы временно закешировать размер массива в отдельной переменной (например, в целях производительности).
Главное, чтобы размер массива при этом всегда был доступен через свойство и без всяких внешних переменных.
Предположим, нам надо написать на JavaScript функцию, суммирующую в цикле элементы массива.
Чтобы в каждой итерации цикла не выполнять
достаточно ресурсоёмкое для динамического интерпретируемого языка обращение к свойству length,
мы можем сохранить размер массива во временной переменной intLength.
function summ (arNumbers)
{
var intSumm = 0;
//intLength - размер массива, закешированный во временной переменной
var intLength = arNumbers.length;
for (var i = 0; i < intLength; i++)
{
intSumm += arNumbers [i];
}
return (intSumm);
}
Однако, при этом, размер массива нам всегда остаётся доступен через свойство length.
И мы всегда можем обойтись без внешней переменной:
//Передаём массив как единое целое, без отдельной передачи его размера
var intSumm = summ (arNumbers);
object.getProperty () и object.setProperty (value)
У полей и методов объектов есть своё чёткое предназначение:
Поля — хранят данные;
Методы — реализуют поведение объекта.
В нормальном коде ясно видно, где идёт работа с данными, а где реализуется логика поведения объекта:
Работа с данными: objObject.property1 = "value1";
Поведение объекта: objObject.doSomething (param1, param2);
Использование методов в качестве акцессора и мутатора поля — уродство.
Во-первых, смешиваются два различных понятия: данные объекта и его поведение.
Нарушается естественная конструкция для доступа к данным через оператор присваивания. Оператор присваивания самой своей сутью подразумевает присваивание.
objObject.property1 = "value1";
strValue = objObject.property1;
Вызов метода, само использование круглых скобок, подразумевает реализацию поведения объекта.
Во-вторых, чтение и запись свойства реализуется по-разному, что противоречит сути поля.
При использовании свойств, для доступа к полю, как для чтения, так и для записи мы используем одну конструкцию:
objObject.property1
intA = objObject.property1; //Чтение
objObject.property1 = intB; //Запись
При использовании методов, для чтения и для записи поля используются разные конструкции:
objObject.getProperty1 () и objObject.setProperty1 ():
intA = objObject.getProperty1 (); //Чтение
objObject.setProperty1 (intB); //Запись
К полю не будет возможности применять стандартные операторы работы с данными, такие как ++, += и др.
Мы хотим увеличить значение свойства на 1.
При использовании свойств мы можем воспользоваться естественным для этого действия оператором инкрементации:
objObject.property1++;
При использовании методов, для выполнения элементарного действия приходится писать такую кашу:
objObject.setProperty1 (objObject.getProperty1 () + 1);
Для обращения к защищённым полям объекта как к данным, используются свойства. (Внимание: не «открытые поля», а именно — «свойства», см сноску [4]).
Свойство — это интерфейс для красивого и безопасного доступа к данным объекта [5].
При его использовании вызываются методы доступа к данным (например, для выполнения проверки на правильность записываемых в свойство данных).
Однако, вызов этих методов происходит «прозрачно» для разработчика,
что позволяет красиво обращаться к данным:
value = object.property и object.property = value.
Класс Person хранит поле _money, доступ к которому осуществляется через свойство money:
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; //Инкрементация
Свойства поддерживает большое количество современных языков: Delphi, C#, Python, Ruby и др.
Однако немало языков свойства не поддерживают: C++, Java и даже гибкий и красивый JavaScript [6]…
Знаете, есть две вещи, которые обязательно надо добавить в JavaScript. Но это не классы и строготипизированные переменные, как думают многие. Отсутствие классов и строгих типов — это не баг, а фича, дающая JavaScript такую гибкость.
Две возможности, которых действительно не хватает в JavaScript — это перегрузка операторов и поддержка свойств [7].
Рекурсию следует использовать только в тех случаях, когда решение задачи на каждом шаге разбивается на несколько подобных подзадач более низкого ранга
Здесь, в отличие от предыдущих разделов, я не буду столь категоричен.
Ибо рекурсия, или даже философия рекурсии, штука не такая простая.
И вопрос, когда следует (вернее, когда красиво) применять рекурсию, а когда нет, не столь однозначен.
Ну, в функциональных языках (таких как Lisp или Haskell) всё понятно: рекурсия применяется всегда, когда надо выполнить любые повторяющиеся действия. Там даже сумма элементов массива (там он называется списком) вполне может определяться рекурсивно как сумма первого элемента + сумма оставшейся части. В этих языках такой подход гармонирует с философией языка и, потому, красив.
Программа вычисления факториала на функциональном языке Haskell красива,
как в интерационной форме:
factorial n = product [1..n]
…так и в рекурсивной с использованием приёма «Сопоставление с образцом»:
factorial :: Integer -> Integer
factorial 0 = 1
factorial n | n > 0 = n * factorial (n - 1)
В императивных же языках всё сложнее.
Мне кажется, что в этих языках смысл рекурсии состоит в разбиении задачи на несколько подобных подзадач более низкого ранга. А тех в свою очередь — на насколько под-подзадач, и так, в геометрической прогрессии, пока мы не дойдём до тривиального случая.
При рекурсивном обходе дерева, мы разбиваем дерево на несколько поддеревьев (ветвей), каждое из поддеревьев на под-поддеревья, и так, пока не дойдём до листов.
При вычислении определителя матрицы, мы находим определители нескольких подматриц меньшего порядка (миноры), для нахождения же миноров, мы разбиваем каждую из подматриц на под-подматрицы и т.д. пока не дойдём до матриц 2×2.
При сортировке массива слиянием мы сортируем несколько (обычно два) подмассивов этого массива. Для сортировки каждого подмассива мы сортируем их под-подмассивы, пока не дойдём до подпод…подмассивов длины 2.
Поскольку задача разбивается именно на несколько подобных подзадач, то количество данных (локальных переменных) на каждом шаге рекурсии увеличивается. Из-за этого заранее красиво объявить все эти локальные переменные в итеративном алгоритме не получится. Можно, конечно, вручную организовать стек, хранящий состояния каждой «итерации», но это уже будет замаскированная рекурсия, пусть и в обычном цикле без вызова функций. Что нелаконично, малопонятно и не очень красиво. И, соответственно лаконично и красиво использовать рекурсию.
Когда же задача не требует разбиения на несколько подобных, смысл рекурсии вырождается. И применять её в таких случаях — некрасиво.
Самым вопиющим примером уродского применения рекурсии является её использование при вычислении факториала и чисел Фибоначчи.
Я даже не говорю, что это страшно не эффективно. Я говорю, что вычисление факториала и чисел Фибоначчи — чисто итерационная задача, и рекурсивное её решение — это извращение самого смысла, самой сути рекурсии в императивных языках [8].
Как ни странно, эти задачи часто приводят в учебной литературе, причём в самом начале обучения рекурсии, в качестве первого примера. Быть может, рекурсия считается столь сложным для обучения приёмом, именно из-за того, что её объяснение ведётся на совершенно противоестественных её сути примерах…
Параметры любой нетривиальной функции должны задаваться по осмысленному имени, а НЕ положению в списке аргументов.
Никто не будет спорить с тем, что имена должны отражать суть переменённых.
И что использование имён переменных вроде a0, a1, a2 — не самый понятный и красивый приём.
Точнее, не иметь осмысленного имени могут переменные, не являющиеся уникальными, входящие в состав коллекции, обрабатываемые не каждая отдельно, а вместе, в цикле.
Осмысленные имена должны быть у уникальных переменных, обрабатываемых отдельно.
Программа, выводящая имя продукта, его кодовое имя и список глюков:
println ("Имя продукта: "+ objWinVista.name); //Windows Vista
println ("Кодовое имя: "+ objWinVista.codename); //Longhorn
println ("Число глюков: "+ objWinVista.bugsCount); //1 000 000 000 :-)
println ("Список глюков:");
for (long numBugNumber = 0; numBugNumber < objWinVista.bugsCount; numBugNumber++)
{
println (objWinVista.bugs [numBugNumber]);
}
В данном примере 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 (
new Alpha (-1, Alpha.DECREASING_ENABLE, 0, 0, 8000, 0, 0, 0, 0, 0),
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, может быть есть ещё).
Функция перевода текста.
$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);
Гораздо больше кода, но зато более понятно и красиво. Хотя, объявление лишних структур для каждой функции, тоже не очень красивое решение.
К счастью, сейчас процедурные языки используются достаточно редко, и главным образом в тех случаях, когда больше важна не красота и понятность, а скорость выполнения программы (например, C).
В классических объектно-ориентированных языках вместо сложной функции,
лучше всего реализовать шаблон проектирования
«Command»:
создать объект; затем, обращаясь через свойства с осмысленными именами, записать в него данные;
и вызвать метод apply ();
Rectangle rectangle1 = new Rectangle ();
rectangle1.x = 80;
rectangle1.y = 25;
rectangle1.width = 50;
rectangle1.height = 75;
rectangle1.rotation = 30;
rectangle1.borderWeight = 2;
rectangle1.borderColor = "red";
rectangle1.backgroundColor = "blue";
rectangle1.alpha = 20;
rectangle1.draw ();
Опять же, гораздо больше кода, но зато понятно, и достаточно красиво.
И, наконец, в динамических языках, не поддерживающих именованные параметры (например JavaScript), в качестве параметра функции можно передать созданный на лету объект с осмысленными свойствами.
Функция анимации из библиотеки Dojo Toolkit. В качестве аргумента функции передаём объект со свойствами-аргументами функции:
var slideLeft = dojo.fx.slideTo
({
node: divAnim,
duration: 1000,
left: 0,
top: 80
});
Функция поиска файлов. Объект с аргументами имеет сложную структуру:
var arSerchResult = searchFiles
({
text: "Пушкин",
extensions: ["txt", "rtf", "doc", "pdf", "odt"],
hidden: true,
modified:
{
from: new Date (01, 02, 2008),
to: new Date (15, 03, 2008)
},
size:
{
min: 10000,
max: 1000000
}
});
Лаконично, понятно и красиво!
Пока всё на этом.
Наверняка, в некоторых местах вы будете со мной несогласны — ведь чувство красоты у всех разное.
Ну и не стоит забывать, что отсутствие в коде приведённых «уродских приёмов» — лишь идеальная ситуация, к которой надо стремиться, но приходится жертвовать в определённых ситуациях (например, в целях производительности). Ведь умение приносить жертвы при решении практических задач — один из элементов профессионального программирования.
↑ Стив Макконнелл ещё более категоричен, чем я. В книге «Совершенный код» он пишет:
В идеальном случае сразу объявляйте и определяйте каждую переменную непосредственно перед первым обращением к ней
↑ Вообще, надо стараться избегать длинных функций и методов. Одно из правил рефакторинга: «если текст метода не умещается на экране без прокрутки — это уже повод разбить его на несколько более коротких методов».
Однако, это далеко не всегда удаётся, например при генерации отчётов или при реализации сложных вычислений.
↑
Есть и противоположенная точка зрения.
Например, Ховик Меликян в статье «Клиника плохого кода» пишет, что
возврат корней квадратного уравнения через параметр функции:
num_roots = qe_solve(10, 20, 2, &x1, &x2); — гораздо красивее, чем использование объектов.
↑ Не надо путать методы, открытые поля и свойства (как, например, в этом самоуверенном комментарии).
Открытое поле — это просто переменная внутри объекта, доступ к которой извне ничем не ограничен.
При использовании открытых полей нет никакой возможности отследить обращение к ним, или провести проверку корректности записываемых в поле данных.
Поэтому, использование открытых полей — нежелательно.
Для контроля обращения к данным можно использовать методы
object.getProperty() и object.setProperty(value).
Методы позволяют отслеживать обращение к данным, выполнять при этом сопутствующие действия и проверять правильность введённых данных.
Однако, использование методов — очень некрасиво, о чём я и пишу в статье.
Тем не менее, в языках, не поддерживающих свойства, при выборе между использованием открытых полей и методов — лучше использовать методы. Хоть это и очень некрасиво, но, зато, гибко и безопасно.
Вместо открытых полей и методов, при наличие такой возможности, следует использовать свойства.
Свойства — это интерфейс для доступа к данным, совмещающий гибкость и безопасность методов с красотой открытых полей.
При обращении к свойствам вызываются всё те же методы, однако вызов их происходит «прозрачно» для разработчика.
Это позволяет красиво обращаться к данным: value = object.property и object.property = value.
Как в этом примере с классом Person и свойством money .
↑ Разумеется, свойствами, как и любой другой возможностью, не стоит злоупотреблять, применяя их где только можно.
Свойства следует использовать для реализации доступа к данным объекта. Ведь использование свойств, само использование оператора присваивания подразумевает доступ к данным.
Если же, при обращении к данным происходят сильные побочные действия — использовать свойства нежелательно.
В этом случае, красиво и правильно, как раз, использовать методы. Ведь вызов метода, само наличие круглых скобок предполагает реализацию поведения объекта. О чём я и писал в самом начале этой главы.
↑ Формально поддержка свойств появилась ещё в JavaScript 1.5:
object.__defineGetter__ ("property", function () {/*Код аксессора*/})
object.__defineSetter__ ("property", function (value) {/*Код мутатора*/})
Однако она только относительно недавно стала работать в «нормальных браузерах» и, несмотря на все обещания, до сих пор не работает в Internet Explorer.
↑ Перегрузка операторов появится только в JavaScript 2.0.
↑ Для вычисления чисел Фибоначчи использование рекурсии имеет какой-то смысл: всё-таки основное определение этой функции является рекурсивным:
Fib (n) = Fib (n−1) + Fib (n−2); Fib (0) = 0; Fib (1) = 1
Однако, наличие же у факториала рекурсивной формы записи:
n! = n × (n−1)!
нисколько не приближает нас ни к пониманию смысла рекурсии,
ни к пониманию смысла факториала
как произведения чисел от 1 до n.
С первым и вторым пунктом НЕ согласен.
Объявление переменных в начале логического блока позволяет контролировать их использование и занятость в блоке. Сам пишу на Delphi, а в последнее время все чаще на PHP. Уже пару раз встречал подобное:
tmp = rand(1, 10); // Тут работаем с tmp tmp = random_string(); // Тут работаем с новым tmp ... tmp = tmp * 1.5;
В том же Delphi такое не реально.
По поводу возврата результата через параметр: да, по большей части такое только вредит. Но, бывают случаи, когда из функции надо вернуть 2 результата: результат работы функции и доп.данные. В таких случаях, можно воспользоваться массивом или классом, но мне это не нравится. Проще что-то, наподобии следующего:
function doSum(aParam1, aParam2: integer; out oResult: integer):boolean;
С пунктом «Доступ к свойствам объекта через object.getProperty () и object.setProperty (value)» не согласен.
Во-первых, смешиваются два различных понятия: данные объекта и его поведение.
Представьте себе ситуацию, в какой-либо программе поголовно используется код типа psnBillGates.money = lngNewRiches;
Раз этак сотню-другую. А потом вдруг при каждом изменении денег у psnBillGates, да и у любого объекта того же класса, потребуется оповестить налоговую службу об изменении денег на счету. Как вы будете поступать в этом случае, к каждой строчке psnBillGates.money = lngNewRiches; будете дописывать оповещение служб. Мне кажется проще использовать метод setMoney(value) и в нём единожды добавить оповещение и налоговой, и антимонопольной службы.
То есть на изменение данных у объекта должна быть возможность реагировать (то же поведение)! (Да хотя бы проверка на валидность нового значения)
Да по сути все верно написано.
Есть конечно кое какие притензии по поводу 6-го пункта.
Вот пример обстрактный
a = array(1,2,3);
for(i=0, len = a.len; i<len; i++)
{
...
}
ну вот как тут можно обойтись без 6-го пункта?
если не заводить отдельную переменную для длины масива то её прийдётся вычислять каждую итерацию, это не всегда хорошо да и дальше, если масив большой
думаю дальше...
Объявление всех переменных в начале функции — страшное зло.
Я с этим не согласен, ведь если есть какие-то (логические) блоки в методе значит этот метод можн разбить на совокупность других методов.
Не в стречал методов с отдельными логическими блоками, которые нельзя разбить на несколько методов.
А функции по 200 строк - ....
>Использование методов в качестве акцессора и мутатора поля — уродство.
А если нужно какуюто валидацию делать? В свойстве это не уродство?
Cheatsheet на основе этого можно было бы сделать красивый.
Но, к сожалению, универсальных coding guidelines не бывает.
А давно в lisp нельзя писать последовательно инструкции?
а что делать с do нотацией в haskell?
Чувак в общем. По порядочку.
"Переменные должны объявляться в начале логического блока, в котором они используются, а НЕ в начале функции или программы."
Тут кому как удобно... Тут сильно спорить не буду.
"Функция должна возвращать результат, зависящий от ее параметров, а НЕ принимать результат в качестве аргумента."
Ничего она не должна. Смотри сюда. Есть вектор который надо наполнить внутри ф-и... Т.е. легче сделать так
int foo(std::vector<int> &vec1)
{
//сбор вектора.
}
main
{
std::vector<int> vec;
foo(vec);
//тут уже плностью готовый вектор. И нет затрат на сборку конструирование, копирование и разрушение вектора. И т.д.
}
"Отсутствие локальных функций"
Как по мне так полный бред... В с++ есть отдельные блоки, но ты первый в них запутаешься... Если они у тя будут по 20 а то и больше строк..
"Отсутствие else if"
Тут все зависит от ситуации...
"Для доступа к данным должны использоваться свойства, а НЕ методы."
Круто! По ООПшному! Слов нет, и что такое инукапсуляция тоже никто не задумывается.. Да бес с ней! Любой объект и не только меняет проперти этого обьекта. Или иными словами меняют проперти все кому не лень.
"Параметры любой нетривиальной функции должны задаваться по осмысленному имени, а НЕ положению в списке аргументов."
Эээ, полный бред... У функции есть набор параметров такой какой считает нужным автор ф-и... И это полный бред... Извини...
З.Ы. Я не знаю может ты и мнишь из себя крутого программера, но почитай мат часть... Поможет... Про массивы и числа фибоначи просто умолчу, хотя и про это есть что сказать... Но лень.. Так шо книги почитай а потом в сеть пихай... И еще не отправляй это на rsdn. Засмеют...
deepshark, а что мешает добавить оповещение в блок set? :)
То есть на изменение данных у объекта должна быть возможность реагировать (то же поведение)! (Да хотя бы проверка на валидность нового значения)
Вы путаете поля и свойства. Свойство (property) в современных языках — это геттер и сеттер, маскируемые под обычное поле. А снаружи всё прозрачно.
За статью спасибо. Интересно и качественно написано. В большинстве вещей с вами согласен.
Именованные параметры кажется есть ещё в Groovy, если не путаю ничего, ибо знакомился поверхностно и с удовольствием забыл.
Никогда кстати не задумывался насчёт ненужности рекурсии в некоторых ситуациях. Хотя вроде и не злоупотреблял.
Спасибо за статью
Можно поставь якаря для главного списка ( чисто для новых читателей )
Представьте себе ситуацию, в какой-либо программе поголовно используется код типа psnBillGates.money = lngNewRiches;
Раз этак сотню-другую. А потом вдруг при каждом изменении денег у psnBillGates, да и у любого объекта того же класса, потребуется оповестить налоговую службу об изменении денег на счету. Как вы будете поступать в этом случае, к каждой строчке psnBillGates.money = lngNewRiches; будете дописывать оповещение служб. Мне кажется проще использовать метод setMoney(value) и в нём единожды добавить оповещение и налоговой, и антимонопольной службы.
То есть на изменение данных у объекта должна быть возможность реагировать (то же поведение)! (Да хотя бы проверка на валидность нового значения)
Ну так просто в свойство добавляеться оповещение. Свойства - это и есть объединенный set и get. Просто они позвляют получать нормальный доступ с применением операторо.
Спасибо за отличную статью. Некоторая часть подобных мыслей самому приходила в голову.
За статью огромное спасибо!!!
Полностью согласен со всеми пунктами.
Я сматрю многим не понравилось работа с методами через getProperty(),setProperty(value), кто-то даже про инкапсуляцию говарил и про опавещение кого-то там, друзья советую вам взглянуть как это сделано в C#, акцессоры и мутаторы никуда не диваються!!!
Мы просто преобритаем удобный нэмспейс и плюсы использования операторов с данными свойствами класса, отсюда красоту написания кода.
Очень надеюсь что будет продолжение. Так как для новичка это статья будет очень интересна.
Об искустве программирования можно многое что написать ;-).
СПАСИБО ещё раз!!!
к Автору.
Во многом согласен. Кроме вот этих пунктов:
Первое:
Это говорит только о том, что Вы пишите не думая вот и все. Переменные объявленные в начале программы (точнее в начале каждой подпрограммы, если быть точным) делают код однозначным, заставляют не писать спагетти-код (длиной более одного экрана). И еще Если Переменная нужна только один раз, то в 80% случаев без нее можно обойтись! А иначе код становится трудно-читаемым!
Далее по тексту:
4. Отсутствие else if;
else if - первый шажок к нечитаемому коду. Замена - синтаксис case (swich в других языках): во-первых, он читаемее, во-вторых, чаще всего работает быстрее, так как нет лишних проверок.
Пункты с которыми согласен лишь отчасти:
6. Хранение размера массива в отдельной переменной;
Иногда (когда речь идет о действительно больших массивах) затраты на много кратное вычисление длины массива не оправданы, и проще сохранить длину массива в отдельном месте.
9. Отсутствие именованных параметров функции;
Крайне спорно. При написании кода
x=func_name(param1:lala,param2:lala2);
Текст увеличивается и как следствие менее читаем. Во-вторых, возможность жонглирования порядком переменных - прямой путь к ошибке!
Michael, artyfarty, Ilya, спасибо, был невнимателен, смотрел узко.
Алик Кириллович, спасибо за статью и дискуссию.
Статья отличная, спасибо.
Возражу по поводу чисел фибоначчи: не взирая на то, что их определение рекурсивно, рассчитывать их рекурсивно - очень большая ошибка, ведь F(n) = F(n - 1) + F(n - 2) =
(F(n - 2) + F(n - 3)) + F(n - 2) - тоесть у нас уже на втором шаге два независимых вызова F(n - 2), каждый из которых в свою очередь вызовет F(n - 3) и F(n - 4). Таким образом мы получим лавинную реакцию, которая съест процессор при маленьких n, а при сравнительно небольших (~50-60) стает уже неподъемной. Для сравнения - итерационный алгоритм линейный, и скорее упирается в длину целого числа.
Можно также написать умную рекурсию, которая будет кешировать результаты - но это уже другая история.
Вобщем резюме: если рекурсивная функция вызивает себя более одного раза - подумайте о границах дерева вызовов. Оно может оказатся значительно больше, чем вы ожидаете.
Попытаюсь тоже пройтись по порядку:
1). пункт - не согласен точнее согласен лишь отчасти, в случае если пишется функция, то место под неё в стеке всё равно выделяется сразу под все локальные переменные. Если переменные стоят в начале, то сразу видно, что и как используется функцией. Если они граммотно прокомментированы, то проблем вобще не возникает. Про "поменять местами 2 переменные" - фунции рефакторинга в IDE придумали трусы?
Смысл выделать переменные есть если использовать блоки {} в C подобных языках. Но почему бы в таких случаях не использовать вложенную функцию.
2). возврат функции через результат точно нужен в функциональных языках программирования там без этого никуда :). В остальных же опять возникают вопросы иногда это действительно удобно иногда нет, например вы возвращаем результат + код ошибки(или даже список ворнингов, чтобы не было соблазна исключение кидать), тогда тут надо делать объект обёртку, что не всегда удобно особенно в языках типа C/C++. В языках где объекты делаются на лету, конечно большой проблемы не стоит.
Про матрицы был приведён ужасный пример на C. Вы создаёте объект внутри функции в локальной переменной, не подскажете, а что происходит с локальными переменными после возврата из функции? Правильно они освобождаются, а у Вас радостно передаётся ссылка на переменную "зомби", (в Яве,C# и проч языках со сборщиками мусора подобной проблемы нет, но в C/C++/Pascal надо бы об этом помнить).
3). Круто, тут ничего не скажешь :) жаль не во всех языках это реализуемо. Ну и наверное если локальная функция потом может использоваться в разных функциях, то "привет refactoring".
4). Пример хороший, по в Paskal варианты "if ( cond1) and (cond2) then" не равнозначен варианту "if (cond1) then if (cond2) then", т.к. паскаль в первом случае не прервёт проверку после проверки cond1. Вобще в этом вопросе всё сильно зависит от языка и компилятора.
Написавшему, что else if это замена switch просьба вспомнить, что в switch есть магическая команда break; и код типа switch(a) { case 1: do1; case 2: do2;break; } очень неудобно переписывается через else if. А if (cond1 && cond2){} else if (cond3) {} никак не пишутся через switch.
5). статья правильна во всех случаях когда не нужна сверхскорость. на опыте (паскаля) паралельные массивы просто быстрее (порядок уже сказать не могу давно было)
7). доступ к fields через properties. (огорчило не знаение участниками дискуссии, того что такое property). Тут вопрос опять же спорный. В принципе property это очень круто, если язык позволяет использовать разные уровни доступа. т.е. private setField; public getField; /паскаль не позволяет, в java только в следующей версии появятся свойства/, в этом случае пропертями не обойтись
8). согласен с автором. В не функциональных языках действительно лучше не использовать рекурсию там, где это возможно. Но надо вспомнить о том, что можно сохранять результат вычисления фунции в зависимости от поданных значений. Причём в c (с gcc точно) компилятор может сам позаботиться об этом - в таком случае можно получить прибавку в скорости (за счет памяти)
9). да человеку, которому лень изучать Ваши интерфейсы так будет удобно. Так же это удобно, когда можно передавать не все параметры из большого кол-ва, тогда не надо писать тысячи перегруженных функций (особенно в пхп где перегрузки нет (не уверен на счёт пхп5). Но хорошо бы подумать за счёт чего появляется это удобство и как работает такой подход :). Может получиться так, что потери слишком большие. Мне понравился вариант в python где функция задаётся как def name(a,b,c,k*,k**): где k* это список именованных параметров k** просто список параметров по номеру (пинайте больно если перепутал порядок), но общий смысл тот, что там обработаны все возможности: передача с заданным порядком, именованные параметры, передача неописанных именованных параметров, передача списка неименованных параметров.
10). возможность создания объектов это круто конечно. Но опять же нужно думать какие у этих объектов могут быть возможности и что будет стоять за этим созданием объектов. В общем в языках с использованием статистического /не путать со static/ связывания мы его уже не получим.
Все могу слова не претендуют на оригинальность или правильность.
Как говорит препод по информатике: "В программировании всегда приводятся противоположные рекомендации. Поэтому умение программировать заключается в том, чтобы выбирать среди рекомендаций правильный баланс. Следование любому набору рекомендаций доведённому до абсурда приведёт к тому, что программа будет нечитаемой, неподдерживаемой и нестабильной".
С Уважением.
Не согласен по поводу локальных функций.
Свое не согласия вырожу только одним вопросом:
При повторном использовании локальной функции в другой функции ее что придется искать(а я понял автор очень сильно не нравиться ходить по коду) и копировать или забить на поиски и плодить код, не хорошо это...
И по свойствам, да в тех языках где поддерживается синтаксис и использование понятия свойство не нужно плодить методы, но по-моему в C\C++ без функций не обойтись, ведь данные объекта(класса) должны храниться отдельно, а любые действия над ними оформляются функциями. Один из принципов ООП (инкапсуляция).
И еще примеры с факториалом и числами Фибоначчи, правильно написано приводятся в учебниках как пример, так как легче объяснить что значит рекурсия. Рекурсия сложное понятие, и не многим она дается с первого раза, да после обучения нужно писать более организованный код, но это уже остается на совести пользователя.
Спасибо за статью, полезно прочитать.
автор. респект!
сам думаю 100% также.
АХРЕНЕННО.
Последнее время начинаю думать, почему все языки не есть яваскрипт + c# ? И тогда не пришлось бы плодить адское колво 1 разовых языков
@Melis
9. Отсутствие именованных параметров функции;Крайне спорно. При написании кода
x=func_name(param1:lala,param2:lala2);Текст увеличивается и как следствие менее читаем. Во-вторых, возможность жонглирования порядком переменных - прямой путь к ошибке!
Жонглирование порядком? Насколько я понимаю, список аргументов предстает ассоциативным массивом.
Статья интересная. Но мне(да и не только мне) было бы интереснее, если бы пример кода был бы на двух языках. К примеру на вашем(я так его и неопределил) и на питоне к примеру
Все правильно сказано, я сам программист, и придерживаюсь точно таких же принципов.
Статья имеет огромное значение для начинающих программистов, вроде меня...
Сжечь еретика! У каждого своё понятие красоты, не надо приводить аксиомы, забивая молодежи головы больными стереотипами...
@phpdude
Последнее время начинаю думать, почему все языки не есть яваскрипт + c# ? И тогда не пришлось бы плодить адское колво 1 разовых языков
Вы имеете ввиду asm, c, bash,java, pascal etc.?
Почти все советы спорные и даже очень.
А для кого стараться? Для взломщиков? Ага! Чтобы им код понимать легче было! Каждый программирует так, как ему легче и понятнее.
Здрасте!
Первый же приём...
А вам не кажется, что если класс или метод можно разбить на логичесмкие блоки, то лучше было бы разбить сам класс или метод на несколько отдельных классов/методов?
И сразу же приводите пример огромного метода, который можно было бы разбить на 5 нормальных и у которых объявление всех локальных переменных начинается в начале метода. :)
З.Ы.: А на картинке что? Неужели мне не привиделось и вы объявляете стопку переменных в одной строке, через запятую? И кто говорит о чистоте кода?
var intLength, elTemp, intCenterIndex, intLeftSubarrayLength, intRightSubarrayLength, arSortedSubarray, intLeftElIndex, intRihtElIndex, i;
Вы коллекционируете самые плохие практики программирования?
Вы только представьте: У нас есть функция в 300 строк кода [2].
Вот где зло и о чём надо было писать, а не о постановке переменных.
Извините, что сразу такие комментарии. Просто сразу, первое же и задело. Видел я такой код, в нём прривлекательности ровно нуль. А, вообще, очень достойная попытка описать что-нибудь - очень добротно и многословно, прям разжевали и в рот положили.
Я не думаю что JavaScript именно тот язык программирования, который нужно приводить в пример в статье о "красоте программного кода".
Возврат результата функции через её параметр - очень нужная и важная штука.
Локальные функции лучше вообще вынести в отдельный контекст/класс *Context.
В общем-то это всё моё ИМНО.
Я раньше загонялся красотой кода, структурой и т.д. Но после того как на меня наехал препод по кодингу за то, что я использую не рациональный подход, предусмотрев слишком много лишних вариантов и код получился у меня не понятный(хотя его оптимизация заняла раз в 10 больше времени разработки),я забил.
Переменные не выношу, все должно быть к месту...
Совершая нападки на аксессоры, ты плюешь против ветра. Много ли серьезных книжек ты прочел, и, что самое главное, много ли серьезных проектов поднял и сопровождал ?..
Да ваще какая разница какой код главное результат!
@Tena: ну не скажите... Красота кода - это очень важно!
А по поводу использования параллельных массивов - многие их используют в парсерах. Когда приходится при помощи регулярных выражений вытаскивать определенные значения.
Забота о красоте кода это правильно, в конце концов, его ведь читают чаще, чем пишут. Спорные моменты есть, но в статье о красоте, к тому же не привязанной к конкретному языку без них не бывает.
О параллельных массивах скажу, что стараюсь при наличии возможности использовать ассоциативные вместо плоских в подобных случаях. Хотя конечно это не всегда лучше, и не во всех языках есть.
Про аксессоры и мутаторы полностью согласен, свойства выглядят красивее. Причем я предпочитаю проверку данных писать именно в мутаторе (будь то метод или свойство), а не где-то вовне, мне кажется это помогает сохранить некую внутреннюю целостность объекта. Иногда можно наблюдать, как ее делают в обработчике события, но смешивание интерфейса с логикой мне решительно не нравится.
Про именованные параметры можно добавить о существующих в Python (может и где-то еще, не в курсе) значениях параметров по умолчанию, позволяющих не перегружать методы и создавать логичное дефолтное поведение.
Еще можно было бы как-нибудь написать о примерах плохого оформления, но это уже несколько другой вопрос.
Спасибо за статью
Можно поставь якаря для главного списка ( чисто для новых читателей )
Да уж...не знал,что многие эти приемы настолько смертельны для программы...огромное спасибо автору!=)
Все считаю по теме, широко написано
Статья конечно неоднозначная, по крайней мере для меня. Все языки меня мало волнуют, а вот применимо к javascript интересно. Обзор стилей написания заставил проанализировать собственную технику письма. Считаю так, если логично применять тот или иной способ, то его нужно применять. Не всегда удается продумать все наперед, для этого нужен определенный опыт проектирования систем. К примеру нет смысла делать функцию локальной если ее функционал будет использоваться в дальнейшем и это сразу закладывается на будущее, с другой стороны нет смысла плодить такие функции в массовом количестве. Мне кажется здесь важно понимать такую грань, а не делать или не делать как написано в этих "10 правилах". Однако подведу черту, статья безусловно заставляет задуматься над тем что используешь в данный момент. Побольше бы таких статей.
подскажите что делать если реально устаешь от кодинга?
Сначала критика:
1)
Вы только представьте: У нас есть функция в 300 строк кода
такую функцию стоит добавить в признаки уродского кода ;)
2)
Отсутствие else if
switch / case, и будет вам счастье.
3)
Написавшему, что else if это замена switch просьба вспомнить, что в switch есть магическая команда break; и код типа switch (a) { case 1: do1; case 2: do2;break; } очень неудобно переписывается через else if. А if (cond1 && cond2){} else if (cond3) {} никак не пишутся через switch.
switch (true) { ... } и пишется всё, что угодно.
4)
Обязательное хранение размера массива в отдельной переменной
смотря какой размер массива, правда, автор сам уже уточнил:
Разумеется, нет ничего плохого в том, чтобы временно закешировать размер массива в отдельной переменной (например, в целях производительности).
5)
Доступ к свойствам объекта черезobject.getProperty () и object.setProperty (value)
Плохие примеры... billGates.getProperty('money') - бесспорное уродство. Но вот billGates.money += 100, тоже довольно плохо, потому что при прямой записи у нас нет возможности контролировать валидность данных, и совершать связанные действия.
Вот неплохая статья по теме, если интересно - читайте:
http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html?page=1
6)
Использование рекурсии ... http://www.alik.su/articles/10-ugly-programming-techniques/recursion-to-factorials.png
Картинка немного радикальная ;) может корректнее было написать "не стоит использовать рекурсию, если вы не понимаете зачем она там".
7)
↑ Перегрузка операторов появится только в JavaScript 2.0.
Жуть. Надеюсь, ни один нормальный интерпретатор не будет ее поддерживать ... так ни для чего более умного, чем обфусцирование кода, она не нужна.
П.С.: Хорошо, что есть такие статьи - они заставляют думать :) Автору - спасибо.
Почитал, хм, любопытно, и сделал такие выводы.
В статье идёт речь о персональной(для каждого отдельно взятого) манере написания кода.
Утверждать, что писать надо так, и только так, а не иначе, ну это не серьёзно. Все мы люди, и все мы разные.
Несколько дней назад, мне попалась маленькая заметка, но уж очень яростная, её смысл можно свести к одному предложению:
"Все козлы, кто пишет не так, как я, мне не удобно читать ваш код"
И человек приводит пример, как мы должны писать, чтобы ему было хорошо. Написано, всё это было на грани фола.
А здесь нам предоставили возможность дискутировать, хотя и не вижу о чём.
На одном из форумов попался вопрос начинающего, как правильно писать код, ну ему и ответили, как тебе удобно так и пиши.
ух... спс за статью, но можно было бы разбить на несколько ибо не осилил всю
Добавлю и я свое имхо. Много с чем не согласен, но особо...
Пункт 2.
Кодил я на ассемблере. А вы?
Вы в курсе каким образом передается параметр в функцию? Через стэк. А каким образом он возвращается? Тоже через стэк.
А теперь представим что вы возвращаете через стэк объект на 5мб. А теперь это в цикле на 1000+ итераций в кадр.
О да! почему у меня все висит! А почему на эту игру не хватает 4гб оперативки? Как такое возможно?!
Категорический против 7 пункта. Был такой случай что пришлось у объектов отслеживать изменение параметра(что не было изначально запланировано в проекте), что бы запретить в некоторых случаях, а в некоторых модифицировать параметр иначе, чем запрашивается. Что бы вы делали с вашим object.property++ в данном случае?
Это называется жесткая структура кода, когда изменение модели поведения ведет за собой изменение всего кода. И его категорический не советуется применять, дабы избежать загнивания проекта
Процесс творческий, кто-то любит порядок и красоту, кто-то и в хаосе себя вполне уютно чувствует.
Беру статью на заметку
У меня друг программист, думаю ему пригодятся советы
Спасибо
5
Классная статья! Некоторые моменты приходили и мне в голову. А вам +1 подписчик:)
А Вы не заметили, что Вы своими пунктами как идуальный, фактически, описываете функциональный язык программирования?
Неплохие советы начинающим, а так очень много спорных моментов, особенно если идёт речь об оптимизации на скорость работы.
Интересный пост - лично я в своих проектах тоже как-то по началу думал так же ,но потом как-то разленился, так что грустно как-то...Спасибо за единомыслие!
Стремление к красоте кода - очень-очень похвальное поведение, но уверен, что правила должны быть у каждого свои. Могу привести контрпример ко второму пункту, где при описанном подходе не только потеряется эффективность, но и красота:
Предположим, что у нас есть большой объект и набор функций, вносящих в этот объект изменения - например, матрица, которая пересчитывается. Каждая функция встречается больше одного раза - поэтому их нельзя объединять. Каждая функция может определить, что объект плохой и она не может продолжить его изменение. Технически это очень напоминает конвейер. Все функции в примере возвращают булево значение - OK или FAIL, на его основании принимается решение стоит ли дальше продолжать обработку объекта.
var object;
if (function_1(&object) &&
function_2(&object) &&
function_3(&object) &&
function_4(&object) &&
function_5(&object)
) {
...
}
Читать было интересно, но есть очень спорные моменты, где каждому своё.
1-й пункт высосан из пальца.
Вопрос возникает, что делать, если разрабатываешь на чистом Си? Как эта теория сработает? Заявления о том, что язык Си фигня и прочее - как-то смешно будет выглядеть.
Да и более верным подходом будет разбить большую мотню кода на более мелкие процедуры
Как раз изучая программирование возьму на заметку
Я правда только начинаю ПХП постигать, такое громадное количество букав не реально за раз переварить,будем возвращаться
По поводу объявления переменных могу сказать то, что кому-то удобно объявить их в начале программы и он это делает на протяжении 10 лет и ему это удобно. Может для Вас после этого переписать этот код будет невозможным заданием, а ему удобно. А в остальном согласен.
Товарищи!
Хороший код - это понятный код. А понятным его делает добротная документация, со схемами, и большое количество комментариев в нужных местах. Ибо программисты, которые будут разбираться в коде после Вас, могут иметь иные представления о красоте кода.
Очень полезные советы, раньше некоторые элементы делала не так, теперь благодаря вашему доступному объяснению, буду делать более удобно. Кстати блоки "В двух словах" очень наглядно демонстрируют основную мысль
Мне очень понравилась статья! Большое спасибо автору. Сделал для себя пару выводов.. Сейчас изучаю Delphi..
Вам не кажется что статья длинновата??:)) но я все таки осилил её до конца и пару полезных советов изьял для себя, спасибо
Задумался над вторым пунктом.. О некоторых пунктах вообще раньше не думал. Буду выводить свою систему правил))
Спасибо за интересную статью. Действительно, аккуратный код сейчас очень важною. Даже в егэ по информатике на паскале требует ОПТИМАЛЬНОЕ решение задачи! вот так)
Полезные и нужные советы, некоторые части ранее творил не так, теперь благодаря вашему доступному объяснению, буду делать более удобно.
Спасибо за статью, считаю она очень полезна для PHP программистов.Всё по порядку, подробно и понятно. Ваши статьи интересно читать!
Для любого программера это полезно. Прочитав, я задумался о своем стиле коддинга. Почти все уродские приемы я не использую давным давно, но есть один.
Спасибо за пост. Буду читать дальше ваш блог
ЗЫ первый мой коммент
Отсутствие комментариев в коде или комментарии не по делу.
Статья полнейшая лажа.
Такие вещи в первом семестре объясняют в уважающем себя инженерном ВУЗе.
Полезно для тех кто не обучается по специальности программиста,т.е. для тех кто до прочтения этой статьи являлся говнокодером.
Помню как нам преподавательница по программированию все вбивала, что программировать надо красиво, с понятной структурой. И действительно потом анализировать программу на ошибки становилось легче и код становился более читаемым.
Самый близкий мне по идеологии язык — Руби. В нём, например, доступ к параметрам осуществляется всегда через методы. В качестве примера, покажу класс, в котором доступ к параметру записывается в лог.
class ProtectedClient < Client # Все данные класса содержатся в переменных, начинающихся на @. # Они недоступны извне. def money @log.info 'Получен баланс клиента' @money # "return" можно опускать end def money=(val) # в названиях методов могут содержаться знаки равно, арифметических операций, а так же восклицательный и вопросительный знак @log.info 'Изменён баланс клиента' @money = val end end petr = ProtectedClient.new p petr.money # выведет количество денег Пети и запишет это в журнал petr.money = 500 # изменит количество денег Пети и запишет это в журнал # эквивалентно petr.money=(500)
Из примера, кстати видно ещё много приятностей Руби. Например, при вызове методов, скобки можно опускать; точки с запятой в конце строк не нужны. При изучении языка видишь ещё множество подобных штук, упрощающих написание кода и делающих его красивым.
Я за 5ть лет обучения научился красиво писать программы, работаю программистом уже несколько лет. А вот когда у жены не приняли задачу на паскале, сделанную мною, по причине большого количества функций(4 функции в программе), я просто офигел, пришлось переписывать все без функций, код увеличился в 2 раза. Потом задачу снова забраковали - переменные слишком длинные, используйте однобуквенные сокращения, сказал препод.
Я уже не первый год работаю разработчиком на Java. Хоть я и завел свой блог, я все еще пишу кое какие программы на Java. Язык передовой, но, конечно минусов он не лишен. Правда этой осенью уже будут выпускать 7ую Джаву. Может в новых версиях будет поддержка свойств.
Прочитал статью, вспомнил как в универе над преподами издевался используя в С++, обращение к массиву в виде номер_элемента[имя-массива], половина преподавателей просто не верило, что это работает.
Статья отличная! Спасибо ) Пошел читать МакКонела.
Хорошая развернутая статья. Некоторые подобные идеи приходили в голову. В большинстве вещей соглашусь. В первую очередь, что код должен быть грамотно и красиво написан, что б хотя бы самому не заблудится в нем :). И после написания другой программер понимал что к чему. Тем более если это большой проект в котором участвуют много народа. Об искусстве программирования можно многое что написать. Жаль не во всех языках реализуемы перечисленные методы.
Я так понял, что это самый популярный пост
Когда прочитал про функцию в 300 строк кода - обалдел.
За последние несколько лет работы, ни один/одна функция/процедура/функ.блок не превысил размерами максимум пару окон редактора.И это не потому что очень простые задачи.
PS Со всем остальным, вообщем, согласен.
Код становится в разы лучше, если исповедует какой-то единый стиль. А это уже ложится на плечи разработчика. Увы, разработчики редко берутся нести такую ношу :)
Не всегда код пишется так "как надо", чаще всего "как придется"... из-за нехватки времени.
Хорошо писать для души... программировать ради программирования, у кого есть такая возможность - счастливые люди :)
Начал читать и, ушел…Ушел куда-то. Вспомнил 80-годы прошлого столетия. Как на своем первом компьютере(самостоятельно собранным, рисовал
плату, травил и потом паял все) писал на бейсике для своих детей простенькие программки.
Теперь не те компьютеры, и не те языки программирования
Всю статью не осилил, но многие принципы понял. Это все со временем приходит, по-моему. С практикой
Иногда надо так быстро получить результат, что уже не до красоты кода. Хотя если приучмть себя к определенным правилам, то потом даже не бужешь замечать, как красивый код пишется сам собой
Я считаю что каждый разработчик должен прочитать книгу
С. Макконнелла "Совершенный код" - тогда все описанные ошибки вряд ли будут совершаться.
Не рекомендую для новичков эту статью. Сам занимаюсь программированием 12 лет, знаю более десятка языков. Сразу бросается в глаза, что автор плохо знаком с принципами ООП и современными языками. Вся статья - смесь познаний из области функционального программирования и собственного ИМХО. Соглашусь только с 6,7,8 и 10 пунктами.
Первый пункт у меня вообще вызвал логическое противоречие, поскольку в заголовке написано одно, а в теле параграфа абсолютно другое. Так писать нельзя.
Стоящая статья, буду рекомендовать начинающим программерам. А еще, почему больше не пишите? Что случилось?
Сам я еще не суперпрофессиональный программист, поэтому на счет ВСЕХ десяти описанных случаем ничего не скажу.
Но некоторые из них действительно правильны. Только не думаю, что новичками в программировании это будет полезно, так как им (новичкам) можно голову запутать этой статьей.
Все верно, только это минимальные на мой взгляд требования. Кстати, многие языку страдают от неверного использования глобального адресного пространства (static), но к счастью в javascript нет такого слова.
Серьезная статья. Ненавижу копаться в чужом коде, особенно когда он сделан похабно. Почерпнул у вас интересные моменты. Кто в программировании знает толк - тот оценит вашу статью!
Статья блеск
Читал около часа, но ни секундой не жалею.
Узнал много нового.
Сохранил некоторые выписки из кодов, пригодятся в ближайшем будущем.
Так же добавил сайт в закладки нашёл тут много полезного.
Так же очень понравилась эта статья.
Хорошая развернутая статья. Некоторые подобные идеи приходили в голову. В большинстве вещей соглашусь. В первую очередь, что код должен быть грамотно и красиво написан, что б хотя бы самому не заблудится в нем :). И после написания другой программер понимал что к чему. Тем более если это большой проект в котором участвуют много народа. Об искусстве программирования можно многое что написать. Жаль не во всех языках реализуемы перечисленные методы.
Я только начала изучать программирование. Первые шаги в С++. Было приятно, что все написано доступным языком; что в голове появилась некая структура по теме и уложилась на полочки. Спасибо-)
Да, очень полезная инфа, мне как начинающему программисту)
Чечстно говоря ствтья получилось слишком длинной, стоило её разбить на несколько частей, тогда прще было читать.
С большей частью согласен, но с некторыми вещами нет. Критиковать легче поэтому, на ней и остановлюсь:
Имхо, переменные функции можно объявлять и в начале, если размер функции не очень велик. Это упрощает просмотр кода и ускоряет его разработку
Да, читать конечно много, но интересно, просмотрел свои программы, нашел пару ошибок, спасибо.
Вы хороший программист, понятно по статье. Но не могли бы вы сделать ваши статьи более читабельными. Изображения разделяют текст, а это не есть хорошо. Лучше поставьте обтекание изображения текстом.
Объявление всех переменных в начале программы - нас в университете учили по другому)
конечно же я все не прочитал, так как не все понятно. я начинающий программер))) а так гуд
как говорится - Coding is poetry
в Internet Explorer6 JavaScript 2.0. работает нормально, но появляется 9 и все портится.
читал на хабре, занимательно
отличный материал)
Ох уж эти любители плюсов. Объявление переменных в начале функции было стандартом старого доброго С. Это потом в плюсах стали пихать их где попало.
Статья - просто золото! Всё так понятно, даже мне, начинающему вебмастеру!
Когда я развивал свой первый проект(http://fanyou.ucoz.com/publ/5-1-0-35)
Полезный материал, особенно для тех, кто только учится программировать. Лучше с самого начала придерживаться определенных правил написания кода. Есть конечно спорные моменты, но тут каждый для себя сам определяет.
Замечательный пост. Сижу сейчас и кропотливо анализирую все написанное. Спасибо, одним словом.
Cпасибо полезная статья для новичков. Много нового для себя открыл.
спасибо за подробное описание. Вы оказали большую помощь новичкам. Конечно просто прочитать мало, я занесла данную страницу в закладки, буду усердно изучать. Спасибо.
благодарность за пост.
я пока еще в это только вникаю...Надеюсь разберусь.
На первых порах, когда создавал блог про здоровое питание
тоже было тяжеловато.
Спасибо за статью. Иногда задумывался сам об этом, но как то не проектировал...
Когда я развивал свой первый проект http://zaknews.at.ua
Хоть я пока что только начинающий программер, осилил все) Только понял немного не все.
Спасибо за разжевывание и наглядные картинки, очень полезный материал)
Статья полезная мне очень помогла, спасибо.
Пост интересный, но к сожилению я ничего пока об етом не понимаю, так как совсем недавно начал учить программирование)
Тоже самое, смотрю на это как баран на новые ворота.
Сам использую параллельные массивы, знаю что это не правильно, но так мне удобней и быстрее. Прочитал статью - задумался. Постараюсь переучится.
nytujtyj
Прикольно, у меня оказывается множество ошибок =) будем исправляться, а статью в Evernote
статья прикольная она мне помогла
Определенно самый лучший пост! Спасибо большое! Вот недавно начала развивать проект http://news-plus.ru/ вроде не плохо получается =)
Спасибо, за очень такой пышный текст ;) Интересно было почитать, все подробно расписано. Каждый блогер бы так писал, не то что всякие юкозевцы...
полезная статья
здравствуйте, спасибо конечно за статью, но если честно без обид, я запутался в вашей статье, не мне учить наверно, но оформить немножко по читабельней не помешало бы.
Думаю, для каждого пункта в каком-нибудь языке и в каком-нибудь случае обязательно найдётся исключение, или, того круче, опровержение.
Поэтому, я считаю, красота кода не в этих десяти пунктах :)
Вообще програмирование - исскуство, которое не всем дано. Статья конечно же запала в душу....
Не ну эта статья просто ваще супер. Да какая подробная всё разжованно класс. Да ещё в таблицах=) Супер автор!
Да, для новичков статья самое то что нужно.
Было бы интересновсё прочесть, если бы не так много букв. Просто реально не осилил!
Можно как-то разбить на несколько частей?
блин я до сих пор в ужасе, как можно так много писать?
На уроках информатики такому не научат :))
Игорь, читается на одном дыхании.
Автору за наглядность и подробность +5 молодцом
программа для расчета пошлин на автомобили.
http://92.50.146.14:70/page3.thm
предлагаю интеграцию в ваш сервис
Аська 368254335
Сразу вспоминаешь университетский курс С/С++. Автор молодцом!
Бывает такое напишешь, потом пол дня разбираешь что это было.))))))))
да статья навивает ностальгию по инстовскому курсу c++,а сам материал безумно интересен!
C описанием переменных в блоке где они используются - пытался сам с собой бороться, думая что это неверно
Спасибо автору, если честно,я тоже парочку таких способов использовал. Но теперь понял, что делал неправильно.
Хоть и много букв, но прочитал до конца. Много спорных моментов, но, как говорится, на вкус и цвет товарищей нет.
Sdetkami.ru
Особенно бесит, когда программеры выпендриваются и делают рекурсию везде, где только можно. Рекурсивные функции намного тяжелей разбирать.
Сильно написал. Статья длинная и подробная. Респект.
Реально академию вспомнил. Даже приятно стало, что то, чему учили на факультете программирования не забыто, а используется и процветает до сих пор.
Только эти 10 парметров влияют на красоту кода?
Сууществуют ли еще приему, разрушающие программный код?
Спасибо за подробную статью. Узнал много нового о красоте программного кода. Только вы много пишите. Все в голове не укладывается
Только эти 10 приемов способны разрушить программный код?
Только эти 10 приемов способны разрушить программный код?
Мда, страшная статья, в хорошем смысле этого слова)
Столько букв, а вообще спасибо, постараюсь прочитать еще раз. Надеюсь больше пойму.
хороший статья, я вобще тока начал кое как пргграммированием заниматься так что ещё далёк от всего этого)))но всё равно спасибо
Мне статья понравилась, но все же трудно усваивается)
faceice.com – Все мы когда-нибудь сталкиваемся с компанией iNetGlobal. Большинство людей хвалят эту компанию, когда другие не понимают, о чем это. Сайт faceice.com ознакомит вас с компанией, расскажет все методы заработка и подробно разъяснит схему заработка в iNetGLobal. Заходите – faceice.com
Вот что значит dofollow блог с неплохими пузомерками, 153 коммента, наверно и статью все 152 человека прочитали :-)
да ты что такое говоришь.я между прочим эту статью два дня искал. очень помогла.спасибо за ко коменты парни.
Суперская статья, особенно радует объем написанный вами!
<a href="http://www.programulina.com/">Вебмастеру</a>
Многовато, но многое для себя узнал, как начинающий программист.
Процесс творческий, кто-то любит порядок и красоту, кто-то и в хаосе себя вполне уютно чувствует.
Полезная статейка, некоторые пункты учту для себя, ведь нужно учиться граммотно кодить!
Чем же вам отсутствие else if так не нравится? Зачем его совать там, где не нужно!
Я тоже некоторые пункты не понимаю! Но может опытному программисту видней!
Программирование - это целое искусство! А совершенству нет предела, поэтому можно бесконечно совершенствовать свой код!
Интересная статья, о многом задумался, может немного и сам поменяю стиль!
По-моему, главное, чтобы программ корректно работала, а красота кода - это уже на втором плане!
нас в универе учили что все переменные объявляются в начале программы ))
И зачем все переменные в начале программы, если используются они только в функциях?
Не соглашусь, объявлять переменные нужно в самом начале файла, так уже по переменным можно примерно понять е читая код что и как делает программа.
Великолепный пост, есть над чем задуматься и в чем развиваться создавая новый проект ps: не гнобите ссылки с меня в ответ
с переменными согласен целиком! гораздо удобней объявлять их в блоках где они используются!
Так сказать, "прописные истины" пишешь )))
Да.... а я вот почитал и своих ошибок такую кучу увидел... просто никогда не задумывался над этим - вызов переменной именно там, где она нужна, например... Просто не думал о том, что при выполнении кода надо возвращаться в самое начало.... Да, есть куда стремиться...
ну впринципе америку автор не открыл! ))
По-моему, главное, чтбы программа правльно работала!
@Arkadiy Но когда ты смотришь на нее, а тебе от ее не тошнит...тоже не хорошо!!!
Хотя конечно пускай хорошо работает, а потом можно красоту подогнать!!!
Существуют ли еще приемы, способные разрушить программный код?
Неслабые приемы, действительно могут взломать систему.
Очень правильные советы! Если их соблюдать, разобраться в коде будет намного проще!
впринципе Америку не открыл, но и на этом спасибо ))
Зачем его совать там, где не нужно!
в сокращённом виде из-за ограничений на размер материала.
Если бы все следовали таким правилам, разобраться в чужом, да и в своем коде было бы намного проще!
Сам принцип понял, да и хорошо и понятно описано так что думаю каждый справится
Чтоб я так код писал)
http://www.flashcollection.net
Не согласен по поводу локальных функций.
Свое не согласия вырожу только одним вопросом:
При повторном использовании локальной функции в другой функции ее что придется искать (а я понял автор очень сильно не нравиться ходить по коду) и копировать или забить на поиски и плодить код, не хорошо это...
И по свойствам, да в тех языках где поддерживается синтаксис и использование понятия свойство не нужно плодить методы, но по-моему в C\C++ без функций не обойтись, ведь данные объекта (класса) должны храниться отдельно, а любые действия над ними оформляются функциями. Один из принципов ООП (инкапсуляция).
И еще примеры с факториалом и числами Фибоначчи, правильно написано приводятся в учебниках как пример, так как легче объяснить что значит рекурсия. Рекурсия сложное понятие, и не многим она дается с первого раза, да после обучения нужно писать более организованный код, но это уже остается на совести пользователя.
Спасибо за статью, полезно прочитать.
Очень занятно
Увлекательно, столько нового
ну это впринципе элементарные правила!
В университете всегда говорили , что переменные описываются вначале , странно
Никогда не понимал программистов, применяющих объявление всех переменных в начале программы.
Похоже что у автора блога мания на огромные статьи =)
СПС
СПАСИБО
С некоторыми пунктами категорически не согласен!
Жалко что у меня нет времени учить языки программирования, пока довольствуюсь HTML и CSS.
Мега респект автору. Статья очень сильно помогла
Автору +++
Как раз хтмл изучаю. Очень полезная и простая в понимании статья
согласен с 1м пунктом! ))
Обожаю когда код аккуратный, так он естественно легче воспринимается. Вот когда у вас в комнате бардак, то трудно что-то найти, так и с кодом. Особенно когда пишет код один человек, а переделывает другой, вот тогда и нужен в коде порядок....
Действительно когда код выстроен аккуратно, как то удобнее работать!
Я вот думая лучше по всему этому программированию сделать видео урок как здесь http://1000videourokov.ru/sozdatsayt/phpmysql
ну нас этим правилам учили изначально!
Хоть, я и давно завязал с программированием (о чем сейчас жалею)- сложно снова напрячь свой мозг. Но статья в целом понравилась. А вы случаем курсы по программированию не ведете?
Про переменные тут коммент был. Считаю, что переменные надо описывать вначале. Ну и код должен быть хорошо прокомментирован - факт. А то через полгоде без поллитры мозг сломаешь.
целиком согласен с 1м пунктом!
Очень хорошая статья. Буду пробовать.
Жалко что у меня нет времени учить языки программирования, пока довольствуюсь HTML и CSS.
http://razvivashkino.ru/ - Можно заработать. Платим $. Обращаться по асе 736122
впринципе согласен со всеми пунктами!
А я согласен наоборот со всеми пунктами. В принципе ни чего противоречивого в этом нет вообще.
хорошо написал! всё чётко и понятно!
Действительно, когда код структурирован, с ним легче работать!
Объявление переменных в начале кода - очень хорошая практика, которая еще с Паскаля нравилась. Зря не используют.
Действительно, когда код структурирован, с ним легче работать!
Очень интересный и объёмный пост.
Согласен с пользователем: "Google adsense"
Мне тоже нравится, когда при первом же взгляде на код понимаешь с какими переменными в нём предстоит работать.
про переменные согласен! очень удобно!
Мега респект автору. Статья очень сильно помогла
Адский трут было все сюда выложить
1й пункт даже в учебниках по программцине пишут!
Красиво и аккуратно обычно означает много и громоздко а некоторые оптимизаторы экономят каждый байт :)
та чё громоздко? зато легко найти нужный элемент в случае возникновения ошибки
спасибо,за стптью)с переменными
хорошие советы, особенно про переменные!
Лишь бы программа работала как надо! А красота кода - второстепенное.
Google проводит реформы. Изменений подверглась главная страница сайта и форма поиска. Теперь мы не сможем открыть первую найденную страницу с помощью кнопки "мне повезет". Сама форма поиска теперь будет синего цвета. ec1ef76a19950fe486573788f8966ab5
Пока данные изменения внедряются в финском Google.
Неплохо.
Фильмы на телефон http://tobi-mobi-films.ru/
Для начала наверное хватит.
Существуют ли еще приемы, которые разрушают программный код?
Хорошие советы! Для начинающих программистов самое то!
Абсолютно согласен с 1м пунктом! Как по мне намного удобнее объявлять переменные в нужных блоках!
Внесу и я свою лепту: Dofollow блог кощея.
Блин блог засрали коментами, ну раз автор не чистит и блог уже не ведёт, то недолго ему осталось, особенно убивают коменты людей которые типа статью прочитали и умозаключения свои выдвигают, а вот Рустам - красавчик, пишет прямо - "Фильмы на телефон http:/...."
Всем привет :) Пишите ниже коменты, и получайте кучу тицов.
Хорошая статья!!
<a href="http://pro-anzhi.ru/">трансферы анжи</a>
<a href="http://pro-anzhi.ru/">фк анжи махачкала</a>
для любого квалифицированного программиста это "золотые" правила )))
засрали комменты )) в открытую линки оставляют почистите!!!
Отличная статья!!
Впринципе согласен с автором! Это неотъемлемые правила!
Очистите спамеров! на кол их
Чистота и порядок кода-действительно искусство, к сожалению многим на этот порядок в коде плевать, придерживаются принципа тяп ляп.
За статью спасибо.
прикольно...
а вот новый сервис шары ссылки
для любого квалифицированного программиста это "золотые" правила )))
нас преподы по програмщине приучали к порядку в коде!
согласен с автором! ведь так на много удобнее читать чужой код!
нас преподы по програмщине приучали к порядку в коде!
Действительно хорошие советы по кодингу!
Новости Lineage 2 на http://powerstr1ke.ru
онлайн фильмы смотреть
Отличная статейка, только большевата читать тяжеловато
Отличная статейка, только большевата читать тяжеловато
В большинстве описанных случаев полностью согласен с автором. Код должен быть понятным как до так и после его написания :) ...
Очень занимательная статься. Спасибо, с удовольствием прочитала:)
Вы настоящий художник! Чувствуется, что Вы любите свое дело. Интересно, даже в такой прагматичной вещи, как программирование, можно найти красоту.
Эх всегда мечтал обучиться программированию, хотя бы азам, но мозги у меня не те )
это когда ты для себя что то пишеш можно замарачиваться красотой, а пардон когда до deadline 6 часов, на улице час ночи, а от монитора уже тошнит - точно не до красоты.
toberr, абсолютно согласен с вами, да и на то, чтобы сделать код красивым, нередко уходит много времени
Алик, а вы уже начали создавать свой язык программирования?
По некоторым пунктам категорически не согласен!
Элементарные правила программирования!
absolvo
Очень занимательная статься. Спасибо, с удовольствием прочитала:)
e512d35c75c50c907d62706a6f5efd88
советы действительно полезные!
Полезная статья. Объясняются элементарные, но не всегда заметные вещи. В большинстве согласен с автором, но есть некоторые пункты, с которыми не могу согласиться.
Хранение массива в отдельной переменной все-таки полезно, поскольку наличие такой переменной стимулирует обращаться к ней и, соответственно, обращаться к фиксированному размеру массива.
Доступ через сеттеры и геттеры: очень полезная штука и она НЕ ДОЛЖНА заменяться прямым доступом к полям, поскольку тогда становиться невозможным оптимизация и проверка данных. Так-же вы не сможете, к примеру, реализовать lazy-initialization, а это очень полезная фича.
А так, статься полезная, прочитал с удовольствием.
Люблю, когда код написан красиво. И самому легче потом понимать о чем писал и другим будет удобно.
Полезно, но, как вы правильно сказали - это сугубо личное мнение автора.
Привет.
<a href="http://prof090.ru">i am</a>
всё по делу! красивый код всегда приятно читать!
Есть над чем задуматься. Вы явно не признали бы мой код красивым -)
Есть над чем задуматься. Вы явно не признали бы мой код красивым -)
Это так сказать простые правила программирования )))
Возьму себе на заметку пару советов!
На блогспоте полно ошибок в кодах, ужас просто.
Да, согласен с автором!
Код всегда должен быть чистым и понятным, без "уродских приёмов", поскольку не только тому программисту может понадобиться разобраться в коде, который его писал, но и другому, например тестеру.
Пишите чисто и грамотно! Программирование - это исскуство!
Да уж код надо вычищать до блеска так сказать )
Когда прочитал про функцию в 300 строк кода - обалдел.
За последние несколько лет работы, ни один/одна функция/процедура/функ.блок не превысил размерами максимум пару окон редактора.И это не потому что очень простые задачи.
PS Со всем остальным, вообщем, согласен.
Обалдел когда прочитал, спасибо, буду переделывать почти все сайты на самописной cms...
Однако эти блоки — полноценные элементы в иерархии программы. И они тоже имеют право на собственные «локальные» переменные! Которые объявляются в начале этого блока и используются только в его пределах.
Очень интересная статья, добавлю в закладки.
отличные советы! большое спасибо!
соглашусь, программирование - это искусство
соглашусь, программирование - это искусство
Статья написана хорошо,автор всё разложил по полочкам.Даже мне понятно,хотя ещё в программировании новичок.
Ты так здорово! Я не думаю, Ive читал ничего подобного. Так хорошо, чтобы найти кого-нибудь с некоторыми оригинальные мысли по этому вопросу. Спасибо, что начиная с этого вверх. Этот блог является то, что необходимо в Интернете, кто-то с немного оригинальности. Хорошая работа для чего-то нового в Интернете!
Ппц еле осилил, но интересно посмотреть на эти уродские приемы. Пару совпадений нашел )))
да полный ппц, просто тошнит от таких зрелищь
а мне все таки как то туговато влезает в мозг )))
Цените комфорт и истинную красоту? Желаете проводить время в стильной обстановке, элементы которой гармонично сочетаются между собой, но не имеете понятия, как можно этого достичь? Вам сможет помочь профессиональный дизайнер в Саратове в моем лице! Я помогу вам исполнить вашу заветную мечту и создать стильный и удобный интерьер!
С первым пунктом позволю не согласиться
Советы хорошие, для начинающих программистов самое оно!
Советы хорошие))) Ведь всегда удобней оптимизированный код читать)))
Гуд Пригоится спс
Все программные системы иерархичны. Программы делятся на пакеты, пакеты — на классы, классы разбиваются на отдельные функции.
Обращение к данным происходит следующим образом
Сам немного программирую. Статья шикарная, добавил в заметки, буду время от времени перечитывать :)
Возьму на заметку! Особенно 1й пункт!
всё по делу! красивый код всегда приятно читать!
мне кажется очень фундоментальный подход к коду - такой подход нужно преподавать в вузах!
вот 1,4,5,8 - полностью согласен, даже бесит
а 2 по-мне очень даже незаменимо иногда, так что никуда не деться, хоть и бесит, вот например в библиотеке Qt разработчики точно написали - используются не ссылки, а сеттеры и геттеры во всей библиотеке для улучшения читабельности кода
Сильно, сильно...
Достойно трансляции в массы
это вынос мозга для меня, набор букв и цифр=)
1й пункт желательно всегда исполнять!
Чувак ты реально крут! Нужно своих программеров поучить уму да разуму в написание красивого кода.
Спасибо за примеры как не нудно делать и как нужно.
Добавь на сайт кнопку +1 Такое нужно рекомендовать.
про переменные абсолбтно согласен!
Сочетание техники порядка и красоты является истинной составляющей не только программирования, но и бизнеса в целом.
красивый код это хорошо! это может доказать мастерство программиста!
Красивый код это конечно всегда хорошо! Но к сожалению для этого нужно тратить много времени или иметь большой опыт (что сразу получить конечно же не получится), но стремиться к этому я думаю нужно!
Не нужно иметь много опыта что бы в notepad++ поставить табуляцию =) красиво оформленый код будет понятным, не только вам но и др. разработчикам.
А у меня криворукий код получается)))
ну мне до таких познаний ещё очень далеко...
да ладно, хоршо всё объяснено, мне лично пригодится по работе.
Я только начинаю изучать языки программирования, надеюсь ваши советы будут полезны всем программистам :)
shelchckov.yaroslav@yandex.ru
классный сайт там торренты
http://2011torrents.ucoz.ru/ классный сайт там торренты
с некоторым можно поспорить
конечно можно поспорить. програмирование вещь креативная и у каждого свой подход к решению той или иной задачи
Для меня нет ничего хуже чем разбиратся в чужом коде!!!
Другое дело работать с кодом проффесионального программиста
Да уж код надо вычищать до блеска так сказать )
Убеждаюсь, что даже в программированиии есть место креативу.
Когда-то мечтал стать программистом, кое чему научившись к сожалению забросил это интересное дело. Завидую программерам, у меня усидчивости нехватает.
В XML-языках все элементы, вне зависимости от их смысла оформляются с помощью общих синтаксических конструкций.
извините за спам но не хотели бы вы посетить наш сайт allmanga.org
Реально подскажите пожалуйста - а что делать , когда просто устал уже от кода програмного????
Алик Кириллович, я настаиваю на защите Вами докторской диссертации на данную тему! Блестяще!
----------------------------------------------------------
Есть что для себя отметить.
ну впринципе ничего нового не увидел!
Не чего не понятно, но автору спасибо=)
Спс за статью, а по теме очень познавательно для новичков!)
с первым пунктом абсолютно согласен!
За статью спасибо. Интересно и качественно написано. В большинстве вещей с вами согласен.
Именованные параметры кажется есть ещё в Groovy, если не путаю ничего, ибо знакомился поверхностно и с удовольствием забыл.
Никогда кстати не задумывался насчёт ненужности рекурсии в некоторых ситуациях. Хотя вроде и не злоупотреблял.
интересно было почитать! много подчеркнул для себя!
я настаиваю на защите Вами докторской диссертации на данную тему! Блестяще!
Довольно таки граммотно написано!
Высокий информации. Я действительно удивлен этой темы. Следите за хорошую работу и должность более здесь, чтобы прочитать.
Первый пункт обязателен к исполнению!
Так сказать, азы программирования!
хорошие советы бля начинающих программистов!
очень грамматно и актуально написано!
первый пункт одязателен для исполнения!
думаю что программеры со стажем всё это и так знают!
Moncler Doudoune кт одязателен для исполнения!
если пишу для себя, то все равно, в своем коде я не путаюсь
если для кого-то другого то тут уж все как положено, чистенько с комментами и удобно
Очень полезная статья. Спасибо.
С некоторыми моментами категорически не согласна, для постороннего человека будет тяжело разобраться например когда "Объявление всех переменных не начале программы" так как учили всю жизнь на оборот. Красота должна быть во всем! =)
Это усложняет комментирование блока: в одном месте мы комментируем переменные, но не знаем, как их использовать; в другом месте мы комментируем алгоритм, но не знаем, с какими данными он работает.
print ("Добрый вечер!");
}
Восхищен таким техническим и подробным обзором
if (numHour >= 0 && numHour < 6)
{
print ("Спокойной ночи!");
нас в универе учили что все переменные объявляем в начале программы!
и нас так же учили, чтобы ничего не забыть!
Хорошие советы не только для начинающих.Но от всех этих кодов порой голова идет кругом.
Спасибо автору за интересную статью! Очень много полезного для себя подчеркнул!