Blog Dla Programistów C#/.NET

Zasady SOLID w C#: czy junior powinien się ich uczyć? 5 pułapek, YAGNI i plan nauki w 5 krokach

środa, 4 listopada 2026 Tagi: C#/.NETKarieraSOLIDProgramowanie

SOLID. 5 zasad, o które pytają na wielu rozmowach o pracę i które pojawiają się w niemal każdym kursie programowania obiektowego. Podobno fundament czystego kodu. A jednocześnie 5 zasad, które - uczone w złym momencie - robią początkującym programistom więcej szkody niż pożytku.

Wiem, jak to brzmi. Dlatego od razu jedno zastrzeżenie: SOLID nie jest zły. Zły jest moment, w którym się go uczy, i sposób, w jaki się go uczy. Ta różnica robi tu całą robotę i właśnie o niej jest ten artykuł.

Najpierw zobaczysz, skąd SOLID w ogóle się wziął, bo kontekst wyjaśnia bardzo dużo. Potem przejdziemy przez wszystkie 5 zasad i na konkretnym kodzie w C# pokażę Ci, jak każda z nich potrafi wykoleić naukę programowania. Przeczytasz też, co o przesadzie z "czystym kodem" mówią bardzo doświadczeni ludzie z branży. A na koniec dostaniesz konkretny plan: czego uczyć się zamiast i kiedy do SOLID-a wrócić. Bo wrócić naprawdę warto - tylko nie na starcie.

W bonusie dostaniesz jeszcze jedną radę na rozmowę kwalifikacyjną, która odróżnia recytowanie definicji od prawdziwego zrozumienia.

Zasady SOLID w C#: czy junior powinien się ich uczyć? 5 pułapek, YAGNI i plan nauki w 5 krokach

Skąd się wziął SOLID? Kontekst, o którym mało kto mówi


Szybkie przypomnienie, żebyśmy nadawali na tych samych falach. SOLID to 5 zasad projektowania obiektowego:

• S - Single Responsibility Principle (zasada pojedynczej odpowiedzialności),
• O - Open-Closed Principle (zasada otwarte-zamknięte),
• L - Liskov Substitution Principle (zasada podstawienia Liskov),
• I - Interface Segregation Principle (zasada segregacji interfejsów),
• D - Dependency Inversion Principle (zasada odwracania zależności).

Uncle Bob, rok 2000 i świat C++. Te zasady zebrał i opisał Robert C. Martin, znany jako Uncle Bob, około 2000 roku. Sam akronim SOLID ułożył zresztą kto inny - Michael Feathers, około 2004 roku. Najważniejsze jest jednak tło: to był świat C++, enterprise Javy i zupełnie innych narzędzi. Git jeszcze nie istniał, CI/CD nie było codziennym standardem, wdrożenia były większe i rzadsze, a refaktoryzacja dużo częściej była ręczną, ryzykowną operacją na otwartym sercu. Te zasady odpowiadały na problemy tamtego świata.

2003 vs 2026. Zobacz, jak bardzo zmieniło się otoczenie, w którym piszemy kod:

Obszar20032026
Kompilacja / buildminuty, a nawet godzinyczęsto sekundy
Deployduże, rzadkie wdrożenianawet codziennie
Cofnięcie zmianydramatgit revert
Refaktoryzacjaręcznie, na czuja1 skrót w Riderze albo Visual Studio


Koszt zmiany kodu spadł bardzo mocno. A my dalej uczymy projektowania tak, jakby każda zmiana była katastrofą.

Główna teza: SOLID to leki na choroby, których jeszcze nie przeszedłeś


I tu dochodzimy do sedna. SOLID to leki. Bardzo konkretne leki na bardzo konkretne choroby. Problem w tym, że junior dostaje je, zanim przeszedł którąkolwiek z tych chorób.

Wyobraź sobie, że idziesz do lekarza i bez żadnego badania dostajesz 5 recept. Nie wiesz, co leczą, więc na wszelki wypadek bierzesz wszystkie. Codziennie. Na zapas. Dokładnie tak wygląda nauka SOLID w pierwszym roku programowania.

A skutki uboczne? Najlepiej widać je na kodzie, więc przejdźmy po kolei przez wszystkie 5 zasad. 5 zasad, 5 pułapek.

S - Single Responsibility: pułapka liczby linijek


