niedziela, 11 grudnia 2011

Konwencje nazewnicze

1. Wprowadzenie

Ten krótki dokument wyodrębniony został z opracowania na temat jakości kodu. Opublikowany zostanie osobno ze względu na spójność zagadnienia i konieczność zebrania i wyróżnienia reguł nazewniczych dla kodu.

Dokument przedstawia zasady opisane w (1), przykłady kodu oraz konfigurację narzędzi do wspomagania stosowania konwencji nazewniczych.

Konwencje nazewnicze służą zachowaniu spójności i czytelności tworzonego kodu. Stosowania ich ułatwia przegląd kodu innego programisty, pozwala na szybsze określenie jego funkcji i odnalezienie potencjalnych błędów. Ustandaryzowane nazwy bardzo ułatwiają też nawigację po dużym projekcie.

2. Opis reguł i przykłady

Obowiązującą dla projektu konwencję można znaleźć w całości na stronie (1). Przedstawione zostaną najbardziej istotne zasady dotyczące struktury kodu, reguł językowych i gramatycznych stosowanych przy tworzeniu nazw.

a) Wielkość liter

Dwie stosowane reguły to "camel case" i "Pascal convention".

- "camel case" - rozpoczęcie małą literą, każde następne słowo wielką, np. private TestContext testContextInstance;

Jest stosowana do identyfikatorów (nazw zmiennych) i parametrów funkcji. UWAGA! Dla pól prywatnych często stosowana jest konwencja z podkreśleniem (private TestContext _testContextInstance;). NIE JEST ona zgoda z oficjalnymi zasadami zwartymi w MSDN. Spotkałem się z wyjaśnieniem, że zaniknęła ona ze względu na możliwość automatycznego definiowania properties (np. public string Name { get; set; } ). Sądzę, że należy konsekwentnie stosować konwencję z MSDN i podkreśleń unikać.

- "Pascal convention" - każde słowo rozpoczyna się wielką literą, np. public String SomeLongName { get; set; }. Zasada stosowana dla klas, typów wyliczeniowych, wartości typów wyliczeniowych, zdarzeń, klas wyjątków, pól statycznych tylko do odczytu, interfejsów, metod, Namespace, Properties

- akronimy - jeśli akronim zawiera więcej niż 2 litery, stosujemy Pascal convention, w przeciwnym wypadku skrót piszemy wielkimi literami, przykłady:
HttpWebService (parametr nazwiemy httpWebService),
XmlWriter (xmlWriter),
DBContainer (dbContainer),
IOStream (ioStream)

- UWAGA! Nie stosujemy "Pascal convention" dla nazw, które językowo składają się z dwóch członów, ale stanowią jedno słowo, np. hastable zamiast hashTable, Runtime zamiast RunTime. Na szczęście kod nie powstaje w języku niemiecki, gdzie ten problem staje się dość paskudną zagadką logiczną.

Poniżej fragment kodu zawierający przykłady poprawnego zastosowania konwencji nazewniczych.


namespace OneCardMaster // Pascal
{
public class OneCardMasterGame : IOneCardMasterGame /*interface name always begin with I*/
{

private readonly List playerList = new List();
private readonly IDeck deck;
private int playerLimit; // camel (not _playerLimit !)

public event EventHandler GameStateChanged; // Pascal

public CardValue CardValue; // Pascal

public static readonly int Number = 10; // Pascal

public string SomePropertyName { get; set; } // Pascal

public OneCardMasterGame()
{
deck = new Deck();
playerLimit = 10;
CardValue = CardValue.Ace;
}

public void AddPlayer(IPlayer playerParameter)// parameter name - camel, method name - Pascal
{
if (playerList.Count < playerLimit)
{
playerList.Add(playerParameter);
}
}


b) Ogólne zasady nazewnicze

Przy tworzeniu nazw zawsze przedkładamy czytelność ponad zwięzłość. Nazwa CanScrollHorizontally jest lepsza niż ScrollableX .

W kodzie używamy słów języka angielskiego. Należy upewnić się co do pisowni takich słów. Nietrudno sobie wyobrazić problemy z wyszukiwaniem metod, które zawierają "literówki".

Należy unikać "magicznych trzyliterowych skrótów" i innych akronimów, o których nie jesteśmy pewni że zostaną dobrze zrozumiane.

Nie należy używać podkreśleń, apostrofów, ani żadnych innych niestandardowych znaków.

c) Biblioteki, namespace

