Tym razem trochę o metodologiach programowania. A konkretnie jednej z nich czyli Kopiuj-Wklej, Copy-Paste Development, StackOverlow Development. Wiele nazw, ten sam temat.

 

Każdemu programiście zdarzyło się na jakimś etapie skopiować kawałek kodu z jednego miejsca do innego. Źródłem mógł być wcześniej napisany samodzielnie kod, bądź też pochodzący z jakiegoś źródła z Internetu. Ci, którzy zaczynali się uczyć programowania z książek pewnie przepisywali kod i sprawdzali co się stanie.

 

Kiedy zaczynałem programować w .NET pamiętam, że miałem taką książkę z przykładami kodu. Bardzo dużo różnych fragmentów gotowych do wykorzystania. W tamtym czasie była to bardzo pomocna pozycja. Dawno to było, pewnie ok roku 2005. Jednakże pamiętam, jak dzięki recepturom zawartym w książce udawało mi się dość szybko rozwiązywać problemy, z którymi się mierzyłem. Dzisiejsza rzeczywistość jest znacznie inna i te same rzeczy można odszukać na StackOverflow.

 

Jednakże to co pomagało w czasie uczenia się języka w późniejszym czasie wielokrotnie powodowało irytację. Pracując na dużych projektach i widząc bardzo dużą duplikację kodu miałem nawet ochotę wyciągać ludziom klawisze C i V na klawiaturze.

 

Jak większość rzeczy są dobre i złe strony każdego podejścia. Po prostu trzeba wiedzieć jak korzystać odpowiedzialnie z dostępnych narzędzi.

 

Minusy programowania przez kopiuj-wklej

Niezrozumiały kod

Bardzo często czas jest ważniejszy od jakości. Ktoś ma problem do rozwiązania. Nie wie jak go ogarnąć. Pyta o pomoc wujka Googla. Ten mu odpowiada linkiem do StackOverlow. Tam gotowy kawałek kodu. Szybkie kopiuj-wklej do własnego projektu. Bach działa. Lecimy dalej. Niestety ale to jest rzeczywistość. Problem jest w tym, że kod, który jest kopiowany nie zawsze jest zrozumiały dla osoby, która go kopiowała. Aplikacja działa, programista nie zastanawia się dlaczego tak jest. Najważniejszy jest wynik a nie zrozumienie problemu. Taka praktyka może rodzić więcej problemów.

 

Możliwe błędy

Kod, który był opublikowany na StackOverflow czy innej stronie, mógł być napisany pod konkretny problem. Nie koniecznie on był dokładnie taki sam jak osoby, która go później kopiowała. Kod taki mógł być też uproszczony. Mogło w nim nie być żadnej walidacji zmiennych. Po prostu miał pokazywać jak coś można osiągnąć w przypadku idealnych parametrów. Wiadomo, że walidacja wymaga pisania dodatkowego kodu, który najczęściej jest zbędny w przypadku strony Q&A. Bezmyślnie skopiowany kod, może więc prowadzić do błędów w czasie działania programu.

 

Nieoptymalny kod

Kod opublikowany na stronie Q&A może być napisany nieoptymalnie. Ma pokazywać rozwiązanie danego problemu. Jednakże niekoniecznie może być napisany pod kątem problemu z jakim się mierzy programista, który go kopiuje. Przykładem może być łączenie łańcuchów znaków w pętli. Od razu nasuwa się tutaj użycie klasy StringBuilder. Jak ktoś zada pytanie jak połączyć w pętli łańcuchy znaków to dostanie pewnie odpowiedź w podobnym stylu

 

var builder = new StringBuilder();

for(int i = 0; i <= 10; i++)

{

builder.Append("a");

}

 

Teoretycznie w takim podejściu nie ma nic złego. Teoretycznie, bo kod może być niedopasowany do konkretnego problemu. StringBuilder jest ciekawą klasą, która ma zainicjalizowaną początkowy bufor. W momencie kiedy zostanie dodanych więcej znaków niż jest dostępnych w buforze to bufor jest podwajany. Ta operacja może być bardzo kosztowna w przypadku wielu iteracji. Bardziej optymalnym rozwiązaniem może być inicjalizacja obiektu z większym buforem. Tyle tylko, że jak się kopiuje bezmyślnie kod to nie zwraca się uwagi na takie detale. Innym problemem jest wybieranie odpowiedzi, która ma najwięcej głosów nie czytając komentarzy czy innych odpowiedzi. Te inne odpowiedzi mogą być o wiele bardziej efektywne, ale często wygrywa pierwsza. Tak na marginesie to inne metody łączenia ciągów znaków mogą być bardziej efektywne.

 