Pierwsza zasada brzmi: klasa powinna mieć tylko jeden powód do zmiany. To najkrótsza zasada z całej piątki i, moim zdaniem, ta, która robi najwięcej szkód u początkujących.

Czym właściwie jest "jeden powód do zmiany"? No właśnie. Zasada brzmi prosto, ale w praktyce szybko robi się nieostra. Sam Uncle Bob później doprecyzowywał (m.in. w książce "Czysta architektura"), że "powód" oznacza ludzi albo aktorów, dla których kod się zmienia. Junior zwykle tego kontekstu nie ma. Dostaje zasadę bez miary, więc mierzy tym, co widzi - liczbą linijek.

Kod, który podobno "łamie SRP". Oto klasyczny przykład z kursów: UserService, w którym walidacja, hashowanie hasła, zapis do bazy i wysyłka maila siedzą w jednej metodzie.

public class UserService
{
public void Register(string email, string password)
{
if (!email.Contains('@'))
throw new ArgumentException("Zły email");

var hash = _hasher.Hash(password);

_db.Users.Add(new User(email, hash));
_db.SaveChanges();

_mail.Send(email, "Witaj na pokładzie!");
}
}

I teraz szczerze: kilkanaście linijek. Czytasz je od góry do dołu jak opowiadanie. 1 plik to cała historia rejestracji, a nowa osoba w projekcie ogarnia ją w 2 minuty. Ten kod działa i jest czytelny.

Ten sam kod po kursie SOLID. A tak często wygląda ta sama funkcjonalność po "poprawkach" zgodnych z SRP - folder Registration z takimi plikami:

• IUserRegistrationService.cs i UserRegistrationService.cs,
• IEmailValidator.cs i EmailValidator.cs,
• IPasswordHasher.cs i PasswordHasher.cs,
• IUserRepository.cs i UserRepository.cs,
• IWelcomeEmailSender.cs i WelcomeEmailSender.cs,
• IUserFactory.cs i UserFactory.cs.

12 plików, 1 funkcjonalność. Interfejs plus implementacja na walidację, na hashowanie, na repozytorium, na maila, na fabrykę... Logika? Dokładnie ta sama. Tylko rozmazana po 12 plikach. Żeby zrozumieć rejestrację użytkownika, skaczesz teraz po całej solucji.

Pułapka SRP. Junior nie tnie klas po odpowiedzialnościach, bo nie wie, czym one są. Tnie po liczbie linijek. Efekt: 50 klas po 5 linijek. To nie jest czysty kod. To puzzle - tylko bez obrazka na pudełku.

O - Open-Closed: kod, który ma przewidzieć przyszłość


Druga zasada: kod ma być otwarty na rozszerzanie i zamknięty na modyfikacje. Brzmi mądrze, prawda? A w praktyce znaczy tyle: pisz kod tak, żebyś nie musiał go zmieniać. Czyli - przewiduj przyszłość.

Mały haczyk. Żeby zamknąć kod na zmiany, musisz wiedzieć, co się zmieni. Junior nie umie przewidywać zmian w projekcie. Senior, tak między nami, też nie umie - on po prostu już wie, że nie umie. Co więc robi junior? Zabezpiecza się przed wszystkim. Czyli w praktyce przed niczym.

Rabat po kursie SOLID. Przykład. Zadanie brzmi: dodaj rabat 10% dla stałych klientów. Junior po kursie SOLID pisze coś takiego:

public interface IDiscountStrategy
{
decimal Apply(decimal price);
}

public class RegularCustomerDiscount : IDiscountStrategy
{
public decimal Apply(decimal price) => price * 0.90m;
}

public class NoDiscount : IDiscountStrategy
{
public decimal Apply(decimal price) => price;
}

public class DiscountStrategyFactory
{
public IDiscountStrategy Create(Customer customer) =>
customer.IsRegular ? new RegularCustomerDiscount()
: new NoDiscount();
}

Interfejs IDiscountStrategy, 2 strategie i fabryka strategii. Cała ta architektura... na jedno "if".

A wystarczyło... to:

public decimal ApplyDiscount(decimal price, bool isRegular)
=> isRegular ? price * 0.90m : price;

