Tworzenie funkcji

Co chwilę, jako programiści, tworzymy sobie w naszych programach nowe funkcje. I jakiekolwiek każda z nich ma znaczenie, często mamy dylemat jak od początku do końca dobrze je zapisać. Powstaje w związku z ich deklaracją kilka dylematów.

W całym zagadnieniu wyszło mi, że owe podproblemy są posortowane od najtrudniejszego/najbardziej spornego do najbardziej fundamentalnych.

Spis treści:
1. Kolejność argumentów
2. „Piętnaście” argumentów
3. Nazewnictwo
3.1. Nazewnictwo od bytu obiektowości
3.2. Nazewnictwo właściwymi słowami kluczowymi
4. Sens tworzenia funkcji

1. Kolejność argumentów

W jakiej kolejności wpisać jej przyjmowane argumenty?

Myślę, że najlepiej to posortować według ważności (priorytetu) argumentów. Co to znaczy? Mamy dla przykładu dwa argumenty – nazwa pliku jakiejś bazy oraz np. nazwa kolumny sortującej. Jeżeli ten drugi argument to „id”, wtedy nie ma potrzeby sortowania tej tablicy z bazy i wystarczy z niej zwrócić to wszystko tak jak jest (z założeniem, że kolejne nowe elementy mają id = ++m_dLastId;). W takim wypadku, ten argument zdaje się być nieprzydatny, ale tylko w tym wypadku. Z tego powodu jego powinniśmy wywindować na koniec – read_base( string filename, string sort_col ).

2. „Piętnaście” argumentów

Inny problem to kiedy mamy funkcje w stylu CreateWindow z WinAPI. Piętnaście przyjmowanych argumentów i używanie tego aż prosi się o problemy, szczególnie, kiedy jeszcze mamy kilka wersji tej funkcji (a’la polimorfizm). Powstaje pytanie – stworzyć jakąś specjalną strukturę czy nie? Lekarstwem na ten stres nie może być jeden właściwy przepis. Podawane argumenty (tudzież zależności i wynikłości) to:
- czy później ta struktura będzie jeszcze gdzieś przekazywana przez jakieś tam API, którego używamy (ew. tworzymy)
- czy więcej funkcji niż ta jedna będzie używać tej struktury
- jakby nie było zapis w używaniu funkcji będzie krótszy, ale jeżeli jednorazowo to jednak nie (trzeba tworzyć obiekt struktury i wypełniać po kolei jego pola zmiennymi, które od razu moglibyśmy podać do funkcji)
- posiadanie struktury jest lepsze niż kilka wersji danej funkcji, bo do struktury można wpisać odpowiedni konstruktor czyszczący (w C++ struktura już chyba niczym się nie różni od klasy). Z kolei w jednej funkcji przyjmującej dane argumenty będą if’y, które składowe struktury są równe NULL
- czy nasza biblioteka w ogóle ma planowane rozszerzenia. Jeżeli z góry wiadomo, że nie, to decyzja moim zdaniem może być losowa :)

3. Nazewnictwo

W zasadzie rzecz podstawowa, a jednak chcę o tym napisać, może ktoś zagubiony z tego skorzysta :) Otóż, omówię dwa standardowe – tudzież najczęściej spotykane (być może delikatnie przeze mnie zmodyfikowane) – style: C i C++.

3.1. Nazewnictwo od bytu obiektowości

C, czyli programowanie strukturalne. Brak jakiejkolwiek obiektówki, wszystko (tj. funkcje) jest globalne.

Tutaj najczęściej spotyka się nazewnictwo pokroju <typ> create_window( <typ> arg1, <typ> arg2 ). Już wyjaśniam – całe nazwy małymi literami, a między nimi znaki podkreślenia. Nazwy argumentów również małymi literami, nie używamy tam notacji węgierskiej. Tejże używamy dopiero w definicji danej funkcji. Notacja węgierska to nazewnictwo zmiennych zależnie od ich typów tak, że nazwę zmiennej zaczynającą się dużą literą poprzedzamy krótkim stringiem (najczęściej jedną litera) informującym o typie zmiennej. Na przykład:

char cSign = 32; /* spacja */
unsigned int udNumElements; /* taka zmienna jest zdecydowanie lepsza od np. udElementsNumber. Ja w tym wypadku stosuję dNumElements, nie potrzebuję oznaczenia unsigned. A skąd literka "d"? Nie od "int", tylko od "digit" - po prostu liczba. */

Note: zauważ, że komentarz wyżej jest w stylu C. Dopiero w C++ doszedł drugi – „//”
Oczywiście nie jest to ścisłe, ale najbardziej szanowane (;) przykłady można zobaczyć tu.

C++, tj. programowanie strukturalne i obiektowe w jednym, po prostu mamy wybór, również hybrydę :)
Są dwa typy funkcji: globalne i te znajdujące się w klasach, czyli tzw. metody.