Więcej kodu niż potrzeba

O ile na StackOverflow są najczęściej, krótkie fragmenty kodu to już kopiowanie z CodeProject może prowadzić do kopiowania całej masy niepotrzebnego kodu. Przykład. Potrzebne jest zaszyfrowanie jakiegoś teksu. Pytanie do Google i dostajemy odpowiedź z CodeProject, gdzie ktoś zaimplementował sobie kilka różnych metod szyfrowania. Nic, że one nie są potrzebne dla konkretnego zastosowania, ale skoro z CodeProject idzie ściągnąć sobie cały projekt i jeszcze dołączyć go do solucji, więc po co się męczyć i wydzielać to co jest rzeczywiście potrzebne. Na skróty jest najłatwiej. Liczy się przecież często tylko czas, a nie jakość.

 

Brak spójności

Klepanie ficzerów na czas poprzez kopiowanie czyjegoś kodu prowadzi często do tego, że jedna metoda lub klasa może być napisana w różnych stylach. Efekt komiczny. Ot wszystko zależy od osób, które odpowiadały na StackOverflow. Jak się pisze na czas to niekoniecznie patrzy się na refaktoryzację i dostosowanie kodu do własnej konwencji nazewniczej. Więcej o nazwach pod tym linkiem.

 

Wiele potencjalnych miejsc wystąpienia błędu

Inną kategorią jest kopiowanie kodu w ramach projektu. Ot ktoś napisał metodę, która coś tam robi, ktoś inny potrzebuje takiej samej funkcjonalności. Szybkie kopiuj-wklej i problem rozwiązany. Problem się może pojawić w przypadku, gdy kopiowany kod był pełen błędów. Tutaj pisałem więcej o tym do czego może prowadzić takie podejście.

 

Nieaktualny kod

Kopiowanie bez zastanowienia może prowadzić także do kopiowania kodu, który nie będzie dalej rozwijany, gdyż powstał już jego zamiennik. Przykład. Programista potrzebuje wykonać transformację xslt. Trafia na odpowiedź sugerującą użycie klasy XslTransform. No i używa tego bo to pierwsza odpowiedź. Problem w tym, że w międzyczasie pojawiła się transformacja za pomocą XslCompiledTranform. XslTransform została oznaczona jako przestarzała i nie będzie dalej rozwijana.

 

Dobre strony programowania przez kopiuj-wklej

Wystarczy tego demonizowania. Podejście kopiowania i wklejania kodu jeżeli wykorzystane odpowiedzialnie może przynieść także wiele korzyści i znacznie skrócić czas tworzenia oprogramowania jednocześnie podnosząc jego jakość.

 

Snippety

Zmieniasz często projekty? Są one podobne do siebie w swojej naturze? Jak tak to pewnie albo piszesz wielokrotnie ten sam kod, bądź też kopiujesz go pomiędzy projektami. Pamiętasz jak pisałem o książce z recepturami, którą wykorzystywałem podczas uczenia się programowania. Pewnie właśnie tego tutaj potrzebujesz. Repozytorium fragmentów kodu, które wykorzystujesz w wielu projektach. Może być to zewnętrzne repozytorium, lub też w ramach swojego ulubionego środowiska programistycznego możesz sobie przygotować fragmenty kodu, które będziesz wielokrotnie wykorzystywał.

 

Gotowe biblioteki

Możesz pójść dalej w tym podejściu i stworzyć sobie gotowe biblioteki. Korzystasz z platformy .NET? Jak tak to pewnie też korzystasz z NuGetów. Wykorzystywanie NuGetów to nic innego jak kopiowanie tego samego kodu pomiędzy projektami. Z taką różnicą, że kod ten już jest skompilowany. Możesz Tworzyć swoje biblioteki, które będą Ci pomagać w pracy na wielu projektach. Jak coś jest dobrze napisane, obtestowane, najlepiej automatycznie, to po co tą samą funkcjonalność za każdym razem pisać na nowo.

 