1 metoda, 1 linijka logiki. "Ale co, jeśli dojdą nowe rodzaje rabatów?" To wtedy to zmienisz! Od obsługi zmian masz Gita, testy i 5 minut refaktoryzacji. Nie musisz dziś budować katedry na wypadek, gdyby kiedyś przyszli wierni.

YAGNI: zasada, której uczy się za rzadko


Jest zasada, której na kursach uczy się o wiele za rzadko, a która powinna być przed SOLID-em: YAGNI, czyli You Aren't Gonna Need It - nie będziesz tego potrzebować. To jedna z zasad Extreme Programming.

Kod pisany "na zapas" to nie jest inwestycja. To dług. Od dnia, w którym go napiszesz, każdą tę abstrakcję utrzymujesz, czytasz i debugujesz - niezależnie od tego, czy przewidywana przyszłość kiedykolwiek nadejdzie.

L - Liskov Substitution: kwadrat, który uczy strachu


Trzecia zasada, w uproszczeniu: podtyp musi dać się podstawić za typ bazowy bez psucia programu. Ciekawostka: to jedyna zasada z piątki, która ma mocne, akademickie korzenie - Barbara Liskov, 1987, a potem formalniejsze ujęcie Liskov i Wing z 1994 roku. I jednocześnie zasada najgorzej tłumaczona.

Klasyk z każdego kursu. Prawie każdy kurs pokazuje ten sam przykład: kwadrat dziedziczy po prostokącie.

public class Rectangle
{
public virtual int Width { get; set; }
public virtual int Height { get; set; }
}

public class Square : Rectangle /* kwadrat JEST prostokątem? */
{
public override int Width
{ set { base.Width = value; base.Height = value; } }

public override int Height
{ set { base.Width = value; base.Height = value; } }
}

Rectangle rect = new Square();
rect.Width = 4;
rect.Height = 5;

var pole = rect.Width * rect.Height; /* 20? Nie: 25 - bo rect to Square */

Matematycznie kwadrat jest prostokątem, więc niby wszystko gra. Ustawiasz szerokość na 4, wysokość na 5, liczysz pole... i wychodzi 25, a nie 20, bo kwadrat po cichu nadpisał obie wartości. Program się psuje, a razem z nim zaufanie juniora do dziedziczenia. Matematycznie kwadrat jest prostokątem. W kodzie - właśnie nie.

Prawdziwa lekcja. Co z tego przykładu wynosi junior? "Dziedziczenie jest straszne i nic z tego nie rozumiem". A prawdziwa lekcja, której prawie nikt nie mówi wprost, brzmi: dziedzicz rzadko. Zamiast tego składaj klasy z mniejszych klocków, czyli stosuj kompozycję. Jeśli prawie nie używasz dziedziczenia, problem z LSP prawie znika sam.

I - Interface Segregation: eksplozja interfejsów


Czwarta zasada: lepiej wiele małych, konkretnych interfejsów niż jeden wielki. To taka kuzynka SRP - i niesie dokładnie te same pułapki, tylko na poziomie interfejsów.

Efekt u juniora. Wygląda to mniej więcej tak:

public interface IReadable<T>  { Task<T?> GetAsync(int id); }
public interface ICreatable<T> { Task AddAsync(T item); }
public interface IUpdatable<T> { Task UpdateAsync(T item); }
public interface IDeletable<T> { Task DeleteAsync(int id); }

public class ProductRepository :
IReadable<Product>, ICreatable<Product>,
IUpdatable<Product>, IDeletable<Product>
{
/* ... */
}

IReadable, ICreatable, IUpdatable, IDeletable... 4 interfejsy. Implementuje je 1 klasa. Używane są w 1 miejscu. Pytanie: przed czym to chroni? Przed niczym. To architektura dla samej architektury.

Kiedy ISP ma sens. Interface Segregation ma sens wtedy, gdy masz szeroki interfejs i różnych klientów, z których każdy potrzebuje tylko kawałka zachowania. Wtedy nie chcesz zmuszać nikogo do zależenia od metod, których nie używa. Ale w CRUD-zie z 3 kontrolerami często nie masz jeszcze tego problemu. Masz tylko dodatkowy szum. W skrócie: różni klienci - ma sens. Jeden projekt i jedno użycie - często szum.

D - Dependency Inversion: teatr abstrakcji


Ostatnia zasada: zależ od abstrakcji, nie od konkretów. Moim zdaniem najbardziej niezrozumiana zasada z całej piątki.