I tutaj nazewnictwo bywa ciekawsze. Zależy czy naszą bibliotekę kodujemy w guście strukturalnym. Jeżeli nie, to dopiero korzystamy w pełni (poniekąd oczywiście) z C++. Wtedy nasze funkcje globalne mogą przyjąć postać:

 createWindow(  arg1,  arg2 );

Dlaczego tutaj bez znaków podkreślenia? Po pierwszej, aby było różniej od C, a po drugie – (u mnie) ma to związek z nazewnictwem metod:

 OurAPI::createWindow(  arg1,  arg2 ); //metoda uzywana przez usera
 OurAPI::CreateWindow(  arg1, ; arg2 ); //metoda uzywana tylko dla klasy

Druga metoda jest prywatna, a więc użytkownik nie ma do niej dostępu. Dlatego metody prywatne/chronione rozróżniam od publicznych pierwszą literą – duża lub mała.

3.2. Nazewnictwo właściwymi słowami kluczowymi

Poza tym wszystkim, oczywista zasada – nazewnictwo powinno być intuicyjne, krótkie, treściwe. Funkcje powinny być rozróżniane łatwo i szybko, kiedy nasze IDE listuje nam je po wciśnięciu kropki lub wpisaniu „->”.

Kilka moich losowych propozycji w guście C++ (komentarze wyjaśniają):

void loadScript_Gameplay( arguments ); //zamiast loadGameplayScript. Pozniej mozna dodac np. loadScript_Menu itd.
void setEvent_LoadGame( Callback&amp; func ); //podobne do tego wyzej
void setId( uint id ); //zamiast dziwnie czytelnego setID
 
void resetUse( uint id ); //resetuje ilosc uzyc danego elementu w klasie
void resetUseAll(); //moim zdaniem lepsze od resetAllUse, bo mamy szybszy wybor w IDE pomiedzy tym a powyzszym. Zdecydowanie bardziej intuicyjniejsze kiedy mamy dodatkowo to co ponizej
void resetAll(); //resetuje wszystko w danej klasie

4. Sens tworzenia funkcji

Ten dylemat jest chyba najbardziej fundamentalny i dlatego jest na końcu, że jego najszybciej się pozbywamy wraz ze zdobytym doświadczeniem (po kilku tygodniach kodowania jesteśmy raczej bardzo wprawni w mądrej decyzji). Ale o co tak naprawdę chodzi?

Pytamy o sens tworzenia danej funkcji. Najlepiej odpowiemy sobie kolejnymi pytaniami odpowiadającym w sumie nam na wszystko:

  • czy będziemy używali jej wielokrotnie czy tylko jednorazowo? Jeżeli wielokrotnie to też można zwrócić uwagę po ilu „stronach” (tj. usera a kodera biblioteki)
  • czy instrukcji w funkcji będzie dużo?
  • czy, jeśli to metoda, takie użycie daje/jest bezpieczeństwo/bezpieczne?

Tych wszystkich pytań może być oczywiście więcej, ale to już o co pytać, to każdy powiniem wypracować sobie sam :)

Happy coding!

5 odpowiedzi na temat “Tworzenie funkcji”

  1. raver napisał:

    W cpp niema czegoś takiego jak metody, są tylko funkcje składowe (member function :)

  2. namek napisał:

    To stwierdzenie chyba jakieś wyssane z palca :) Być może w innym języku ten termin oznacza coś innego. Wszędzie na necie jest dosyć popularny ten wyraz – przy C++ właśnie. Patrz przykłady [1][2]

    [1] http://www.google.pl/search?hl=pl&q=C%2B%2B+class+method&btnG=Szukaj+w+Google&lr=
    [2] http://pl.wikibooks.org/wiki/C++/Dziedziczenie

  3. Kurak napisał:

    Metoda to nazwa z terminologii OOPu i nie widzę przeszkód, by nie używać jej, programując w C++.

    Ad sama notka – ja podział funkcji w zależności od tego, czy są częścią interfejsu, czy implementacji stosuję w C#. W C++ natomiast wszystkie funkcje nazywamWłaśnieTak. Po angielskiemumu, oczywiście ;>

  4. icek napisał:

    Troche za duzo przykladow. Kazdy i tak ma swoj indywidualny sposob nazewnictwa, jednak wazna jest pozniejsza konsekwencja w nazywaniu. To czy piszemyTak czy w_inny_sposob nie ma znaczenia. Notacja wegierska takze traci coraz bardziej na znaczeniu, zwlaszcza korzystajac z „najnowszych” IDE, wystarczy najechac kursorem na nazwe zmiennej i hop, wyskakuje nam typ w baloniku. ;-) Nie mniej jednak zasady sa bardzo wazne, kod ktory jest czytelny i jasny – nawet bez komentarzy – jest dobry i do tego powinnismy dazyc.

  5. misiaczekk napisał:

    @raver
    Stary.. ale pojechales po bandzie.

Zostaw odpowiedź