Szablony projektów w IDE

W VisualStudio tworząc nowy projekt, możesz sobie wybrać szablon z którego on powstanie. Można pisać ten kod każdorazowo samodzielnie. Pytanie tylko po co to robić. Szablony projektów sprawdzają się idealnie jak tworzysz rozwiązania, które są do siebie podobne. Fajne jest to, że można tworzyć także swoje szablony. Ponownie jest to tworzenie duplikacji kodu, jednakże w tym przypadku jest to bardzo pomocne i oszczędzające czas. Nikt chyba nie lubi wykonywać wielokrotnie tej samej pracy.

 

Modele

Piszesz aplikacje w oparciu o API, którego jesteś pierwszym klientem? Tworzysz web service, który ma swój model, następnie serializowany do JSON i zwracany w odpowiedzi do klienta. Jak klient po swojej stronie też potrzebuje taki sam model POCO, to nie ma sensu pisać go na nowo, tylko skopiować. Można naturalnie stworzyć osobną bibliotekę, zawierającą modele. Jednakże nie w każdej sytuacji jest to możliwe do zrealizowania.

 

Nauka

Kopiowanie czyjegoś kodu, jeżeli robione odpowiedzialnie, może być też dobrą nauką. Analizując kod, a nie tylko go kopiując, możesz się nauczyć wielu ciekawych rzeczy. Rozwijać swoje umiejętności. Takie podejście może nauczyć Ciebie rozwiązywania problemów w inny sposób niż robiłeś to dotychczas. Możesz łatwiej zrozumieć mechanizmy i techniki, które wcześniej były jedną wielką niewiadomą.

 

Zaoszczędzony czas

Na pewno tworzenie oprogramowania z wykorzystaniem istniejącego już kodu w bardzo dużym stopniu może zaoszczędzić czas na zrealizowanie projektu. Musi być to robione jednakże odpowiedzialnie. Tak by zarówno eliminować niepotrzebne, powtarzalne czynności, jednocześnie dbając o jakość.

 

A jakie ty widzisz zalety bądź wady podejścia do programowania przez kopiuj-wklej? A może nie kopiujesz w ogóle, nie używasz gotowych komponentów? Podziel się swoją opinią na ten temat.

 

We wpisie na temat czystego kodu jako jeden z jego atrybutów wymieniłem nadawanie odpowiednich nazw.

 

Napisany kod jest czytany jest przez kompilator. Dla niego nie ma znaczenia to jak obiekty, zmienne czy inne rzeczy będą nazwane. Ostatecznie będzie kod maszynowy, który się wykona albo nie. Jednakże najczęściej to kod ten czytany jest albo przez autora albo też innych programistów.

 

Z uwagi na to, że będzie to czytała istota ludzka to warto by było aby czytający mógł się skupić na tym co ten kod robi a nie jak wygląda. Jak piszesz kod dla kolegi, to pisz go tak jak ty byś chciał aby on pisał dla Ciebie.

 

Konwencja nazewnicza

Programujesz od dłuższego czasu? Zmieniasz projekt? Siadasz do środowiska, w którym programujesz. I co? Piszesz jak zawsze czy tak jak inni w projekcie? Konwencja nazewnicza używana dotychczas może nie być taka sama jak w nowym projekcie. Jeżeli uważasz, że to w jaki sposób zespół nazywa elementy w swoim kodzie jest daleki od idealnego, to zmieńcie tą praktykę wspólnie. Jednakże, niech będzie spójność.

 

Inspekcja kodu

Jeżeli w projekcie nad którym pracujesz przeprowadzasz regularnie inspekcję kodu, to konwencja nazewnicza ma ogromne znaczenie. Sprawdzając kod innej osoby zamiast skupiać się na kłujących w oczy nazwach lepiej poświęcić uwagę na to co ten kod rzeczywiście robi. Albo powinien robić.

 

Sposób komunikacji

