Przejdź do treści

Zasady nazewnictwa obiektów Neosa i stylu C#

Nazewnictwo elementów definiowanych w Neos Expercie

Zasady ogólne

Wszelkie nazwy piszemy wielkimi literami z dodatkiem cyfr i znaku podkreślenia, bez spacji ani polskich znaków. Chodzi o to aby nazwy wszystkich elementów były poprawnymi identyfikatorami klas lub zmiennych w języku C#.

Nazwy projektów

Nazwa projektu standardowego powinna być skonsultowana z głównym architektem i pasować do z góry ustalonej listy projektów wchodzących w skład systemu Teneum. Nazwy projektów indywidualnych tworzonych dla konkretnego klienta na etapie wdrożenia powinny zawierać skróconą nazwę tego klienta.

Nazwy obiektów biznesowych

W przypadku obiektów opartych o tabele nazwa obiektu powinna być zawsze identyczna jak nazwa tabeli. Jeśli tabele posiadają prefiks, np MDM_INVENTORIES to nazwa obiektu opartego o tą tabelę także powinna brzmieć MDM_INVENTORIES Nazwy obiektów opartych o zapytania SQL lub o programowe źródło danych nie powinny mieć prefiksu bazodanowego, ale powinno to być jedno lub dwa słowa najlepiej charakteryzujące przeznaczenie danego obiektu.

Obiekty do konkretnych integracji, które czerpią dane z XXXX a zapisują w YYYY powinny być nazywane XXXX2YYYY, np DOKUMNAG2BKDOCS, EMAILWIAD2COSTINVOICE lub analogicznie. Dzięki temu jest jasne, jaką integrację zawiera dany obiekt.

Jeśli opierasz obiekt na zapytaniu SQL, zastanów się, czy w twoim zapytaniu, któraś tabela jest wiodąca. Wiodąca tabela to ta, której pola najczęściej pojawiają się w wyniku zapytania, a klucz główny tej tabeli stanowi także klucz główny wyników zapytania. Jeśli np. budujesz zapytanie zwracające statystyki klientów, czyli dla każdego klienta chcesz mieć informację ile mu wystawiłeś faktur, przyjąłeś zamówień, itp., tabelą wiodącą będzie tabela KLIENCI. W takim przypadku utwórz obiekt biznesowy o symbolu STAT4KLIENCI.

Foldery w projekcie

Obiekty biznesowe gromadzimy w folderach. W jednym folderze grupujemy obiekty składające się na jedną funkcjonalność. Np wszelkie obiekty biznesowe związane z nową kartoteką towarową w Teneum gromadzimy w folderze inventory. Nazwy folderów piszemy małymi literami bez żadnych prefiksów bazodanowych. Jeśli obszar funkcjonalny jest bardzo rozbudowany, to można obszar dzielić na mniejsze i definiować podfoldery.

Nazwy pól modelu danych

Nazwy pól modelu danych powinny być zgodne z fizyczną nazwą pola w tabeli lub pola zwracanego przez zapytanie SQL.

Nazwy parametrów

Parametry w obiekcie biznesowym powinny być nazywane tak, jak prywatne pola w klasach C#, czyli od znaku podkreślenia i małej listery, np _parameter, _searchText.

Symbole akcji

Stosujemy zasady analogiczne jak do nazywania metod wykonawczych akcji. Czyli akcja posiadająca metodę ShowInvoice powinna mieć symbol ShowInvoice.

Nazwy metod

W metodach wygenerowanych przez Neosa zostawiamy takie nazewnictwo jakie Neos wygenerował. Jedynie uzupełniamy miejsca wykropkowane.

Stosujemy podobne zasady jak w przypadku nazewnictwa akcji. Czyli każde słowo z wielkiej listery, bez podkreśleń i spacji. Zwykle nazwa metody powinna się składać z jednego, dwóch lub trzech słów, gdzie pierwsze słowo jest czasownikiem mówiącym, co robi dana metoda. Przykłady:

  • CreateFolder - metoda służy do zakładania jakiegoś folderu
  • UpdateCustomer - metoda aktualizuje dane klienta
  • GetOrderList - metoda zwraca listę zamówień

Najczęściej używane czasowniki do nazw metod logiki biznesowej, to:

  • Create - do tworzenia nowych elementów, a nie New, Insert, Add
  • Update - do aktualizacji istniejących a nie Modify, Edit
  • Delete - do usuwania elementów, a nie Remove ani Erase
  • Get - do zwracania elementów lub list

Najczęściej używane czasowniki do nazw metod logiki interfejsu, to:

  • Show - ogólny czasownik metod służącycj do pokazywania okien
  • New - do inicjacji okien edycji, w których dodajemy nowe rekordy
  • Edit - do inicjacji okien edycji, w których edytujemy dane

Symbole form

Najczęściej używane symbole form:

  • NEW - okno do dodawania nowych rekordów
  • EDIT - okno do edycji danych
  • BROWSE - okno do przeglądania danych
  • DICT - okno słownika

Zalecane dodatkowe symbole form:

  • BROWSE4CUSTOMER - forma przeglądania ale dedykowana do przeglądania danych zafiltrowanych do wybranego klienta
  • INNEREDIT - forma przeznaczona do edycji ale z przeznaczeniem do zagnieżdżenia na innej formie
  • DELIVERYINFO,STATUSINFO, FINANCEINFO - fragmentaryczne formy edycyjne z wybranymi grupami pól. Zwykle takie formy są zagnieżdżone na głównej formie EDIT.

Nazewnictwo w C#

