суббота, 7 мая 2011 г.

Стиль Оформления Flash Кода / Coding standards (Conventions) for AS3 (actionscript3)


Хороший код — это лучшая документация. Каждый раз, когда вы захотите добавить комментарий, спросите себя: «Как я могу улучшить этот код, чтобы он не требовал комментирования?»
Стив МакКоннелл.


Думаю каждый из нас, общаясь с коллегами, часто сталкивался с «варварским» стилем кодирования оппонента. Попытки убедить в ошибочности выбранного стиля чаще переходят в вялотекущую «религиозную войну». Но кажется должен быть последний аргумент в любом подобном споре, хочется верить, что в данном случае это будет сам Adobe, а именно его Flex SDK Coding Conventions (FCS).



Дальше будут следовать выдержки из Flex SDK Coding Conventions (FCS). В моем переводе и моими комментариями. В переходах к комментариям буду использовать Hyzhak, возвращения обратно к исходному тексту будут маркироваться аббревиатурой: FCS.

К сожалению на данный момент май 2011 года FCS не дописан, однако многие основополагающие момент в нем все же есть. Поэтому недостающие части говоломки я решил восполнить своими советами =).

Рецепты Flex SDK Coding Conventions

Исходный материал Flex SDK Coding Conventions (eng)

Общие посыл

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

Имена (Naming)

Хорошие имена крайне важны для понятности кода и возможности его использования. У вас всегда должно быть достаточно времени, чтобы подумать над выбором имен, особенно если в дальнейшем эти мена пойдут в публичный API.
Имена переменных (свойств), функции (методов) и классов должны полностью соответствовать их назначению. Но при этом следует придерживаться лаконичности и простоты в названиях.

Аббревиатуры

Аббревиатуры следует избегать. Однако есть исключения:
auto, info, num, min, max, regexp, util и др.
Т.е. если сокращение все же есть, оно должно быть общепринятым.

Тип в имени

Лучшим выбором будет такое же имя переменной как и ее тип, к примеру:
var button:Button = new Button();
Использование префиксов и названии переменных описано в [Приложении 1], а описание переменных класса в [Приложении 2].
Не используйте суффиксов с стиле AS1:
window_mc, ok_btn.

Имена пакетов

Имена пакетов следует начинать с маленькой буквы. Если имя состоит из нескольких слов, то они пишутся без разделительных знаков через заглавную букву. Имя должно быть существительным либо глаголом с +ing (герундий). Можно использовать множественное число.
Более расширенные указание находятся в [Приложении 3].

Имена интерфейсов

Имена интерфейсов следует начинать с буквы I:
IList, IFocusManager, IUID.

Имена классов

Не стоит использовать множественное число в названии классов.

Имена констант

В имени констант все буквы пишутся в верхнем регистре. Если имя содержит более одно слова они разделяются '_'. Константы всегда должны быть статичные:
public static const FOO_BAR:String = "fooBar";

Имена аргументов

В качестве имени аргумента сеттера следует использовать только 'value':
public function set label(value:String):void
а не:
public function set label(lab:String):void 
public function set label(labelValue:String):void
public function set label(val:String):void
для функций подписавшихся на событие, а качестве имени аргумента рекомендуется использовать 'event'.

Использование языка (Language Usage)

API основанный на свойствах

Старайтесь использовать в своем API больше свойств чем методов. Т. к. это больше соответствует декларативном стилю программирования (MXML).

Декларация типа

Старайтесь максимально сузить тип переменной. К примеру в циклах в качестве индекса следует использовать int, а не Number.

uint

Используйте uint только не для численных переменных: цвет, битовые маски и др.. Для циклов только int.

Array

Если вы решили воспользоваться типом Array. Необходимо описать тип объектов этого массива:
var a:Array /* of String */ = [];
или:
function f(a:Array /* of Number */):Array /* of Object */
{
...
}

Импортирование (import statements)

Импортировать определенные классы, интерфейсы, функции уровня пакетов, а не группы. Правильно:
import mx.controls.Button;
import flash.utils.getTimer;
а не:
import mx.core.*;

Оператор for

Следует заранее вычислять верхнюю границу цикла:
var n:int = a.length;
for (var i:int = 0; i < n; i++) 
{     
... 
} 
а не делать это в цикле:

for (var i:int = 0; i < a.length; i++)
{
...
}

Указание доступа

Перед тем как указать public или protected для API, стоит подумать дважды, а нужно ли это. Эти функции должны быть хорошо документированы, а также поддерживаться несколько релизов API, перед тем как стать устаревшими (deprecated).

Разное