Pisanie kodu to nie tylko komunikacja z kompilatorem. Pisząc kod komunikujesz się z innymi osobami, które będą go w przyszłości czytały. Po pierwsze niech napisany kod jasno wyraża cel. Po drugie niech łatwo się go czyta. Jak już te warunki masz spełnione to pomyśl o tym jak będą Ciebie oceniać inni. Napisany przez Ciebie kod jest Twoją wizytówką. To jak Twoje CV. Tak więc dbaj o to by wyglądał on jak najlepiej.

 

Powszechne nazwy to część języka programowania

Już wcześniej cytowałem Phila Karltona.

There are only two hard things in Computer Science: cache invalidation and naming things.

 

Problem nie musi być jednakże tak duży jak by się mogło wydawać. Zacznij od tego by nazwy określające powszechne zagadnienia stały się częścią języka programowania. Nie wymyślaj nowych nazw na istniejące już terminy. Na to nie sensu tracić czasu. Odstaw swoje ego a kreatywność wykorzystaj na lepsze rozwiązanie problemów z jakimi się mierzysz. Na pewno nimi nie są nazwy jakich powinieneś używać.

 

Bądź też konsekwentny w tym co robisz. Jak spełnisz ten warunek to nazywanie elementów kodu przestanie być problemem. Stanie się to tak naturalne jak pisanie nowej klasy czy deklarowanie zmiennej.

 

Nazwy łatwe do wymówienia

Jeżeli musisz prezentować co robi Twój kod innym osobom z zespołu, to niech on będzie napisany tak by łatwo było Ci go wymówić. Nie komplikuj w tym miejscu. Nie skracaj przesadnie nazw. Nie ma sensu łamać sobie języka omawiając kod tylko po to by zaoszczędzić kilka znaków napisanych na klawiaturze.

 

Nazwa wskazuje cel

Nazwa metody, argumentów, zmiennych itp. Powinny jasno wskazywać na cel w jakim będą użyte. Jeżeli warunek ten zostanie spełniony to kod staje się samo komentujący. Analizując tak napisany kod nie trzeba poświęcać zbyt wiele czasu na rozszyfrowanie działania algorytmu. Zagadnienie to nawiązuje na używania powszechnych nazw. W przypadku zmiennych może być to bardzo proste, jednakże większy poziom skomplikowania jest w przypadku metod. Metody jest trudniej nazywać szczególnie w przypadku gdy robią zbyt dużo rzeczy. Na ratunek przychodzi pojedyncza odpowiedzialność. Temu zagadnieniu poświęcę osobny wpis. Pisanie kodu SOLID-nego to bardzo ciekawy, choć trudny aspekt programowania. Pozornie trudny, bo gdy nawyki się już wyrobi to trudne stają się inne rzeczy.

 

Komentarze w kodzie

Jest to temat bardzo często kontrowersyjny. Ja jednakże wychodzę z założenia, że kod sam się powinien komentować. Jak wszystko zostanie dobrze nazwane to nie ma potrzeby dodawania komentarzy. Po co spędzać czas na pisanie komentarzy jak można go wykorzystać na wymyślenie dobrej nazwy. Komentarze w kodzie to bardziej złożony temat. Czasem straszny a czasem zabawny. Poświęcę na niego osobny wpis w przyszłości.

 

Długość nazw

Nazwy powinny być na tyle długie by było wiadomo jaki jest cel tego na co wskazują. Jednakże powinny być też na tyle krótkie, by nie trzeba było używać suwaka poziomego lub zmniejszania wielkości czcionki aby wszystko objąć wzrokiem. PO prostu ich długość powinna być w sam raz. Jeżeli nazwa jest zbyt długa to może to oznaczać na złamanie zasady pojedynczej odpowiedzialności. Znowu kłania nam się jeden z filarów SOLID-nego kodu. Reasumując nazwa powinna być zwięzła ale nie taka która ma w sobie ukrytych wiele skrótów. Najlepiej jak skrótów w nazwach nie ma w ogóle.

 

Jeżeli akcja to czasownik

Metody odpowiedzialne są za jakieś akcje. Dlatego też w ich nazwie powinien znajdować się czasownik, lub też powinny być czasownikiem. Warto spojrzeć na ten problem z perspektywy kontekstu w jakim jest kod wykonywany. W przypadku metod oczekujemy, że coś zrobią. Jeżeli funkcja ma pełni rolę gettera, np. zwracającego wartość CSV obiektu, to może być nazwana GetCSV. Obiekt, mógłby mieć także właściwość CSV, której getter by zwracał tą samą wartość. Jednakże metoda sugeruje, że coś się będzie wykonywać, natomiast właściwość, że wartość jest już zapisana w obiekcie. Może mieć to wpływ na wydajność programu.

 