Co słyszy junior. Bo wiesz, co z tej definicji słyszy junior? "Aha, czyli każda klasa musi mieć interfejs". I zaczyna się: IUserService, IOrderService, IInvoiceService, IEmailService, IPaymentService, INotificationService... I-wszystko-Service.

Teatr abstrakcji. Tak powstaje coś, co nazywam teatrem abstrakcji:

public interface IUserService
{
Task<User?> GetByIdAsync(int id);
Task<int> CreateAsync(CreateUserDto dto);
}

public class UserService : IUserService
{
public Task<User?> GetByIdAsync(int id) { /* ... */ }
public Task<int> CreateAsync(CreateUserDto dto) { /* ... */ }
}

Interfejs IUserService, a pod nim klasa UserService. 1 implementacja. Od 3 lat. I umówmy się - na zawsze. To nie jest abstrakcja, tylko kopiowanie sygnatur metod przez Ctrl+C. Zero zyskanej elastyczności i 1 plik więcej do utrzymania.

O co naprawdę chodzi w DIP. Sedno tej zasady to kierunek zależności między modułami: Twoja logika biznesowa nie powinna znać szczegółów - bazy danych, frameworka, zewnętrznego API. Interfejs tworzysz wtedy, gdy masz powód:

• jest druga implementacja,
• wyznaczasz granicę modułu,
• interfejs realnie ułatwia Ci testy.

Nie "bo tak się robi". Kontener DI w .NET przyjmuje zwykłe klasy tak samo dobrze jak interfejsy:

/* rejestracja zwykłej klasy - bez interfejsu */
builder.Services.AddScoped<UserService>();

/* wstrzykiwanie działa dokładnie tak samo */
public class UsersController : ControllerBase
{
private readonly UserService _userService;

public UsersController(UserService userService)
{
_userService = userService;
}
}

A gdy pojawi się realny powód, interfejs wyciągniesz w kilka sekund - Rider i Visual Studio robią to jednym skrótem.

Co przedwczesny SOLID robi juniorowi w głowie? 3 objawy


Zbierzmy to w całość. Przedwczesna nauka SOLID daje zwykle 3 objawy. Sprawdź, czy któryś brzmi znajomo:

• Paraliż. "Boję się cokolwiek napisać, bo pewnie złamię jakąś zasadę". 1 godzina myślenia nad strukturą. 0 linii kodu.

• Over-engineering. "To tylko todo-lista, ale zrobiłem clean architecture". 40 plików, 6 warstw, 0 użytkowników. Projekt umiera przygnieciony własną architekturą, zanim ktokolwiek go zobaczy.

• Cargo cult, czyli rytuały bez zrozumienia. Pytasz na code review: "Czemu tu jest interfejs?". Odpowiedź: "Bo... tak się robi". Recytacja zamiast myślenia.

Najgorszy efekt. Junior zaczyna oceniać swój kod po zgodności z zasadami, a nie po tym, czy działa i czy da się go czytać. A to dokładnie odwrotność tego, po co te zasady w ogóle powstały.

Nie tylko ja tak mówię: Sandi Metz, Dan Abramov i John Ousterhout


Możesz teraz pomyśleć: dobra, ale to tylko jedna opinia. Słuszna uwaga. Dlatego posłuchaj ludzi, których dorobku raczej nie podważysz.

Sandi Metz: duplikacja jest tańsza niż zła abstrakcja. Sandi Metz, legenda świata programowania obiektowego i autorka książki "99 Bottles of OOP", mówi wprost:

"Duplikacja jest o wiele tańsza niż zła abstrakcja."

W oryginale: "duplication is far cheaper than the wrong abstraction" - tę myśl rozwinęła w głośnym tekście "The Wrong Abstraction" z 2016 roku. Przeczytaj to zdanie jeszcze raz. Skopiowany kod naprawisz w 5 minut. Złej abstrakcji, na której wisi pół projektu, możesz nie odkręcić nigdy.