Для строковых констант следует использовать двойные кавычки
“blah”
, а не не одинарные
'blah' 

Объявления (Declarations)

Каждое объявление переменной или константы должно располагаться на отдельной строке:
var a:int = 1;
var b:int = 2;
а не следовать одна за другой на одной строке:
var a:int = 1, b:int = 2;

Переменные класса

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

Организация файла (File Organization)

Свойства

Гетер должен идти раньше сеттера.

Форматирование


Разделители (Section separators)

Основные разделители кода должны иметь следующий вид:
//--------------------------------------------------------------------------
//
//  Overridden methods
//
//--------------------------------------------------------------------------
Вспомогательные:
//----------------------------------
//  visible
//----------------------------------
Более полный список разделителей в [приложении 4].

Операторы и команды

Следует размещать пробелы между операторам присваивания, сравнения, и инфиксными операторами (+, -, * и др.):
a = a + b * c;
а не:
а=a+b*c;

Выравнивание фигурных скобок

Скобку '{', группирующую код в блоки следует располагать на отдельной строчке, над скобкой, замыкающей блок кода:
function f():void
{
    var n:int = numChildren;
    for (var i:int = 0; i < n; i++)
    {
        if (isHere(i))
        {
            x = horizontalGap * i;
            y = verticalGap * i;
        }
    }
}
а не так:
function f():void{
    var n:int = numChildren;
    for (var i:int = 0; i < n; i++){
        if (isHere(i)){
            x = horizontalGap * i;
            y = verticalGap * i;
        }
    }
}
«Религиозная война» порожденная двумя этими стилями, описана в [Приложении 5].

ASDoc

Комментирование свойств классов

Документировать только геттер.
Hyzhak : А также должны быть задокументированы все public и protected методы классов.

Загадочные участки Flex Coding Conventions

Неожиданно

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

Type

'type' имеет смысл AS3 типа, поэтому в личных целях следует использовать kind.

Присваивание массива

Следует использовать '[]', а не 'new Array()'.

Присваивание объекта

Следует использовать '{}', а не 'new Object()'.

Присваивание анонимной функции

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

Присваивание регулярных выражений

Задавать регулярное выражение в буквенном виде:
var pattern:RegExp = /\d+/g; 
а не:
var pattern:RegExp = new RegExp("\\d+", "g");

Присваивание xml

Тоже самое касается xml. Лучше:
var node:XML = <name first="Jane" last="Doe"/>;
а не:
var node:XML = new XML("<name first=\"Jane\" last=\"Doe\"/>");

Приведение типов

Предпочтительно использовать приведение типов:
IUIComponent(child).document
а не оператор as:
(child as IUIComponent).document.

Тернарный оператор

Следует использовать тернарный оператор:
return item ? item.label : null;
а не:
if (!item)
return null;
return item.label;

Константы класса

В AS3 константы не могут быть Array и Object типа (!). Поэтому следует использовать конструкцию:
static var
, а не:
static const
Однако располагайте их в секции констант, т. к. концептуально они константы.

Неожиданно. Мои расхождение с доктриной Flex Coding Conventions

Слушатели событий

FCC:
Имя функций - обработчиков событий должно завершаться 'Handler':
private function mouseDownHandler(event:Event):void;
Hyzhak:
Имя функции - обработчиков событий должно начинаться с 'on':
protected function onMouseDown(event:Event):void;
Т.к. так удобнее их находить в коде. Достаточно начать вводить on.

Проверка существования

FCC:
Рекомендуется при проверке на существование использовать такое выражение:
if (child)
if (!child)
а не:
if (child != null)
if (child == null)
Hyzhak:
Мое возражение в этом случае заключается в следующем: объект child может принимать значение false. Что в данном случае будет синонимом null, что не всегда допустимо. Да и конструкция с применением null, легче читается.

«Отказ от прошлого опыта»

Константы в условиях

Писать сравнение более натуральной форме:
if (n == 3) // "if n is 3"
а не:
if (3 == n) // "if 3 is n"
С натуральностью я соглашусь, да в принципе я так и делаю, однако 2-ой форме есть определенные оправдания: ее роль дополнительная защита от ошибочного присваивания, т. к. если мы забудем дописать дополнительный знак '='. С кем не бывает. То во 2-ом случае у нас даже не получиться скомпилировать исходник. Первый случай даст лишь warning.

Условные выражения

Если любая из ветвей последствий оператора if/else содержит единичное утверждение, не стоит обрамлять код в {}.
Правильно:
if (flag)
    doThing1();
else
    doThing2():