Ciekawym praktycznym przykładem, z którym większość programistów .NET ma styczność na co dzień są metoda i właściwość Count w LINQ. W tym przypadku nazwa może być zarówno rzeczownikiem jak i czasownikiem. Jednakże wykorzystanie właściwości i metody może mieć określony wpływ na wydajność. W przypadku gdy zwracany obiekt jest typu ICollection, wtedy metoda odwołuje się bezpośrednio do właściwości Count, która została już obliczona podczas dodawania i usuwania elementów. Natomiast w przypadku gdy obiekt nie jest typu ICollection, to wtedy cała kolekcja musi być enumerowana aby obliczyć ilość elementów.

 

W przypadku oporu przed czasownikami dobrze jest chociaż zastosować przyimek lub zaimek, który będzie wskazywał na relację pomiędzy funkcją i jej wynikiem. Przykładem takiego podejścia jest rzutowania typów. Np.. ToString lub IndexOf.

 

Brak akcji jest rzeczownikiem

W przeciwieństwie do metod, od zmiennych i właściwości oczekujemy, że będą one statyczne. Tutaj nie ma z reguły żadnej akcji. Pewnym wyjątkiem są właściwości, które jako minimum wykonują akcję ustawienia wartości ukrywającej się pod nim zmiennej. Z racji tego, że nazywają one coś statycznego powinny być rzeczownikami.

 

Jedna czynność = jedna nazwa

Unikaj używania wielu nazw do określenia tej samej czynności. Jeżeli jest kilka klas, które mają operować na źródle danych to niech metody odpowiedzialne za poszczególne akcje nazywają się tak samo. Czyli jak coś ma być zapisane to metoda w każdej klasie niech nazywa się Save, a nie w jednej Save, w innej Persist a jeszcze w innej Record. Tutaj powrócę do powszechności nazw. Jeżeli jedna nazwa jest używana do opisania danej akcji i jest to traktowane jako część języka to odpada konieczność silenia się nad tworzeniem nazw. Po prostu ta konkretna akcja staję się częścią języka.

 

Tak samo, nie powinno się używać jednej nazwy do nazwania wielu różnych akcji. Załóżmy, że klasa może pobierać wartości z różnych źródeł danych. Np. z bazy danych i pliku. Użycie w tym przypadku nazwy Get dla obu operacji wyciągania danych utrudniłoby czytanie kodu. O wiele czytelniejszy byłby kod gdyby nazwy metod były GetFromDatabase  oraz GetFromFile. Dalej metody, mogłyby przyjmować różną ilość, bądź tym parametrów wejściowych, jednakże byłoby jednoznaczne z jakiego źródła pobierane są dane. To takie uproszczenie tylko, gdyż w przypadku tego typu problemu, lepiej było by zastosować wzorzec Repository. Wtedy rzeczywiście nazwa mogłaby być identyczna, ale to już nazwa klasy wskazywałaby na typ źródła danych.

 

Nieczytelny kod pokusą do jego przepisania

Kod, który jest nieczytelny i trudny do odszyfrowania staje się pokusą do jego przepisania. Przecież jak się przepisze kod na taki, który będzie bardziej czytelny to łatwej będzie go utrzymywać. Tak może i często jest. Jednakże, każda próba przepisania, wymyślenia na nowo, programu zwiększa ryzyko wprowadzenia błędów lub co więcej niepożądanej zmiany logiki istniejącego programu.

 

Najlepiej aby pisać kod, by był on zrozumiały dla innych osób i nie rodził pokus przepisywania. Prosta zmiana nazwy także może rodzić problemy w przypadku, gdy ktoś używa dynamicznych typów danych. Niestety ale na etapie kompilacji nie będzie wiadomo czy stare nazwy są dalej w użyciu.

 

To kilka zasad, których ja staram się przestrzegać pisząc kod z definicji czytelny. Jednakże to jest tylko mój styl i każdy może kodować w inny sposób. Masz inne spostrzeżenia, proszę podziel się opinią.

 