Nazewnictwo w kodzie

Notację PascalCase stosujemy do:

  • klasy
  • struktury
  • metody
  • enumy
  • pola publiczne, protected, internal
  • property
  • namespacey

Notację camelCase stosujemy do:

  • zmienne lokalne
  • parametry

Notację _camelCase stosujemy do:

  • pola prywatne

Kapitalizowane powinny być pierwsze litery słów (włącznie z akronimami)

{
  object localHttpServer; // dobrze
  object localHTTPServer; // źle
}

Nazwy interfejsów poprzedzamy literką I.

Modyfikatory dostępu (static, const, readonly) nie mają wpływu na konwencję.

private int _instanceField = 0;
private static readonly int _staticReadOnlyField = 0;
private const int _constField = 0;

public int InstanceField = 0;
public static readonly int StaticReadOnlyField = 0;
public const int ConstField = 0;

Nazewnictwo w plikach

  • Nazwy plików i folderów - PascalCase
  • Gdzie to możliwe nazwa pliku powinna być taka sama jak nazwa zawartej w niej klasy
  • Stosujemy zasadę jedna klasa - jeden plik

Organizacja kodu

Modyfikatory nadajemy w następującej kolejności:

  • public
  • protected
  • internal
  • private
  • new
  • abstract
  • virtual
  • override
  • sealed
  • static
  • readonly
  • extern
  • unsafe
  • volatile
  • async
protected internal virtual async Task Operation()
{
  //...
}

Struktura klas

  • Grupy elementów
    1. Delegaty
    2. Eventy
    3. Pola const, static, readonly
    4. Pola
    5. Property
    6. Konstruktory/finalizery
    7. Metody
  • Kolejność wewnątrz grup
    1. public
    2. internal
    3. protected internal
    4. protected
    5. private
//Neos.Core.Data.Providers
public abstract class Database : IDatabase
{
  //delegaty
  public delegate Database Factory(DatabaseSettings settings);

  //eventy
  //pola const, static, readonly
  protected const string CreatingDatabase = "...";
  protected const string CreatingDatabaseWithSettings = "...";
  protected const string CreatingConnectionString = "...";

  private readonly ConnectionFactory _connectionFactory;

  //pola
  //property
  public string Name => Settings.DatabaseAlias;

  internal abstract QueryBuilder QueryBuilder { get; }

  protected ILogger { get; }

  //konstruktory/finalizery
  public Database(DatabaseSettings settings, ConnectionFactory connectionFactory, ILogger logger)
  {
    //...
  }

  //metody
  public int RunProcedure(string procedureName, IEnumerable<Parameter> parameters)
  {
    //...
  }

  public int RunSQL(string sql)
  {
    //...
  }

  protected abstract string CreateConnectionString();

  private int InternalRunProcedure(string procedureName, IEnumerable<Parameter> parameters, IDbCommand command)
  {
    //...
  }

  private int InternalRunSQL(string sql, IDbCommand command)
  {
    //...
  }
}

Zasady białych znaków

  • Jedna deklaracja per linia
  • Jedno przypisanie per linia
  • Wcięcia: 2 spacje, bez tabulatorów
  • Maksymalnie 120 znaków w linii
  • Styl Allmana dla klamr (klamry w osobnych liniach)
  • Klamry używane nawet kiedy są opcjonalne
// dobrze
if(<warunek>)
{
 do();
}

//źle
if(<warunek>)
  do();

Zasady kodu C#

Stałe

  • Zmienne i pola powinny być const jeśli to tylko możliwe
  • Jeśli nie można użyć const (np. przy typach referencyjnych) to można wykorzystać readonly (np. referencje na usługi pochodzące z dependency injection)
  • Używamy nazwanych stałych zamiast magick-numberów
if(value < 13) //źle

public const int BlockSize = 13;
...
if(value < BlockSize) //dobrze

Kolekcje:

  • Dla typów parametrów we/wy metod preferujemy najbardziej restrykcyjny typ kolekcji (IReadOnlyCollection / IReadOnlyList / IEnumerable/ itp). Zawsze preferujemy wykorzystanie interfejsu.
  • Wyjątkiem jest przekazanie własności utworzonego obiektu. Wtedy można użyć interfejsów read-write, np. IList

Property:

  • Dla jednolinijkowych propert readonly preferujemy wykorzystanie =>
  • Dla wszystkich innych przypadków { get; set; }

Klasy i struktury:

  • Struktury są przekazywane jako wartość!!!
  • Zawsze preferujemy używanie klas

Lambdy i metody:

  • Preferujemy użycie metod jeśli lambda przestaje być trywialna

Rozszerzenia:

  • Tworzymy metody rozszerzające tylko w przypadku, kiedy nie możemy zmodyfikować klasy rozszerzanej lub dodawana funkcjonalność jest specyficzna dla przypadku użycia

LINQ:

  • Używamy notacji Fluent zamiast SQL-like. Przykład
  • Unikamy używania Collection.ForEach

Krotki:

  • Preferujemy utworzenie klasy dla typów zwracanych zamiast użycia Tuple\<>

Delegaty:

  • Zawsze wywołujemy delegaty przez Invoke() i użycie operatora warunkowego null, np: Delegate?.Invoke()

Atrybuty:

  • Atrybuty powinny być umieszczane w linijce poprzedzającej dekorowany element
  • Przy wielu atrybutach, każdy powinien być umieszczone w osobnej linii

var:

  • Zawsze używamy var gdzie to możliwe i można wywnioskować typ z wyrażenia