а не:
if (flag)
{
    doThing1();
}
else
{
    doThing2();
}
С чем я категорически не согласен. Т. к. не очень удобно расширять подобную функциональность, добавляя новые операции в тело кода. К тому же в FCS на циклические операции подобное правило не распространяется.

Приложение

Далее следуют мои уточнения к стилю оформления кода.

Приложение 1. Имена переменных


Имена переменных следует образовывать добавлением префикса (аббревиатуры, либо полного имени) указывающем на тип переменной.
Пример:
префикс тип пример
общие
i int iMax
u uint uColor
n Number nMin
str String strName
b Boolean bSuccess
специфические для
Flash
mc MovieClip mcChild
tf TextField tfCaption
o Object oValue
специфические для
фреймворков (к примеру PureMVC)
p Proxy pLevel
m Mediator mLevelContainer

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

Приложение 2. Имена переменных классов

Имена private, protected переменных классов следует начинать с «_». К примеру:
private var _iX:int;
private var _iY:int;
private var _strState:String;

Приложение 3. Имена пакетов

Код программы должен располагаться в пакете следующего вида:
<org/com>.<author/company_name>.<project_name>
к примеру:

package org.vasiliy.utils
{
}
или
package com.goodcompany.zombies
{
}
Для меня до сих пор вопрос, от куда взяли разработчики именно такое именование пакетов, однако оно мне кажется весьма логичным, остается только странным, почему сам Adobe не всегда придерживается его.

Приложение 4. Использование разделителей в коде

В классе должны быть следующие разделители:
//--------------------------------------------------------------------------
//
//  Getters&setters
//
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
//
//  Events handlers
//
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
//
//  Methods
//
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
//
//  Logs
//
//--------------------------------------------------------------------------
Последний блок используется для вывода исключений, предупреждений и другой отладочной информации (trace или throw new Error()).
По правде говоря, я раньше всегда использовал более сокращенный вариант разделителей:
//getsetters -----------------------------------------

//events ---------------------------------------------

//methods --------------------------------------------

//logs --------------------------------------------
и т. д. Не факт что впредь получится использовать FCS стиль разделителей, однако я попробую.

Приложение 5. переносить “{“ но новую строчку или нет?

Предпосылки

“{” не переноситься — стиль K&R (Кернеген и Ричи) или 1TBS (One True Brace Style).
“{” переноситься — стиль Олмана.

Доводы обеих сторон

не переносить:
  • больше кода можно увидеть на одном экране;
  • экологичный - экономия бумаги =);
переносить:
  • код выразительнее, т. к. блоки условий и тела кода, визуально разделены строчкой с “{”;
  • легко закомментировать управляющее операторы (if/for/while) подставить любой другое;
Полное описание противостояния можно найти в Википедии: http://en.wikipedia.org/wiki/Indent_style.
Замечу лишь, что Adobe в Flash Builder-е решил дилемму за нас, перенося '{' на новую строчку автоматически, я так и не нашел как бы это можно было отключить.

Приложение 6. Комментарии

TODO

Если есть необходимость указать участок кода, где нужно дописать функциональность следует воспользоваться комментарием вида:
//TODO : blah-blah-blah

FIXME

Если возникло непреодолимое желание «побыдлокодить». Либо набросать функциональность, которая однозначно нуждается в переписывании, следует воспользоваться комментарием вида:
//FIXME : blah-blah-blah

Приложение 7. Позаимствовано у Александр а “Santer” Титова

Одно время попался на руки документ от Александра Титова "Стандарты кодирования и рекомендации. AS3", большинство правил покрываются уже написанными стандартами, однако есть парочка не вошедших в обзор:

Инкапсуляция. Getter/setter

Все свойства класса должны быть максимально закрытыми. Внешний доступ к ним только через getter-ы и setter-ы. Исключение составляют некоторые паттерны проектирования.

Префиксы is-, contains-, has-

Логические переменные и методы снабжаются is-, contains-, has-. Очень удобно, но не обязательно.

Заглючение

На этом пожалуй все. Будет интересно узнать в комментариях ваше мнение о стандартах. Какие применяете вы и почему. Возможно кому-нибудь известна предыстория применяемых в Flex SDK Coding Conventions решений, поделитесь пожалуйста в комментариях. Буду крайне признателен. Текст получился длинным, спасибо что дочитали до конца =)!

Update

Решил добавить небольшой опрос. Цель его определить каких стандартов кодирования мы придерживаемся. Заходите, голосуйте:
http://www.surveygizmo.com/s3/535904/as3-coding-conventions

Links

Комментариев нет:

Отправить комментарий

Press Any Key...