Hello World. Napisałeś dzisiaj jakiś kod? Jesteś z niego zadowolony? Jak na te dwa pytania odpowiedziałeś Tak, to czas aby zapytać się kogoś innego aby przeczytał Twoją pracę. Najlepiej dać go do poczytania z zaznaczeniem, że ma być to w godzinach wieczornych. Poproś o informację zwrotną od osoby czytającej kod czy był on nużący i ciężki do przebrnięcia. A może wręcz przeciwnie czytało się go przyjemnie i łatwo było zrozumieć co dokładnie ma robić.

 

Co zrobić aby Twój recenzent nie musiał wkładać zapałek do oczu aby zakończyć inspekcję kodu? Jeżeli kiedykolwiek było to Twoim zmartwieniem i nie znalazłeś jeszcze odpowiedzi to Ci podpowiem.

 

Czysty kod

Dwa słowa. Czysty kod. Co definiuje czysty kod? Kod czysty, z którym się przyjemnie pracuje to kod prosty. Proste rzeczy są łatwiej przyswajalne.

 

Wyobraź sobie, że pracujesz z audytorem. Jego rolą jest to aby znaleźć jak najwięcej słabych punktów w Twojej pracy. Musi zidentyfikować wszystkie ryzyka. Ty wiesz, że masz trochę za uszami, ale nie chcesz aby prawda wyszła na jaw. Osoba, z którą rozmawiasz uważa się za eksperta. Jednakże Ty wyczuwasz rysę na szkle. Wyczuwasz, że to jest tylko maska i wiedza Twojego oponenta nie jest tak obszerna jak mu się wydaje. On także to wie, ale nie może zrzucić maski. Podejmujesz grę. Mówisz rzeczowo, ale używasz bardzo dużo żargonu. Nie kłamiesz, ale mówisz tak by nie wiele zrozumiał. Im mniej będzie rozumiał tym mniej będzie zadawał pytań. On wie, że musi uchodzić za eksperta, a nie rozumiejąc o czym Ty mówisz nie może zadawać głupich pytań, które mogłyby go ośmieszyć.

 

Czysty kod jest dokładnym przeciwieństwem. Jest prosty w zrozumieniu. Jest taki, o którym osoba robiąca inspekcje kodu może z łatwością opowiadać. Jest to kod, który bez większego wysiłku umożliwi osobie go czytającej opowiedzieć całą logikę biznesową jaki on opisuje.

 

Reguła pierwsza. Keep It Simple, Stupid. Im mniej złożoności tym lepiej

Niech kod będzie prosty.

 

Załóżmy, że dostajemy następujący ciąg znaków: "10 NAN 15". Każdy element w ciągu jest oddzielony spacją. Celem zadania jest wyciągnięcie liczb całkowitych i zsumowanie ich.

 

Proste zadanie, jednakże można do niego użyć wielu sposobów.

 

Metoda 1. Wyrażenia regularne

private static int RegEx(string text)

        {

            return Regex.Matches(text, @"([1-9]\d*)").Cast<Match>().Sum(i => {

                int.TryParse(i.Value, out int numericValue);

                return numericValue;

            });

        }

 

Metoda 2. String split + LINQ

private static int SplitLinq(string text)

        {

            return text.Split(' ').Sum(i=> {

                int.TryParse(i, out int numericItem);

                return numericItem;

            });

        }

 

Metoda 3. String split + pętla for

private static int SplitFor(string text)

        {

            int result = 0;

            foreach(var item in text.Split(' '))

            {

                int.TryParse(item, out int numericValue);

                result += numericValue;

            }

 

            return result;

        }

 

Wynik każdej z metod będzie taki sam. Liczba 25. Jeżeli chodzi o długość kodu to metoda 3 ma najwięcej linii. Ale to nie ilość kodu świadczy o jego prostocie. Na szczęście minęły czasy, kiedy ktoś oceniał programistów po tym ile linii kodu zostało stworzonych.

 

