Najczęściej łamane zasady czystego kodu w C# — 10 przykładów
1. Powielanie (duplication)
Ten sam kod występuje więcej niż raz. Jeśli nie jest to zamierzone, powinien zostać przeniesiony do oddzielnej metody lub klasy — zgodnie z zasadą “nie powtarzaj się” (DRY).
// ❌ ta sama walidacja powielona w dwóch miejscach
if (string.IsNullOrEmpty(imie) || imie.Length > 50) throw new ArgumentException();
// ... gdzie indziej w kodzie:
if (string.IsNullOrEmpty(nazwisko) || nazwisko.Length > 50) throw new ArgumentException();
// ✅ jedna metoda, wywoływana z obu miejsc
static void WalidujTekst(string wartosc)
{
if (string.IsNullOrEmpty(wartosc) || wartosc.Length > 50) throw new ArgumentException();
}2. Zbyt duże klasy i metody
Klasy i metody powinny mieć “tylko jeden powód do zmiany”, zgodnie z zasadą pojedynczej odpowiedzialności (SRP). Typowe sygnały: za dużo linii kodu, zbyt wiele zależności w konstruktorze, kod dotyczący różnych, niepowiązanych przypadków użycia w jednej klasie.
// ❌ jedna klasa robi walidację, zapis do bazy i wysyłkę e-maila
public class ZamowienieService
{
public void ZlozZamowienie(Zamowienie z)
{
if (z.Suma <= 0) throw new ArgumentException();
using var conn = new SqlConnection("...");
conn.Execute("INSERT INTO Zamowienia ...");
new SmtpClient().Send("potwierdzenie@firma.pl", z.Email, "Zamówienie", "...");
}
}
// ✅ trzy klasy, każda z jednym powodem do zmiany
public class WalidatorZamowienia { public void Waliduj(Zamowienie z) { /* ... */ } }
public class ZamowieniaRepository { public void Zapisz(Zamowienie z) { /* ... */ } }
public class PowiadomieniaService { public void WyslijPotwierdzenie(Zamowienie z) { /* ... */ } }3. Niewykorzystany kod
Powstaje na dwa sposoby: kod stał się nieaktualny po refaktoryzacji/zmianie wymagań, albo ktoś dodał funkcjonalność “na zapas”, która nie jest jeszcze potrzebna. W obu przypadkach nieużywany kod dodaje niepotrzebnej złożoności — to dokładnie przypadek, przed którym ostrzega zasada YAGNI (“You Aren’t Gonna Need It”).
// ❌ parametr i gałąź kodu, których nikt jeszcze nie używa "na przyszłość"
public decimal ObliczCene(Produkt p, bool czyPrzyszlaPromocjaSwiateczna = false)
{
if (czyPrzyszlaPromocjaSwiateczna) { /* funkcja jeszcze nieużywana nigdzie w kodzie */ }
return p.Cena;
}
// ✅ dodaj tę logikę, gdy faktycznie będzie potrzebna, nie wcześniej
public decimal ObliczCene(Produkt p) => p.Cena;4. Magiczne stringi i liczby
Kod z bezsensownymi literałami, takimi jak client == 1 albo customer.Type = "AD", jest trudny do odczytania, utrzymania i refaktoryzacji. Wyliczenia (enum) i stałe dają czytelność i sprawdzanie poprawności w czasie kompilacji.
// ❌ magiczne wartości — co znaczy "1"? co znaczy "AD"?
if (client == 1) { }
if (customer.Type == "AD") { }
// ✅ nazwane, sprawdzane przez kompilator
enum TypKlienta { Standardowy = 1, Administracyjny }
if (client == TypKlienta.Standardowy) { }
if (customer.Type == TypKlienta.Administracyjny) { }5. Za dużo instrukcji warunkowych
Gdy logika biznesowa jest skomplikowana, łatwo o głęboko zagnieżdżone if/else, trudne do przeczytania i zrozumienia. Często da się to rozwiązać zasadą Open/Closed i wzorcem Strategy.
// ❌ każdy nowy typ rabatu to kolejna gałąź if/else
decimal ObliczRabat(string typKlienta, decimal suma)
{
if (typKlienta == "VIP") return suma * 0.2m;
else if (typKlienta == "Stały") return suma * 0.1m;
else return 0;
}
// ✅ nowy typ rabatu = nowa klasa, zero zmian w istniejącym kodzie
interface IStrategiaRabatu { decimal Oblicz(decimal suma); }
class RabatVip : IStrategiaRabatu { public decimal Oblicz(decimal suma) => suma * 0.2m; }
class RabatStaly : IStrategiaRabatu { public decimal Oblicz(decimal suma) => suma * 0.1m; }6. Brak hermetyzacji
To najczęściej łamana zasada czystego kodu — spotykana niemal w każdym kodzie. Każda klasa jest publiczna, z publicznymi właściwościami i publicznymi metodami, tylko dlatego, że IDE domyślnie dodaje słowo kluczowe public. To nie jest programowanie obiektowe — obiekty powinny ukrywać swoje wewnętrzności.
// ❌ pełna ekspozycja stanu — każdy może ustawić dowolną, nawet niepoprawną wartość
public class KontoBankowe
{
public decimal Saldo; // dowolny kod może zrobić: konto.Saldo = -1000000;
}
// ✅ stan chroniony, zmieniany tylko przez kontrolowane metody
public class KontoBankowe
{
public decimal Saldo { get; private set; }
public void Wplac(decimal kwota) { if (kwota > 0) Saldo += kwota; }
}7. Niechlujne formatowanie
Nie wpływa na wykonanie kodu, ale wpływa na czytelność — a to problem, nawet jeśli wydaje się drobny. Formatowanie w profesjonalnym kodzie to standard, nie opcja, tak samo jak w dobrze wydanej książce.
// ❌ brak spójnego wcięcia i formatowania
public class Foo{public int X;public void Bar(){if(X>0){Console.WriteLine("x");}}}
// ✅ EditorConfig + formatter (Ctrl+K, Ctrl+D w Visual Studio) wymuszają jeden standard
public class Foo
{
public int X;
public void Bar()
{
if (X > 0)
{
Console.WriteLine("x");
}
}
}8. Za dużo komentarzy
Komentarze tłumaczące oczywisty kod nie są potrzebne — kod mówi sam za siebie, a kontrola źródła (Git) i tak zapamięta historię zmian. Komentarze mają sens tylko w dwóch przypadkach: dokumentowanie publicznego API i wyjaśnienie nietypowego obejścia (hacka), którego nie da się inaczej wytłumaczyć.
// ❌ komentarz powtarza to, co kod już mówi
// sprawdź czy wiek jest większy niż 18
if (wiek > 18) { }
// ✅ zamiast komentarza — nazwa, która mówi to samo bez tłumaczenia
bool czyPelnoletni = wiek > 18;
if (czyPelnoletni) { }Technika refaktoryzacji nazywana Extract Method (wydzielenie fragmentu kodu do osobnej, dobrze nazwanej metody) zwykle zastępuje potrzebę komentarza lepiej niż sam komentarz.
9. Złe i niespójne nazewnictwo
Nazewnictwo jest często pomijane, szczególnie przez mniej doświadczonych programistów — a nie powinno tak być. Dobre nazewnictwo bywa trudne, ale bezpośrednio wpływa na poprawność i łatwość utrzymania kodu.
// ❌ nic nie mówiące nazwy
var d = DateTime.Now;
var lst = GetData();
void Proc(int x) { }
// ✅ nazwa mówi, czym coś jest i po co istnieje
var dataZlozeniaZamowienia = DateTime.Now;
var aktywniKlienci = GetAktywnychKlientow();
void ObliczRabat(int liczbaZamowien) { }10. Zła obsługa wyjątków
Zawsze łap najbardziej specyficzny wyjątek (np. DivideByZeroException, nie ogólny Exception). Uważaj też na ponowne rzucanie wyjątku — throw ex; niszczy oryginalny stack trace, podczas gdy throw; go zachowuje.
// ❌ złapanie wszystkiego + throw ex niszczy informację, gdzie faktycznie wystąpił błąd
try { return 10 / dzielnik; }
catch (Exception ex) { throw ex; }
// ✅ konkretny wyjątek + throw (bez ex) zachowuje oryginalny stack trace
try { return 10 / dzielnik; }
catch (DivideByZeroException) { throw; }Podsumowanie
Dziesięć wzorców, które najczęściej psują czystość kodu, ma jedną wspólną cechę: każdy da się wykryć bez uruchamiania programu, samym czytaniem kodu. Duplikacja, magiczne wartości, brak hermetyzacji i złe nazewnictwo to najtańsze do naprawienia problemy — bo nie wymagają zmiany logiki biznesowej, tylko sposobu, w jaki ta logika jest zapisana. Warto je eliminować na bieżąco, zanim skumulują się w kod, którego nikt już nie chce dotykać.
🚀 Co dalej?
Zobacz to w praktyce na wideo i pobierz darmową roadmapę, żeby ułożyć naukę w spójną ścieżkę do pierwszej pracy.
- 🗺️ Pobierz darmową roadmapę Junior .NET Developer — 12 kroków od podstaw C# do pierwszej pracy: dev-hobby.pl
- 🎬 Subskrybuj kanał YouTube — nowe filmy co tydzień.
8 comments
Dodaj komentarz
Musisz się zalogować, aby móc dodać komentarz.
Zamień wiedzę w umiejętności
Pobierz darmową Roadmapę .NET i ułóż takie tematy jak ten w spójną ścieżkę do pierwszej pracy.
Pobieram roadmapę →
Wielokrotne pojawianie się tego samego kodu jest nieefektywne i trudne do utrzymania. Lepszym podejściem jest refaktoryzacja poprzez przeniesienie wspólnego kodu do oddzielnej klasy lub metody.
Klasy i metody powinny być zminimalizowane, aby spełniały zasadę pojedynczej odpowiedzialności. Duże klasy z wieloma zależnościami i kodem dotyczącym różnych przypadków użycia są trudne w utrzymaniu.
Nieużywany kod powinien być eliminowany, ponieważ dodaje złożoności i utrudnia zrozumienie kodu. Zasada YAGNI (You Aren’t Gonna Need It) podkreśla, że nie powinniśmy dodawać funkcjonalności na zapas.
Używanie magicznych ciągów i liczb utrudnia czytelność kodu. Zamiast tego, należy używać wyliczeń i stałych, co poprawia czytelność i ułatwia utrzymanie kodu.
Nadmiarowe zagnieżdżone instrukcje warunkowe mogą być trudne do zrozumienia. Zasada Open/Closed i wzorzec strategii mogą pomóc w zredukowaniu nadmiaru instrukcji warunkowych.
Choć nie wpływa na działanie kodu, schludne formatowanie jest istotne dla czytelności. Profesjonalizm programisty obejmuje również estetykę kodu.
Obsługa wyjątków powinna być precyzyjna, łapiąc najbardziej specyficzne wyjątki. Unikaj ponownego wyrzucania tych samych wyjątków bez zastanowienia, aby nie stracić istotnych informacji.