Dan Abramov: "Goodbye, Clean Code". Dan Abramov, człowiek z zespołu Reacta i współautor Reduxa, napisał w 2020 roku głośny tekst "Goodbye, Clean Code". Historia jest taka: po godzinach "wyczyścił" kod kolegi z zespołu - usunął duplikację i dorobił piękne abstrakcje. Następnego dnia szef poprosił go, żeby to wycofał. Dziś Dan przyznaje szefowi rację: te "brzydkie" powtórzenia były niezależnymi ścieżkami, które za chwilę miały się rozjechać w różne strony. Jego wniosek? Czysty kod nie jest celem. Jest środkiem - i to nie zawsze.

John Ousterhout: classitis. John Ousterhout, profesor Stanforda i autor książki "A Philosophy of Software Design", nazwał tę chorobę: classitis, czyli obsesyjne mnożenie malutkich klas i metod. Jego rada jest odwrotna do tego, co słyszy junior: mniej modułów, ale głębokich - proste API na zewnątrz, dużo treści w środku. W rozmowie opublikowanej w 2025 roku mocno różnił się w tej kwestii z samym Uncle Bobem. I to dobrze pokazuje, że zasady projektowania to nie prawa fizyki, tylko opinie, o które eksperci wciąż się spierają.

Czego uczyć się zamiast SOLID? Plan w 5 krokach


Skoro nie SOLID na start, to co? Oto konkretny plan - 5 kroków, w tej kolejności:

1. Pisz kod, który działa.
2. Nazywaj rzeczy dobrze.
3. Stosuj regułę trzech.
4. Refaktoryzuj, gdy boli.
5. Pisz testy.

Krok 1: najpierw niech działa. Kent Beck ujął to idealnie:

"Make it work. Make it right. Make it fast."

Najpierw niech działa, potem niech będzie porządnie, na końcu szybko. W tej kolejności, nie odwrotnie. Junior, który zaczyna od "right" przed "work", projektuje zamki na piasku - piękną architekturę rzeczy, która jeszcze nie istnieje.

Krok 2: nazwy ważniejsze niż wzorce. Porównaj:

var d = 3;           /* co to w ogóle jest? */
var retryCount = 3; /* aaa, teraz jasne! */

Dobra nazwa robi dla czytelności więcej niż 5 interfejsów. I trik, który dostajesz gratis: jeśli nie umiesz krótko nazwać klasy albo metody, to znaczy, że robi za dużo. Gratulacje, właśnie odkryłeś SRP. Bez kursu :)

Krok 3: reguła trzech. Piszesz coś pierwszy raz - normalnie. Potrzebujesz tego samego drugi raz - skopiuj i nie przepraszaj, przeżyjesz. Dopiero za trzecim razem wyciągnij część wspólną:

• 1× napisz,
• 2× skopiuj,
• 3× wyciągnij część wspólną.

Abstrakcja zbudowana z 3 prawdziwych przykładów zawsze bije abstrakcję zbudowaną z wyobraźni.

Krok 4: refaktoryzuj, gdy boli. Nie projektuj na zapas - zmieniaj kod, kiedy przyszłość rzeczywiście nadejdzie. Umiejętność zmiany kodu jest dziś warta więcej niż umiejętność przewidywania. Masz Gita, testy i Ridera albo Visual Studio, więc refaktoryzacja to tania operacja.

Krok 5: pisz testy. Kod, który trudno przetestować, to kod źle zaprojektowany - test działa jak wykrywacz. Serio: testy nauczą Cię dobrego designu szybciej niż 10 kursów o SOLID. Bo uczą na Twoim kodzie, a nie na przykładzie z kwadratem i prostokątem.

Kiedy wrócić do SOLID?


Prosta checklista. Wróć do SOLID, gdy:

• masz za sobą 6-12 miesięcy w prawdziwym projekcie,
• utrzymujesz kod, który sam napisałeś pół roku temu,
• poczułeś ten ból, kiedy jedna zmiana ciągnie za sobą poprawki w 15 miejscach naraz.

Wtedy SOLID "kliknie" w tydzień. Bo w końcu będziesz wiedzieć, po co jest.

Słownik, nie religia. I wtedy odkryjesz, że SOLID to świetny słownik, ale fatalna religia. Na code review zdanie "ta klasa ma za dużo powodów do zmiany" to precyzyjna, dorosła rozmowa o kodzie. Ale SOLID jako checklista odhaczana przy każdym pliku to prosta droga do paraliżu i teatru abstrakcji.

Bonus: SOLID na rozmowie kwalifikacyjnej