Metody 1 i 2, taka sama ilość linii kodu. Takie samo podejście jeżeli chodzi o sumowania. Wyrażenie lambda niczym się nie różni. Jednakże metoda 1 ma dodatkowy element skomplikowania. Jest nim wyrażenie regularne. Myślę, że większość programistów sięga po wyrażenia regularne tylko w ostateczności. Jednakże spotkałem się także z ich gorącymi zwolennikami, którzy by do każdego problemu ich użyli. Dodatkowo metoda 1 zawiera jeszcze rzutowanie na Match.

 

Tak naprawdę metoda 1 zawiera błąd w wyrażeniu regularnym. Wystarczy, że parametr wejściowy będzie wyglądał następująco "10 NAN 15 1.5" i wynik zwrócony przez pierwszą metoda nie będzie już 25 a 31. Po prostu wzorzec wyrażenia regularnego jest niekompletny.

 

Tak więc po pierwsze rozwiązania najprostsze są najlepsze. Jak ktoś nie programował w języku, w którym są wykorzystywane lambdy, to metoda 3 będzie najbardziej czytelna i zrozumiała na pierwszy rzut oka.

 

Rozmawiałem kiedyś z kolegą. Mówił, że pracował z bardzo mądrym programistą. Napisał on bardzo ciekawy i zwięzły algorytm. Ów kolega dodał jednak, że innym programistom zrozumienie tego kodu zajęło pół dnia. Są okoliczności, w których warto wysilić się i napisać bardzo optymalny kod. Jednakże w większości aplikacji jest to zwyczajnie niepotrzebne.

 

Spójność

Jakkolwiek piszesz kod. Jakichkolwiek używasz standardów, trzymaj się ich w każdym fragmencie kodu jaki piszesz. Tak naprawdę odnosi się to do całego zespołu. Niech kod wygląda tak jak by pił pisany jednego dnia przez tą samą osobę. Nawet jak produkt rozwijasz od 10 lat.

 

Poniżej krótki przykład braku spójności.

public void DoSomething(string strParam1, int intParam2, string param3)

{…}

 

Uwierz mi taki kod istnieje. Ktoś kiedy zaczął pisać używając notacji węgierskiej. Ktoś inny musiał zmodyfikować metodę, ale już tej notacji węgierskiej nie używał. Ot taki klasyczny potworek. Może zmieniły się standardy kodowania w danej firmie. Nie ma to aż tak wielkiego znaczenia. Jednakże jak już się zmienia konwencje, to albo należy zmienić też istniejący kod, albo też dostosować się do tego co już zostało napisane. Mieszanie stylów sprawia, że po prostu odechciewa się czytać taki kod.

 

Nazwy wszystkiego

Programujesz obiektowo? Twoje klasy i ich atrybuty są reprezentacją rzeczywistości. Powinny przyjmować też rzeczywiste nazwy.

 

Jak piszesz program rysujący kwadraty, to potrzebujesz odpowiedni obiekt.

public class Shape

    {

        public int x { get; set; }

 

    }

 

Powyższy obiekt opisuje tą prostą rzeczywistość. Jest to kształt i ma jeden wymiar. Jednakże będzie to czytelne tylko dla osoby piszącej kod. Bo Shape to obiekt? Może być to równie dobrze koło. A włąściwość x nie będzie już długością boku tylko średnicą. A może ten x to w ogóle nie jest długość boku, tylko ilość jaka ma być narysowana. Tych informacji nie da się niestety wywnioskować czytając ten kod w oderwaniu od całej reszty programu w jakim on jest osadzony.

 

O wiele łatwiej zrozumieć intencje mając następującą klasę

public class Square

    {

        public int SideLengthMilimiters { get; set; }

 

    }

 

Nazwy jakich użyjesz mają bardzo duże znaczenie. Muszą one mieć jednoznacznie definiujące cel. Nie ma znaczenia czy to będzie zmienna, pole klasy, właściwość, metoda czy sama klasa. Cokolwiek to jest niech nazwa nie wprowadza cienia wątpliwości z czym mamy do czynienia.

 

Klasa i właściwość reprezentują rzeczy, tak więc ich nazwy powinny być rzeczownikami. Metody z kolei wykonują jakieś czynności, tak więc naturalnie powinny być czasownikami.

 

Nazywanie rzeczy nie jest łatwe. W końcu nie bez powodu Phil Karlton powiedział kiedyś

 

