Cześć! Bardzo dobrze że przejrzałeś kod projektów i zadajesz te pytania, wydaje się że idziesz właściwą ścieżką.
Zanim pokuszę się o odpowiedź na Twoje pytania (może nie na wszystkie jestem kwalifikowany udzielić odpowiedzi), muszę wyartykułować pewną tezę: tzw. "zasady czystego kodu" to tylko zbiór wskazówek. Owszem - dzięki stosowaniu ich kod staje się zazwyczaj lepszy, jednak trzeba zrozumieć że świat i ludzie nie są perfekcyjni. Wpadanie w niekończącą się rutynę refactoru, żeby kod był krystaliczny, może być ok jeśli możesz sobie na to pozwolić (a i tak na końcu zawsze znajdzie się coś do poprawy). Tworzenie oprogramowania zazwyczaj wygląda jak swego rodzaju kompromis - dostarczanie kodu możliwie jak najwyższej jakości przy jednoczesnych ograniczeniach czasowych na dostarczanie działających funkcjonalności. Zawsze masz założone więzy czasowe - nie jest ważne, czy to jakiś komercyjny projekt w którym masz swój udział (tutaj więzy są narzucone przez management, który sypie złotem) czy to Twój prywatny projekt (w najbardziej optymistycznym przypadku masz tylko 24 godziny swojego czasu na dobę). Musisz też zdawać sobie sprawę, że nawet jeśli 4 na 5 członków zespołu stara się wdrażać zasady czystego kodu, to już jedna osoba która mniej dba o kod który pisze, potrafi znacznie obniżyć jakość produktu - zazwyczaj nikogo nie stać na to, żeby przepisywać na nowo 1/5 funkcjonalności już zaraz przy jej kontrybucji.
Warto też zdawać sobie sprawę z tego, że sporo z tych zasad jest subiektywnych - w jednym zespole zaadoptowane w jeden sposób, w innym - inaczej.
1. Czy używanie, bądź nie klamerek do jednej instrukcji jest konieczne, czy zależy od stylu?
Jeśli kompilator Ci pozwala na nieużywanie klamerek - to konieczne nie jest, zatem zależy od przyjętej w danym projekcie konwencji. Z mojego doświadczenia - klamerki w tej sytuacji są całkiem spoko. Dodają czytelności, zmniejszają ryzyko pomyłki oraz łatwo jest dorzucić jakąś instrukcję do już istniejącej. Oczywiście nie są to super znaczące korzyści. ;)
2. O ile dobrze pamiętam w książce była mowa, że nie należy używać przedrostka "I" do nazw interfejsów, tylko nazywać je normalnie. A klasy, które coś implementują powinny mieć przyrostek "Imp". Ja zauważyłem, że raczej nikt tak nie robi. Jak myślicie o tym?
Wiem że to nie jest żaden argument, ale osobiście nie widziałem żeby ktoś tak tego używał. :) Aczkolwiek zazwyczaj pracowałem ze starszą bazą kodu, gdzie konwencja była ustalona na długo przed popularyzacją książki Martina. Moje zdanie - dopóki wiadomo co jest czym i nazwy nie wprowadzają konfuzji, jest ok. Ważne żeby były spójne w całym projekcie.
3. Czy używanie przedrostka "_" do prywatnych pól jest dobrą praktyką? Zdaniem, autora książki to jest nadmiarowość, której trzeba unikać.
Taka sama nadmiarowość, jak dodawanie przedrostka I lub przyrostka Imp do interfejsu / implementacji. Najważniejsze żeby było spójnie. Ja osobiście nie mam preferencji w tym zakresie. Taki przedrostek w zasadzie szkodliwy nie jest (forma '_' jest dużo lepsza niż np. 'm', które już dodaje literę do identyfikatora i może zaciemnić jego znaczenie), a pomaga na pierwszy rzut oka rozróżnić, czy pracujemy aktualnie z czymś dostępnym publicznie. No i np. w Pythonie to jest systemowe rozwiązanie na ukrycie części składowych.
4. Czy metody/funkcje mogą być długie?
Długie metody nie są fajne pod względem utrzymania, ale czasami się zdarzają z powodów o których pisałem we wstępie. No i zdarza się że refactor takiej metody jest kłopotliwy: jeśli mam przerabiać całą architekturę klas, żeby pozbyć się jednej długiej metody gdzieś na krańcu projektu, gdzie zagląda ktoś raz na ruski rok, to wolę zrobić coś bardziej pożytecznego, np. załatać jakiegoś uciążliwego buga. ;)
5. Gdzieś przeczytałem, że jedna linijka kodu nie powinna być dłuższa niż 80 znaków/kolumn. Moim zdaniem, że jest to bardzo rygorystyczne. Czy jest to konieczne, aby stosować się do takich ograniczeń?
Jak wszystkie te zasady - nie jest to konieczne. O ile oczywiście konwencja w danym projekcie na to pozwala. Są takie które nie przyjmą kodu gdzie chociaż jeden znak wystaje za margines, są też takie w których programiści kierują się raczej zdrowym rozsądkiem i jeśli zdarzy się że coś tam trochę wystaje, a nie zaburza to czytelności, kod jest akceptowany.
6. Często widziałem w różnych projektach, nadmiarowe this do prywatnego pola, nawet w konstruktorach, w których parametry miały inne nazwy niż pola. Czy stosowanie dodatkowego this jest dobre?
Wskazanie explicite że modyfikuje się część aktualnej instancji nikomu jeszcze nie zaszkodziło, a w drugą stronę - na pewno się zdarzyło.
7. Zdaniem autora książki, nie powinniśmy bezpośrednio wywoływać konstruktorów, tylko używać odpowiednich metod. Ja zauważyłem, że różnie jest to stosowane. Jak wy myślicie?
Drugi sposób jest nieco bardziej elastyczny, bo wprowadza dodatkową warstwę abstrakcji. Jednakże wszystkiego trzeba używać z głową. Gdzieś ten konstruktor i tak musi zostać wywołany; a jeśli masz jakąś prostą klasę narzędziową z dwoma atrybutami i jednym konstruktorem, zastanów się czy jest w ogóle sens ukrywać wołanie c-tora i wprowadzać taką dodatkową warstwę.
8. W jednym projekcie zauważyłem, że zamiast stworzyć obiekt i przypisać go do zmiennej, potem wywołać odpowiednią metodę to zamiast tego po prostu wywołano konstruktor i po tym metodę. Jest to dobre rozwiązanie?
Rozwiązanie jest tak dobre lub złe zależnie od kontekstu. Jeśli ktoś nie potrzebował przechowywać instancji, tylko chciał otrzymać wynik działania jakiejś metody którą oferowała klasa, to dlaczego miał tego nie zrobić w taki sposób? ;) Pozostaje pytanie, czy ta metoda nie powinna dostać statycznego odpowiednika, jeśli pojawiło się zapotrzebowanie na jej zawołanie bez potrzeby przechwycenia obiektu.
9. Z tego co mi wiadomo test powinien mieć tylko jedną asercję. Poniżej jest fragment kodu, który ma więcej niż jedną asercję. Czy metody testowe mogą mieć kilka asercji?
Szczerze - nie wyobrażam sobie tego w praktyce. Wyobraź sobie scenariusz, w którym testujesz czy jakiś obiekt stworzył się poprawnie. Czy napiszesz kilka(naście) metod testowych gdzie w każdej z nich zawrzesz tworzenie danego obiektu w taki sam sposób oraz asercję jednego atrybutu stworzonego obiektu? A co jeśli sama kreacja danego obiektu nie jest tania? Jasne - im mniej asercji w metodzie, tym bardziej zwarty kod i mniejsze ryzyko błędu, ale zawsze trzeba podchodzić do wszystkiego z głową, biorąc pod uwagę kontekst w którym się pracuje.
10. Czy poprawne jest stosowanie w metodach testowych ify, switche, pętle, try-catch itd.?
Im prostszy scenariusz testowy, tym lepiej. Jednak testy to jest też oprogramowanie i czasami się bez tego nie da poprawnie zaimplementować testu.
11. W książce było powiedziane, że metoda nie powinna mieć więcej niż 2 lub 3 parametry (nie pamiętam czy 2 czy 3). Zdarzało mi się widzieć, że konstruktory lub metody miały dużą ilość argumentów. Czy można w czystym kodzie stosować takie konstruktory/metody?
Tutaj tak samo jak z długością metody - raczej nie powinno się. Ale znów: jeśli miałbym zrobić refactor i wprowadzić dodatkowe 2 klasy tylko po to, żeby zredukować liczbę argumentów jednej metody z 4 do 2, to zadałbym sobie najpierw pytanie, czy to się rzeczywiście opłaca. ;)
12. Nazwy powinny przedstawiać intencję. Czy zatem stosowanie takich zapisów podobnych do poniższych jest dobre w czystym kodzie?
Nie, taki kod jaki pokazałeś na 99% nie przeszedłby pozytywnie przez moje review. Aczkolwiek należy pamiętać że wszystkie identyfikatory są zawsze użyte w jakimś kontekście. Jeśli w danej domenie powszechnie wiadomo co to jest `sa` czy `psb`, to to jest ok.
13. Czy używanie przyrostków „Manager”, „Processor”, „Data”, „Info” jest złe? Spotykałem nie raz typy z tymi przyrostkami, ale w książce jest mowa, że nic one nie znaczą i należy je unikać.
A co zatem zrobisz, jeśli Twoim zadaniem będzie napisać procesor tekstu? ;D
14. Czy to złe gdy coś podobnie się nazywa? Podobno należy tego unikać, aby nie było w przyszłości nie chcianych błędów.
Wszystko z głową. Generalnie to stworzenie sobie takich 3 zmiennych to proszenie się o kłopoty - programista może się pomylić, reviewer nie wychwycić błędu... Jeśli zajdzie potrzeba stworzenia sobie takich trzech zmiennych służących do przechowania jakichś 3 instancji, może lepiej nadać im jakieś bardziej rozpoznawalne nazwy, np. firstStudent, secondStudent lub spiąć je w jakąś listę / tablicę.