Reality check: tak, o SOLID mogą Cię zapytać na rozmowie kwalifikacyjnej. Dlatego naucz się definicji i przygotuj do każdej zasady po jednym przykładzie. Ale zrób jedną rzecz więcej: przy każdej zasadzie powiedz, kiedy jej nie stosować.

To odróżnia recytację od zrozumienia i daje Ci realną przewagę. Wykutą definicję umie powiedzieć wiele osób. Kompromisy rozumie dużo mniej.

Ściąga: 5 zasad SOLID, typowe pułapki i kiedy mają sens


Wszystkie 5 zasad w jednej tabeli - przyda się też przed rozmową kwalifikacyjną:

ZasadaW skrócieTypowa pułapka junioraKiedy ma sens
S - Single ResponsibilityKlasa ma tylko jeden powód do zmianyCięcie klas po liczbie linijek: 12 plików na 1 funkcjonalnośćGdy klasę zmieniają różni ludzie (aktorzy) z różnych powodów
O - Open-ClosedOtwarte na rozszerzanie, zamknięte na modyfikacjeStrategia, fabryka i interfejs na jedno "if"Gdy nowe wymaganie naprawdę przyszło - wtedy refaktoryzujesz
L - Liskov SubstitutionPodtyp da się podstawić za typ bazowy bez psucia programuKwadrat dziedziczący po prostokącie i strach przed dziedziczeniemZawsze, gdy dziedziczysz - ale dziedzicz rzadko i wybieraj kompozycję
I - Interface SegregationWiele małych interfejsów zamiast jednego wielkiego4 interfejsy, 1 klasa, 1 miejsce użyciaSzeroki interfejs i różni klienci, z których każdy potrzebuje tylko kawałka
D - Dependency InversionZależ od abstrakcji, nie od konkretówInterfejs do każdej klasy, czyli teatr abstrakcjiDruga implementacja, granica modułu, realnie łatwiejsze testy


Podsumowanie


SOLID nie jest zły. Zły jest moment, w którym każe się go uczyć ludziom, którzy jeszcze nie napisali i nie utrzymywali wystarczająco dużo kodu. W skrócie:

• Kontekst: SOLID powstał w świecie, w którym zmiana kodu była droga i ryzykowna. Dziś koszt zmiany spadł bardzo mocno.
• 5 zasad, 5 pułapek: cięcie po linijkach, przewidywanie przyszłości, strach przed dziedziczeniem, eksplozja interfejsów i teatr abstrakcji.
• 3 objawy: paraliż, over-engineering i cargo cult.
• Zamiast tego: pisz kod, który działa, dbaj o nazwy, stosuj regułę trzech, refaktoryzuj, gdy boli, i pisz testy.
• Powrót do SOLID: po 6-12 miesiącach w prawdziwym projekcie - wtedy zrozumiesz go w tydzień i będzie Twoim słownikiem, a nie religią.
• Na rozmowie: definicja + 1 przykład + kiedy zasady nie stosować.

Jeśli masz zapamiętać z tego artykułu jedno zdanie, niech to będzie to: najpierw naucz się pisać kod. Potem naucz się go zmieniać. SOLID przyjdzie sam.

Jeśli chcesz regularnie dostawać taką konkretną wiedzę o .NET, dołącz do mojej społeczności - jest w niej już ponad 10 000 programistów .NET. Raz w tygodniu wysyłam jednego maila: najważniejsze newsy ze świata .NET i konkretna wiedza bez lania wody. Członkowie społeczności dostają też rabaty na wszystkie moje kursy, zanim ktokolwiek inny o nich usłyszy. Zero spamu, sama treść - a wypisujesz się jednym kliknięciem.

Wystarczy, że zostawisz swój adres tutaj: modestprogrammer.pl/vip. Do zobaczenia w środku.

Autor artykułu:
Kazimierz Szpin
Kazimierz Szpin
CTO & Founder - FindSolution.pl
Programista C#/.NET. Specjalizuje się w Blazor, ASP.NET Core, ASP.NET MVC, ASP.NET Web API, WPF oraz Windows Forms.
Autor bloga ModestProgrammer.pl
Dodaj komentarz
© Copyright 2026 modestprogrammer.pl | Sztuczna Inteligencja | Regulamin | Polityka prywatności. Design by Kazimierz Szpin. Wszelkie prawa zastrzeżone.