There are only two hard things in Computer Science: cache invalidation and naming things.

 

Więcej o nadawaniu nazw możesz poczytać w tym wpisie.

 

Nie zostawiaj po sobie śladu obecności

Czytałem niedawno książkę na temat ćwiczenia uważności (Mindfulness: Jak wytrenować dzikiego słonia i inne przygody w praktyce uważności). Jedna z metod ćwiczeń polega na tym by nie zostawiać po sobie śladu obecności. Jak przygotowujesz sobie posiłek w kuchni, to doprowadź ją do takiego stanu w jakim była przed Twoim pojawieniem się tam. A najlepiej dodaj coś jeszcze od siebie i uprzątnij coś jeszcze.

 

W programowaniu ciężko nie pozostawić po sobie śladu. No chyba, że się tylko kod ogląda, a nie pisze go. Metoda ta ma jednakże swoje zastosowanie. Z tym, że jest opisana w inny sposób.

 

Boy scout rule. Leave the campground cleaner than you found it.

 

W praktyce sprowadza się to do tego by kod, który jest modyfikowany wyglądał lepiej niż został zastany. Nie ma sensu rzucać się na refaktoryzację całego systemu. Jednakże poprawienie kodu na poziomie metody lub klasy będzie na pewno wartością dodaną. A wysiłku dużo nie kosztuje.

 

Nie duplikuj kodu. Don't Repeat Yourself (DRY)

Do rzadkości nie należy kopiowanie już istniejącego kodu do swojego programu. Jednym z przejawów tego jest podejście zwane "Copy and Paste driven development".

 

Programista 1, napisał metodę w swojej klasie. Zobaczył ją Programista 2 i pomyślał, że potrzebuje dokładnie takiego samego kodu u siebie. Podobnie postąpił Programista 3, kopiując kod od Programisty 2. No i wyszło, że ten sam kawałek kodu jest w 3 miejscach. A mógł być tylko w jednym aby łatwiej było nim zarządzać.

 

Oprogramowania jednakże lubi żyć swoim życiem. Czasami rodzą się w nim inne życia. Czyli błędy. No i taki znalazł Programista 1 w swoim kodzie. Poprawił błąd i jest szczęśliwy. Nie powiedział jednakże tego Programiście 2, no bo skąd miał wiedzieć, że ten skorzystał z jego kodu. Tym bardziej skąd miał wiedzieć, że Programista 3 także miał ten sam kod u siebie. Po pierwsze kod stał się nie spójny, a po drugi są w nim błędy, które trzeba niezależnie utrzymywać. Same problemy.

 

Jak by ich było mało, Programista 3 jest bardzo kreatywny i dużo kodu dodaje do repozytorium (nie jest to równoznaczne z pisaniem). Stwierdził, że ta metoda, którą skopiował nie działa tak jak powinna. Zmienił ją tak by działała dobrze.

 

Jako, że Programista 3 jest trzecią iteracją programisty, jest więc bardziej obeznany z dobrymi praktykami. Postanowił, że poprawi też metodę w klasie Programisty 2. Tylko, że nic mu nie powiedział, bo ten był prawie na urlopie.

 

Pech chciał, że ten kod nie działał już tak jak on tego oczekiwał.  Przy okazji wyszło, że użytkownicy programu, przyzwyczaili się już do błędu, który został naprawiony przez Programistę 1 a występował on w kodzie Programisty 2. Zrobili sobie nawet obejście problemu. No i kolejny problem. Okazało się, że ten błąd w kodzie Programisty 2, przekształcił się w inny klasyk.

 

It'a not a bug, it's a feature

 

Chodzenie na skróty jest dobre na krótką metę. Długo terminowo może być bardzo kosztowne. Jak masz zamiar kopiować kod to zastanów się nie dwa a pięć razy czy nie lepiej byłoby go wydzielić do osobnego miejsca i dzielenie pomiędzy modułami.

 

Czy nie tak postępujesz z frameworkiem, którego używasz? A może dekompilujesz kod i wklejasz do swojej klasy?

 

To tylko kilka zasad czystego kodu. Temat będę w różnych wątkach kontynuował w przyszłości. Złożoność i mnogość aspektów jest bardzo duża i każde z zagadnień mogłoby być poparte długim wywodem.