Schemat nazywania bibliotek to <Company>.<Component>.dll - w naszym przypadku proponuję jako "Company" używać "PremiumHands".

Namespace tworzy się wg następującego schematu: <Company>.(<Product>|<Technology>)[.<Feature>][.<Subnamespace>], czyli np. Microsoft.WindowsMobile.DirectX lub PremiumHands.OneCardMaster.Administration.

d) Klasy, struktury, interfejsy

Nazwami klas powinny być rzeczowniki w liczbie pojedynczej. W przypadku dziedziczenia warto rozważyć zawarcie w nazwie klasy, nazwy klasy bazowej, np. CardException. Nie jest to jednak niepodważalna zasada. Łatwo sobie wyobrazić strukturę dziedziczenia uniemożliwiającą stosowanie takiej konwencji.

Interfejsy nazywają się podobnie jak klasy, ich nazwy rozpoczynają się od "I", np. IOneCardMasterGame.

Nazwa typu wyliczeniowego powinna być rzeczownikiem w liczbie mnogiej (np. CardColors). Poszczególne wartości to rzeczowniki w liczbie pojedynczej (np. Heart, Spade, Club, Diamond).

e) Pola, metody, właściwości (properties), zdarzenia

Główne informacje zawarte zostały w punkcie a).

Metody to najczęściej krótkie zdania, w których czasownik występuje w formie bezokolicznika, np. GetEmployeeRecord.

Podobnie wygląda nazewnictwo zdarzeń. W tym wypadku należy zwrócić uwagę na czas gramatyczny. Przeszły określa, że zdarzenie jest wywoływane w następstwie pewnej sytuacji, np. FormClosed. Zdarzenie FormClosing oznacza, że sytuacja powodująca wykonanie zdarzenia nie jest dokonana i może np. zostać przerwana. Własne typy zdarzeń powinny swoją nazwę kończyć na EventHandler, np. WindowEventHandler. Argumenty dla event handler to zawsze Object sender i e typu dziedziczącego po EventArgs.

g) Nazewnictwo testów

Dla nazewnictwa metod testowych, podobnie jak w przypadku zwykłych metod, używamy stylu "Pascalowego". Na końcu metody dodajemy przyrostek "Test". Nazwa powinna opisywać rodzaj wykonywanego testu, np. AgeCalculationWithNoBirthDateTest().

3. Konfiguracja ReSharpera

ReSharper pozwala na wyróżnianie problemów z konwencjami nazewniczymi. Przykładowy błąd wyróżniany jest w sposób przedstawiony na ilustracji:



Należy zwrócić uwagę, że wskazywany błąd, tak naprawdę nie jest w tym wypadku sytuacją sprzeczną ze stosowanymi w projekcie konwencjami nazewniczymi. Konieczna jest zatem zmiana w konfiguracji ReSharpera. Wybieramy menu [ReSharper] -> [Options], następnie w opcjach "Languages" -> "C#" znajdujemy "C# Naming Style". Okno dialogowe pozwalające na dokonywanie zmian wygląda następująco:



Resharper pozwala również na definiowanie własnych, bardziej skomplikowanych zasad określanych na podstawie poziomu dostępu, rodzaju kodu. Możliwe jest też wyłączenie asystenta.

4. Odnośniki

(1) MSDN Libary, "Guidelines for Names", http://msdn.microsoft.com/en-us/library/ms229002.aspx

(2) ReSharper 6, "JetBrains ReSharper Revievier's Guide", http://www.jetbrains.com/resharper/documentation/reviewers_guide.html

(3) Scott Belware, "C# Code Style Guide", http://www.sourceformat.com/pdf/cs-coding-standard-bellware.pdf

0 komentarze:

Prześlij komentarz

 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | Online Project management