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& 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!
Luty 16th, 2008 at 19:59
W cpp niema czegoś takiego jak metody, są tylko funkcje składowe (member function :)
Luty 16th, 2008 at 23:16
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
Luty 20th, 2008 at 18:12
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 ;>
Luty 22nd, 2008 at 7:00
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.
Lipiec 15th, 2008 at 10:24
@raver
Stary.. ale pojechales po bandzie.