Kurs JavaScript i TypeScript · Moduł 7: Zaawansowany TypeScript

Modyfikatory dostępu (public, private, protected)

14 min czytania
W tej lekcji9

"Bezpieczeństwo w Parku Jurajskim to kwestia najwyższej wagi," stwierdza stanowczo John Hammond, prezes InGen. "Niektóre dane i procedury muszą być dostępne dla wszystkich pracowników, inne tylko dla osób z odpowiednimi uprawnieniami, a jeszcze inne są tak krytyczne, że dostęp do nich ma wyłącznie najwyższe kierownictwo."

Podobnie jak w Parku Jurajskim, w programowaniu obiektowym kontrola dostępu do właściwości i metod klas jest fundamentalnym aspektem dobrego projektowania. TypeScript oferuje trzy modyfikatory dostępu, które pozwalają precyzyjnie określić, kto i w jakim zakresie może korzystać z poszczególnych elementów klas: public, private i protected.

Wprowadzenie do modyfikatorów dostępu

Modyfikatory dostępu określają, z jakich poziomów kodu można uzyskać dostęp do właściwości i metod klasy:

  1. public - dostęp z dowolnego miejsca w kodzie (domyślny)
  2. private - dostęp tylko wewnątrz danej klasy
  3. protected - dostęp wewnątrz danej klasy i klas dziedziczących

Przyjrzyjmy się, jak te modyfikatory działają w praktyce, implementując system zarządzania dinozaurami w Parku Jurajskim.

Modyfikator public

Elementy oznaczone jako public są dostępne z dowolnego miejsca w kodzie - zarówno wewnątrz klasy, jak i na jej instancjach. Jest to domyślny modyfikator dostępu w TypeScript - jeśli nie określisz żadnego modyfikatora, element zostanie automatycznie oznaczony jako public.

1class Dinozaur {
2  // Właściwości publiczne
3  public nazwa: string;
4  public gatunek: string;
5  public wiek: number;
6
7  constructor(nazwa: string, gatunek: string, wiek: number) {
8    this.nazwa = nazwa;
9    this.gatunek = gatunek;
10    this.wiek = wiek;
11  }
12
13  // Metoda publiczna
14  public wyświetlInformacje(): void {
15    console.log(`${this.nazwa}: ${this.gatunek}, ${this.wiek} lat`);
16  }
17}
18
19// Tworzenie instancji
20const rex = new Dinozaur("Rex", "Tyrannosaurus", 7);
21
22// Dostęp do publicznych właściwości i metod z zewnątrz klasy
23console.log(rex.nazwa);  // "Rex" - dostęp do publicznej właściwości
24rex.wiek = 8;  // Modyfikacja publicznej właściwości
25rex.wyświetlInformacje();  // Wywołanie publicznej metody

Właściwości i metody publiczne są jak ogólnodostępne informacje w parku - na przykład mapa parku, godziny otwarcia, czy lokalizacje toalet. Wszyscy mogą ich używać bez ograniczeń.

Modyfikator private

Elementy oznaczone jako private są dostępne tylko wewnątrz klasy, w której zostały zdefiniowane. Ta hermetyzacja pozwala na ukrycie wewnętrznych szczegółów implementacji i zapobiega przypadkowym modyfikacjom stanu z zewnątrz klasy.

1class DiagnozaZdrowia {
2  // Właściwości prywatne
3  private id: string;
4  private wynikiBadań: Record<string, number>;
5  private uwagi: string;
6  public statusZdrowia: "zdrowy" | "chory" | "krytyczny";
7
8  constructor(id: string) {
9    this.id = id;
10    this.wynikiBadań = {};
11    this.uwagi = "";
12    this.statusZdrowia = "zdrowy";
13  }
14
15  // Prywatna metoda - używana tylko wewnątrz klasy
16  private analizujWyniki(): "zdrowy" | "chory" | "krytyczny" {
17    // Złożona logika analizy wyników badań
18    const temperaturaCiała = this.wynikiBadań["temperaturaCiała"] || 37;
19    const poziomLeukocytów = this.wynikiBadań["poziomLeukocytów"] || 7000;
20
21    if (temperaturaCiała > 40 || poziomLeukocytów > 15000) {
22      return "krytyczny";
23    } else if (temperaturaCiała > 38.5 || poziomLeukocytów > 11000) {
24      return "chory";
25    } else {
26      return "zdrowy";
27    }
28  }
29
30  // Publiczne metody korzystające z prywatnych właściwości
31  public dodajWynikBadania(nazwa: string, wartość: number): void {
32    this.wynikiBadań[nazwa] = wartość;
33    this.statusZdrowia = this.analizujWyniki();  // Wywołanie prywatnej metody
34  }
35
36  public dodajUwagę(uwaga: string): void {
37    this.uwagi += uwaga + "\n";
38  }
39
40  public pobierzRaport(): string {
41    return `
42      ID: ${this.id}
43      Status zdrowia: ${this.statusZdrowia}
44      Uwagi: ${this.uwagi || "Brak"}
45    `;
46  }
47}
48
49// Tworzenie instancji
50const diagnozaRaptora = new DiagnozaZdrowia("VL-001");
51
52// Używanie publicznego API
53diagnozaRaptora.dodajWynikBadania("temperaturaCiała", 39.2);
54diagnozaRaptora.dodajWynikBadania("poziomLeukocytów", 12500);
55diagnozaRaptora.dodajUwagę("Objawy infekcji wirusowej");
56
57console.log(diagnozaRaptora.statusZdrowia);  // "chory" - dostęp do publicznej właściwości
58console.log(diagnozaRaptora.pobierzRaport());  // Wywołanie publicznej metody
59
60// Próba dostępu do prywatnych elementów
61// console.log(diagnozaRaptora.id);  // Błąd: Property 'id' is private
62// console.log(diagnozaRaptora.wynikiBadań);  // Błąd: Property 'wynikiBadań' is private
63// diagnozaRaptora.analizujWyniki();  // Błąd: Property 'analizujWyniki' is private

Prywatne właściwości i metody są jak zastrzeżone procedury laboratorium genetycznego - tylko upoważniony personel laboratorium (metody klasy) ma do nich dostęp, a dla reszty personelu są one ukryte ze względów bezpieczeństwa.

Modyfikator protected

Elementy oznaczone jako protected są dostępne wewnątrz klasy, w której zostały zdefiniowane, oraz w klasach dziedziczących. Pozwala to na tworzenie hierarchii klas, gdzie klasy pochodne mają dostęp do chronionych elementów klasy bazowej, ale elementy te pozostają niedostępne z zewnątrz hierarchii klas.

1// Klasa bazowa
2class Zwierzę {
3  // Właściwości chronione
4  protected id: string;
5  protected gatunek: string;
6  protected waga: number;
7  protected zdrowie: number;  // 0-100
8
9  constructor(id: string, gatunek: string, waga: number) {
10    this.id = id;
11    this.gatunek = gatunek;
12    this.waga = waga;
13    this.zdrowie = 100;
14  }
15
16  // Chroniona metoda
17  protected zmodyfikujZdrowie(zmiana: number): void {
18    this.zdrowie = Math.max(0, Math.min(100, this.zdrowie + zmiana));
19  }
20
21  // Publiczna metoda
22  public wyświetlStatus(): string {
23    return `${this.gatunek} (${this.id}): Waga: ${this.waga}kg, Zdrowie: ${this.zdrowie}%`;
24  }
25}
26
27// Klasa dziedzicząca
28class Dinozaur extends Zwierzę {
29  public imię: string;
30  private poziomAgresji: number;  // 0-10
31
32  constructor(id: string, gatunek: string, imię: string, waga: number) {
33    super(id, gatunek, waga);
34    this.imię = imię;
35    this.poziomAgresji = 5;
36  }
37
38  // Metoda wykorzystująca chronione elementy klasy bazowej
39  public nakarm(ilość: number): void {
40    this.zmodyfikujZdrowie(ilość * 5);  // Dostęp do chronionej metody z klasy bazowej
41    this.poziomAgresji = Math.max(0, this.poziomAgresji - 1);
42    console.log(`${this.imię} został nakarmiony. Nowy poziom zdrowia: ${this.zdrowie}%`);
43  }
44
45  public doznajObrażeń(ilość: number): void {
46    this.zmodyfikujZdrowie(-ilość);  // Dostęp do chronionej metody z klasy bazowej
47    this.poziomAgresji = Math.min(10, this.poziomAgresji + 2);
48    console.log(`${this.imię} doznał obrażeń. Nowy poziom zdrowia: ${this.zdrowie}%`);
49  }
50
51  // Nadpisanie metody bazowej z wykorzystaniem chronionych właściwości
52  public wyświetlStatus(): string {
53    return `${this.imię} (${this.gatunek}): Waga: ${this.waga}kg, Zdrowie: ${this.zdrowie}%, Agresja: ${this.poziomAgresji}/10`;
54  }
55}
56
57// Klasa dziedzicząca z modyfikacją chronionej właściwości
58class DinozaurMorski extends Zwierzę {
59  private głębokośćNurkowania: number;
60
61  constructor(id: string, gatunek: string, waga: number, głębokośćNurkowania: number) {
62    super(id, gatunek, waga);
63    this.głębokośćNurkowania = głębokośćNurkowania;
64
65    // Modyfikacja chronionej właściwości w konstruktorze
66    this.zdrowie = 90;  // Morskie dinozaury zaczynają z mniejszym zdrowiem
67  }
68
69  public pływaj(czas: number): void {
70    // Używanie chronionej metody
71    this.zmodyfikujZdrowie(czas > 10 ? -10 : 5);
72
73    console.log(`Dinozaur morski ${this.id} pływał przez ${czas} minut na głębokości ${this.głębokośćNurkowania}m`);
74    console.log(`Nowy poziom zdrowia: ${this.zdrowie}%`);
75  }
76}
77
78// Tworzenie instancji
79const velociraptor = new Dinozaur("VL-002", "Velociraptor", "Blue", 150);
80const mosasaur = new DinozaurMorski("MS-001", "Mosasaurus", 15000, 50);
81
82// Używanie publicznych metod
83velociraptor.nakarm(2);
84velociraptor.doznajObrażeń(15);
85console.log(velociraptor.wyświetlStatus());
86
87mosasaur.pływaj(15);
88console.log(mosasaur.wyświetlStatus());
89
90// Próba dostępu do chronionych elementów
91// console.log(velociraptor.id);  // Błąd: Property 'id' is protected
92// console.log(mosasaur.zdrowie);  // Błąd: Property 'zdrowie' is protected
93// velociraptor.zmodyfikujZdrowie(50);  // Błąd: Property 'zmodyfikujZdrowie' is protected

Chronione właściwości i metody są jak procedury bezpieczeństwa parku - dostęp do nich mają tylko upoważnieni pracownicy i ich podwładni (klasa bazowa i klasy dziedziczące), ale nie goście parku (kod zewnętrzny).

Konstruktor i modyfikatory dostępu

TypeScript oferuje specjalną składnię dla konstruktorów, która pozwala na bezpośrednie deklarowanie właściwości z modyfikatorami dostępu w parametrach konstruktora. Jest to skrótowy zapis, który pozwala uniknąć powtarzania kodu.

1// Tradycyjny zapis
2class PracownikParku {
3  private id: string;
4  public imię: string;
5  public nazwisko: string;
6  protected poziomDostępu: number;
7
8  constructor(id: string, imię: string, nazwisko: string, poziomDostępu: number) {
9    this.id = id;
10    this.imię = imię;
11    this.nazwisko = nazwisko;
12    this.poziomDostępu = poziomDostępu;
13  }
14}
15
16// Skrócony zapis z modyfikatorami w parametrach konstruktora
17class PracownikParkuSkrócony {
18  constructor(
19    private id: string,
20    public imię: string,
21    public nazwisko: string,
22    protected poziomDostępu: number
23  ) {}  // Puste ciało konstruktora - wszystko już zostało zainicjalizowane
24}
25
26// Oba zapisy działają identycznie
27const pracownik1 = new PracownikParku("EMP-001", "John", "Hammond", 10);
28const pracownik2 = new PracownikParkuSkrócony("EMP-002", "Ellie", "Sattler", 8);
29
30console.log(pracownik1.imię, pracownik1.nazwisko);  // "John Hammond"
31console.log(pracownik2.imię, pracownik2.nazwisko);  // "Ellie Sattler"
32
33// console.log(pracownik1.id);  // Błąd: Property 'id' is private
34// console.log(pracownik2.id);  // Błąd: Property 'id' is private

Ta skrócona składnia jest szczególnie przydatna w prostych klasach z wieloma właściwościami, ponieważ redukuje ilość powtarzającego się kodu.

Hermetyzacja i wzorzec dostępowy (gettery i settery)

Jednym z głównych powodów stosowania modyfikatorów dostępu jest hermetyzacja - ukrywanie szczegółów implementacji i udostępnianie tylko kontrolowanego interfejsu do interakcji z klasą. Częścią tego podejścia jest stosowanie getterów i setterów - specjalnych metod kontrolujących dostęp do właściwości.

1class EnclosureSecurity {
2  // Prywatne właściwości
3  private _statusOgrodzenia: "aktywne" | "nieaktywne" | "w_naprawie";
4  private _poziomNaładowaniaBaterii: number;  // 0-100
5  private _ostatniaPrzegląd: Date;
6  private _logZdarzeń: string[];
7
8  constructor() {
9    this._statusOgrodzenia = "aktywne";
10    this._poziomNaładowaniaBaterii = 100;
11    this._ostatniaPrzegląd = new Date();
12    this._logZdarzeń = ["System uruchomiony: " + new Date().toISOString()];
13  }
14
15  // Gettery - kontrolowany dostęp do odczytu prywatnych właściwości
16  get statusOgrodzenia(): "aktywne" | "nieaktywne" | "w_naprawie" {
17    this.dodajLog("Odczytano status ogrodzenia");
18    return this._statusOgrodzenia;
19  }
20
21  get poziomNaładowaniaBaterii(): number {
22    this.dodajLog("Odczytano poziom naładowania baterii");
23    return this._poziomNaładowaniaBaterii;
24  }
25
26  get ostatniaPrzegląd(): Date {
27    return new Date(this._ostatniaPrzegląd.getTime());  // Defensywna kopia
28  }
29
30  get raportBezpieczeństwa(): string {
31    this.dodajLog("Wygenerowano raport bezpieczeństwa");
32    return `
33      Status ogrodzenia: ${this._statusOgrodzenia}
34      Poziom naładowania baterii: ${this._poziomNaładowaniaBaterii}%
35      Ostatni przegląd: ${this._ostatniaPrzegląd.toLocaleDateString()}
36      Liczba zdarzeń w logu: ${this._logZdarzeń.length}
37    `;
38  }
39
40  // Settery - kontrolowany dostęp do modyfikacji prywatnych właściwości
41  set statusOgrodzenia(nowyStatus: "aktywne" | "nieaktywne" | "w_naprawie") {
42    this.dodajLog(`Zmieniono status ogrodzenia z ${this._statusOgrodzenia} na ${nowyStatus}`);
43    this._statusOgrodzenia = nowyStatus;
44
45    if (nowyStatus === "nieaktywne") {
46      this.uruchomProcedurySecurity();
47    }
48  }
49
50  // Ten setter zawiera logikę walidacyjną
51  set poziomNaładowaniaBaterii(nowyPoziom: number) {
52    if (nowyPoziom < 0 || nowyPoziom > 100) {
53      throw new Error("Poziom naładowania baterii musi być między 0 a 100");
54    }
55
56    const staraWartość = this._poziomNaładowaniaBaterii;
57    this._poziomNaładowaniaBaterii = nowyPoziom;
58    this.dodajLog(`Zmieniono poziom naładowania baterii z ${staraWartość}% na ${nowyPoziom}%`);
59
60    if (nowyPoziom < 20) {
61      this.dodajLog("UWAGA: Niski poziom baterii!");
62    }
63  }
64
65  // Prywatna metoda pomocnicza
66  private dodajLog(wiadomość: string): void {
67    this._logZdarzeń.push(`${new Date().toISOString()}: ${wiadomość}`);
68  }
69
70  // Prywatna metoda biznesowa
71  private uruchomProcedurySecurity(): void {
72    this.dodajLog("ALERT: Uruchomiono procedury awaryjne!");
73    // Logika obsługi sytuacji awaryjnej
74  }
75
76  // Publiczna metoda
77  public przeprowadźPrzegląd(): void {
78    this._ostatniaPrzegląd = new Date();
79    this.dodajLog("Przeprowadzono przegląd ogrodzenia");
80    this._poziomNaładowaniaBaterii = 100;
81    this._statusOgrodzenia = "aktywne";
82  }
83
84  // Publiczna metoda
85  public pobierzLogZdarzeń(): string[] {
86    // Zwracamy kopię, aby uniemożliwić modyfikację oryginalnego logu
87    return [...this._logZdarzeń];
88  }
89}
90
91// Użycie klasy
92const security = new EnclosureSecurity();
93
94// Używanie getterów
95console.log("Status ogrodzenia:", security.statusOgrodzenia);
96console.log("Poziom baterii:", security.poziomNaładowaniaBaterii);
97
98// Używanie setterów
99security.statusOgrodzenia = "w_naprawie";
100security.poziomNaładowaniaBaterii = 85;
101
102// Używanie publicznych metod
103security.przeprowadźPrzegląd();
104
105// Próba ustawienia nieprawidłowej wartości
106try {
107  security.poziomNaładowaniaBaterii = 110;  // Wywoła błąd
108} catch (e) {
109  console.error(e.message);
110}
111
112// Odczyt raportu i logów
113console.log(security.raportBezpieczeństwa);
114console.log("Historia zdarzeń:");
115security.pobierzLogZdarzeń().forEach((log, i) => {
116  console.log(`${i + 1}. ${log}`);
117});

Używanie getterów i setterów pozwala na większą kontrolę nad dostępem do danych:

  • Możemy dodać logikę walidacji przed zmianą wartości
  • Możemy wykonać dodatkowe operacje przy dostępie do danych
  • Możemy zwracać defensywne kopie obiektów, aby zapobiec przypadkowym modyfikacjom
  • Możemy logować dostęp do danych

Praktyczny przykład: System zarządzania dinozaurami

Poniżej przedstawiamy kompleksowy przykład systemu zarządzania dinozaurami, który wykorzystuje wszystkie omówione modyfikatory dostępu dla stworzenia dobrze zorganizowanej architektury obiektowej:

1// Typy i interfejsy
2type StatusDinozaura = "zdrowy" | "chory" | "w leczeniu" | "uśpiony" | "zmarły";
3type TypDiety = "mięsożerny" | "roślinożerny" | "wszystkożerny";
4
5interface IMonitorowania {
6  rozpocznijMonitorowanie(): void;
7  zakończMonitorowanie(): void;
8  pobierzStatusMonitorowania(): string;
9}
10
11// Abstrakcyjna klasa bazowa
12abstract class ZwierzęParku {
13  // Właściwości z różnymi modyfikatorami dostępu
14  private _id: string;
15  protected _gatunek: string;
16  protected _wiek: number;
17  protected _waga: number;
18  protected _status: StatusDinozaura;
19  private _dataUtworzenia: Date;
20  private _ostatniaAktualizacja: Date;
21
22  constructor(id: string, gatunek: string, wiek: number, waga: number) {
23    this._id = id;
24    this._gatunek = gatunek;
25    this._wiek = wiek;
26    this._waga = waga;
27    this._status = "zdrowy";
28    this._dataUtworzenia = new Date();
29    this._ostatniaAktualizacja = new Date();
30  }
31
32  // Gettery
33  get id(): string {
34    return this._id;
35  }
36
37  get gatunek(): string {
38    return this._gatunek;
39  }
40
41  get wiek(): number {
42    return this._wiek;
43  }
44
45  get waga(): number {
46    return this._waga;
47  }
48
49  get status(): StatusDinozaura {
50    return this._status;
51  }
52
53  // Setter ze sprawdzeniem wartości i aktualizacją daty
54  set status(nowyStatus: StatusDinozaura) {
55    if (this._status !== nowyStatus) {
56      this._status = nowyStatus;
57      this._ostatniaAktualizacja = new Date();
58      this.logZmianyStatusu(nowyStatus);
59    }
60  }
61
62  // Chroniona metoda dostępna dla klas dziedziczących
63  protected aktualizujWagę(nowaWaga: number): void {
64    if (nowaWaga > 0) {
65      this._waga = nowaWaga;
66      this._ostatniaAktualizacja = new Date();
67    } else {
68      throw new Error("Waga musi być większa od zera.");
69    }
70  }
71
72  // Prywatna metoda dostępna tylko wewnątrz tej klasy
73  private logZmianyStatusu(nowyStatus: StatusDinozaura): void {
74    console.log(`[${new Date().toISOString()}] Zmiana statusu ${this._id}: ${nowyStatus}`);
75  }
76
77  // Abstrakcyjna metoda - każda klasa dziedzicząca musi ją zaimplementować
78  abstract pobierzOpisKliniczny(): string;
79
80  // Publiczna metoda
81  public pobierzPodstawoweDane(): string {
82    return `ID: ${this._id} | Gatunek: ${this._gatunek} | Wiek: ${this._wiek} lat | Waga: ${this._waga} kg | Status: ${this._status}`;
83  }
84}
85
86// Klasa dziedzicząca
87class Dinozaur extends ZwierzęParku implements IMonitorowania {
88  // Prywatne właściwości specyficzne dla dinozaurów
89  private _dieta: TypDiety;
90  private _poziomAgresji: number; // 1-10
91  private _czyMonitorowany: boolean;
92  private _ostatniPosiłek: Date | null;
93  private _przypisanyWybieg: string | null;
94
95  constructor(
96    id: string,
97    gatunek: string,
98    wiek: number,
99    waga: number,
100    dieta: TypDiety,
101    poziomAgresji: number
102  ) {
103    super(id, gatunek, wiek, waga);
104    this._dieta = dieta;
105    this._poziomAgresji = this.walidujPoziomAgresji(poziomAgresji);
106    this._czyMonitorowany = false;
107    this._ostatniPosiłek = null;
108    this._przypisanyWybieg = null;
109  }
110
111  // Implementacja metody abstrakcyjnej
112  pobierzOpisKliniczny(): string {
113    let opis = `Dinozaur ${this.gatunek} (ID: ${this.id})\n`;
114    opis += `Status zdrowotny: ${this.status}\n`;
115    opis += `Wiek: ${this.wiek} lat, Waga: ${this.waga} kg\n`;
116    opis += `Dieta: ${this._dieta}\n`;
117    opis += `Poziom agresji: ${this._poziomAgresji}/10\n`;
118    opis += `Ostatni posiłek: ${this._ostatniPosiłek ? this._ostatniPosiłek.toLocaleString() : "Brak danych"}\n`;
119
120    return opis;
121  }
122
123  // Implementacja interfejsu IMonitorowania
124  rozpocznijMonitorowanie(): void {
125    this._czyMonitorowany = true;
126    console.log(`Rozpoczęto monitorowanie dinozaura ${this.id}`);
127  }
128
129  zakończMonitorowanie(): void {
130    this._czyMonitorowany = false;
131    console.log(`Zakończono monitorowanie dinozaura ${this.id}`);
132  }
133
134  pobierzStatusMonitorowania(): string {
135    return this._czyMonitorowany ? "Aktywny" : "Nieaktywny";
136  }
137
138  // Publiczne metody specyficzne dla dinozaurów
139  public nakarm(ilość: number): void {
140    if (ilość <= 0) {
141      throw new Error("Ilość pokarmu musi być większa od zera.");
142    }
143
144    const przyrostWagi = this._dieta === "mięsożerny" ? ilość * 0.3 : ilość * 0.2;
145    this.aktualizujWagę(this.waga + przyrostWagi);
146
147    this._ostatniPosiłek = new Date();
148    this._poziomAgresji = this.walidujPoziomAgresji(this._poziomAgresji - 1);
149
150    console.log(`Dinozaur ${this.id} został nakarmiony. Nowa waga: ${this.waga} kg`);
151  }
152
153  public przydzielDoWybiegu(kodWybiegu: string): void {
154    this._przypisanyWybieg = kodWybiegu;
155    console.log(`Dinozaur ${this.id} został przydzielony do wybiegu ${kodWybiegu}`);
156  }
157
158  public uśpij(): void {
159    this.status = "uśpiony";
160    this._poziomAgresji = 1;
161    console.log(`Dinozaur ${this.id} został uśpiony`);
162  }
163
164  public obudź(): void {
165    if (this.status === "uśpiony") {
166      this.status = "zdrowy";
167      this._poziomAgresji = this.walidujPoziomAgresji(this._poziomAgresji + 2);
168      console.log(`Dinozaur ${this.id} został obudzony`);
169    }
170  }
171
172  // Gettery i settery
173  get dieta(): TypDiety {
174    return this._dieta;
175  }
176
177  get poziomAgresji(): number {
178    return this._poziomAgresji;
179  }
180
181  get ostatniPosiłek(): Date | null {
182    return this._ostatniPosiłek ? new Date(this._ostatniPosiłek.getTime()) : null;
183  }
184
185  get przypisanyWybieg(): string | null {
186    return this._przypisanyWybieg;
187  }
188
189  set poziomAgresji(poziom: number) {
190    this._poziomAgresji = this.walidujPoziomAgresji(poziom);
191  }
192
193  // Prywatna metoda pomocnicza
194  private walidujPoziomAgresji(poziom: number): number {
195    return Math.max(1, Math.min(10, poziom));
196  }
197}
198
199// Użycie systemu
200function testSystemuZarządzaniaDinozaurami() {
201  // Tworzenie dinozaurów
202  const trex = new Dinozaur("T-REX-01", "Tyrannosaurus Rex", 8, 7500, "mięsożerny", 9);
203  const stegozaur = new Dinozaur("STEG-01", "Stegosaurus", 12, 5000, "roślinożerny", 4);
204
205  // Wyświetlanie podstawowych informacji
206  console.log("=== DANE PODSTAWOWE ===");
207  console.log(trex.pobierzPodstawoweDane());
208  console.log(stegozaur.pobierzPodstawoweDane());
209
210  // Operacje na dinozaurach
211  trex.nakarm(50);
212  trex.przydzielDoWybiegu("CARNIVORE-A");
213  trex.rozpocznijMonitorowanie();
214
215  stegozaur.nakarm(80);
216  stegozaur.przydzielDoWybiegu("HERBIVORE-B");
217
218  // Zmiana statusu
219  trex.status = "w leczeniu";
220  stegozaur.uśpij();
221
222  // Pobieranie raportów klinicznych
223  console.log("\n=== RAPORTY KLINICZNE ===");
224  console.log(trex.pobierzOpisKliniczny());
225  console.log(stegozaur.pobierzOpisKliniczny());
226
227  // Status monitorowania
228  console.log("\n=== STATUS MONITOROWANIA ===");
229  console.log(`T-Rex monitoring: ${trex.pobierzStatusMonitorowania()}`);
230  console.log(`Stegozaur monitoring: ${stegozaur.pobierzStatusMonitorowania()}`);
231
232  // Próba dostępu do prywatnych/chronionych elementów
233  // Poniższe linie spowodowałyby błąd kompilacji:
234  // console.log(trex._id);  // Błąd: Property '_id' is private
235  // console.log(trex._gatunek);  // Błąd: Property '_gatunek' is protected
236  // trex.logZmianyStatusu("zdrowy");  // Błąd: Property 'logZmianyStatusu' is private
237  // trex.walidujPoziomAgresji(5);  // Błąd: Property 'walidujPoziomAgresji' is private
238}
239
240// Uruchomienie testu
241testSystemuZarządzaniaDinozaurami();

W powyższym przykładzie:

  1. Abstrakcyjna klasa bazowa (ZwierzęParku) definiuje wspólne właściwości i zachowania dla wszystkich zwierząt w parku
  2. Prywatne właściwości (_id, _dataUtworzenia, _ostatniaAktualizacja) są dostępne tylko wewnątrz klasy
  3. Chronione właściwości (_gatunek, _wiek, _waga, _status) są dostępne w klasie bazowej i klasach dziedziczących
  4. Publiczne gettery i settery zapewniają kontrolowany dostęp do właściwości
  5. Prywatne metody (logZmianyStatusu, walidujPoziomAgresji) są używane jako pomocnicze implementacje dostępne tylko wewnątrz klasy
  6. Chronione metody (aktualizujWagę) są dostępne dla klas dziedziczących
  7. Abstrakcyjna metoda (pobierzOpisKliniczny) wymusza implementację w klasach dziedziczących
  8. Interface (IMonitorowania) definiuje kontrakt, który klasa Dinozaur musi implementować

Kiedy i jak stosować modyfikatory dostępu

Wybór odpowiedniego modyfikatora dostępu zależy od konkretnego przypadku użycia, ale istnieje kilka ogólnych zasad:

  1. Używaj private dla:

    • Wewnętrznych szczegółów implementacji
    • Danych, które nigdy nie powinny być dostępne poza klasą
    • Metod pomocniczych, które nie są częścią publicznego API
    • Właściwości, które muszą być modyfikowane tylko przez kontrolowane settery
  2. Używaj protected dla:

    • Właściwości i metod, które muszą być dostępne w klasach dziedziczących
    • Implementacji, które mogą być rozszerzone lub nadpisane w klasach pochodnych
    • Danych, które są wspólne dla całej hierarchii klas, ale nie powinny być dostępne z zewnątrz
  3. Używaj public dla:

    • Elementów, które tworzą publiczny interfejs API klasy
    • Metod, które muszą być dostępne dla wszystkich użytkowników klasy
    • Właściwości, które mogą być bezpiecznie odczytywane lub modyfikowane z zewnątrz

Podsumowanie

Modyfikatory dostępu w TypeScript są kluczowym narzędziem do implementacji zasad dobrego projektowania obiektowego, takich jak enkapsulacja, dziedziczenie i polimorfizm. Pozwalają na precyzyjną kontrolę dostępu do właściwości i metod klas, co sprzyja tworzeniu bezpiecznego, modularnego i łatwego w utrzymaniu kodu.

Podobnie jak w Parku Jurajskim, gdzie system bezpieczeństwa wymaga różnych poziomów dostępu, dobrze zaprojektowany system obiektowy wykorzystuje modyfikatory dostępu, aby zapewnić, że każdy element jest dostępny tylko dla tych, którzy rzeczywiście powinni mieć do niego dostęp.

John Hammond podsumowałby to krótko: "W Parku Jurajskim i w programowaniu - bezpieczeństwo jest najważniejsze. Modyfikatory dostępu są jak strefy bezpieczeństwa w parku - publiczne dla wszystkich, chronione dla specjalistów, a prywatne tylko dla najwyższego szczebla zarządzania. To klucz do utrzymania porządku i zapobiegania... niespodziankom."

Kod do tej lekcji: index.ts
1// Modyfikatory dostępu w TypeScript - Park Jurajski
2console.log("System bezpieczeństwa Parku Jurajskiego");
3console.log("Demonstracja modyfikatorów dostępu: public, private, protected\n");
4
5// ===========================================
6// 1. Podstawowe modyfikatory dostępu
7// ===========================================
8console.log("=== 1. MODYFIKATORY DOSTĘPU ===");
9
10class BasicDinosaur {
11  // public - dostępne z każdego miejsca (domyślnie)
12  public name: string;
13  public species: string;
14
15  // private - dostępne tylko w tej klasie
16  private dnaSequence: string;
17  private securityCode: number;
18
19  // protected - dostępne w tej klasie i klasach dziedziczących
20  protected age: number;
21  protected habitat: string;
22
23  constructor(name: string, species: string, dnaSequence: string) {
24    this.name = name;
25    this.species = species;
26    this.dnaSequence = dnaSequence;
27    this.securityCode = Math.floor(Math.random() * 10000);
28    this.age = 0;
29    this.habitat = "Enclosure";
30  }
31
32  // Publiczna metoda
33  public getBasicInfo(): string {
34    return `${this.name} (${this.species})`;
35  }
36
37  // Prywatna metoda
38  private validateDNA(): boolean {
39    return this.dnaSequence.length > 10;
40  }
41
42  // Chroniona metoda
43  protected calculateMaturity(): string {
44    if (this.age < 5) return "Młody";
45    if (this.age < 15) return "Dorosły";
46    return "Stary";
47  }
48
49  // Publiczny dostęp do prywatnej funkcjonalności
50  public isDNAValid(): boolean {
51    return this.validateDNA();
52  }
53
54  // Metoda z kontrolowanym dostępem do prywatnych danych
55  public getSecureInfo(accessCode: number): string | null {
56    if (accessCode === this.securityCode) {
57      return `DNA: ${this.dnaSequence.substring(0, 5)}...`;
58    }
59    return null;
60  }
61}
62
63const basicDino = new BasicDinosaur("Rex", "T-Rex", "ATCGATCGATCG");
64
65console.log("Podstawowe info:", basicDino.getBasicInfo());
66console.log("Publiczne pola:");
67console.log("  Name:", basicDino.name);
68console.log("  Species:", basicDino.species);
69
70console.log("DNA valid:", basicDino.isDNAValid());
71
72// Te nie zadziałają - błędy kompilacji w TypeScript:
73// console.log(basicDino.dnaSequence); // Error: private
74// console.log(basicDino.age); // Error: protected
75// basicDino.validateDNA(); // Error: private method
76
77console.log("\n");
78
79// ===========================================
80// 2. Dziedziczenie i modyfikatory
81// ===========================================
82console.log("=== 2. DZIEDZICZENIE I MODYFIKATORY ===");
83
84class CarnivorousDinosaur extends BasicDinosaur {
85  private huntingExperience: number;
86  protected packSize: number;
87
88  constructor(name: string, species: string, dnaSequence: string) {
89    super(name, species, dnaSequence);
90    this.huntingExperience = 0;
91    this.packSize = 1;
92  }
93
94  // Dostęp do protected pól z klasy bazowej
95  public setAge(newAge: number): void {
96    this.age = newAge; // OK - protected w klasie bazowej
97    console.log(`${this.name} ma teraz ${this.age} lat`);
98  }
99
100  public getMaturityStatus(): string {
101    // Dostęp do protected metody z klasy bazowej
102    return `${this.name} jest ${this.calculateMaturity()}`;
103  }
104
105  // Nadpisanie protected metody
106  protected calculateMaturity(): string {
107    const baseMaturity = super.calculateMaturity();
108    const huntingLevel = this.huntingExperience > 10 ? "Doświadczony łowca" : "Początkujący";
109    return `${baseMaturity} - ${huntingLevel}`;
110  }
111
112  public hunt(): string {
113    this.huntingExperience++;
114    return `${this.name} poluje! Doświadczenie: ${this.huntingExperience}`;
115  }
116
117  // Nie można uzyskać dostępu do private pól z klasy bazowej
118  // public getDNA(): string {
119  //   return this.dnaSequence; // Error: private in base class
120  // }
121}
122
123const carnivoreDino = new CarnivorousDinosaur("Blue", "Velociraptor", "GCTAGCTAGCTA");
124carnivoreDino.setAge(8);
125console.log("Status dojrzałości:", carnivoreDino.getMaturityStatus());
126console.log("Polowanie:", carnivoreDino.hunt());
127
128console.log("\n");
129
130// ===========================================
131// 3. Enkapsulacja danych wrażliwych
132// ===========================================
133console.log("=== 3. ENKAPSULACJA DANYCH WRAŻLIWYCH ===");
134
135class SecureDinosaur {
136  public readonly id: string;
137  public name: string;
138
139  private dnaSequence: string;
140  private medicalHistory: string[];
141  private behaviorPatterns: Map<string, number>;
142
143  protected enclosureAccess: boolean;
144  protected emergencyProtocols: string[];
145
146  constructor(id: string, name: string, dna: string) {
147    this.id = id;
148    this.name = name;
149    this.dnaSequence = dna;
150    this.medicalHistory = [];
151    this.behaviorPatterns = new Map();
152    this.enclosureAccess = true;
153    this.emergencyProtocols = ["Tranquilizer", "Emergency Lock"];
154  }
155
156  // Kontrolowany dostęp do danych medycznych
157  public addMedicalRecord(record: string, veterinarianCode: string): boolean {
158    if (this.validateVeterinarianAccess(veterinarianCode)) {
159      this.medicalHistory.push(`[${new Date().toISOString()}] ${record}`);
160      return true;
161    }
162    console.log("Unauthorized access to medical records");
163    return false;
164  }
165
166  public getMedicalHistory(veterinarianCode: string): string[] | null {
167    if (this.validateVeterinarianAccess(veterinarianCode)) {
168      return [...this.medicalHistory]; // Kopia, nie referencja
169    }
170    console.log("Unauthorized access to medical records");
171    return null;
172  }
173
174  // Kontrolowany dostęp do wzorców zachowań
175  public updateBehaviorPattern(pattern: string, intensity: number, researcherCode: string): boolean {
176    if (this.validateResearcherAccess(researcherCode)) {
177      this.behaviorPatterns.set(pattern, intensity);
178      console.log(`Behavior pattern '${pattern}' updated to ${intensity}`);
179      return true;
180    }
181    console.log("Unauthorized access to behavior data");
182    return false;
183  }
184
185  public getBehaviorAnalysis(researcherCode: string): string | null {
186    if (this.validateResearcherAccess(researcherCode)) {
187      const patterns = Array.from(this.behaviorPatterns.entries());
188      return patterns.map(([pattern, intensity]) => `${pattern}: ${intensity}`).join(", ");
189    }
190    console.log("Unauthorized access to behavior data");
191    return null;
192  }
193
194  // Prywatne metody walidacji
195  private validateVeterinarianAccess(code: string): boolean {
196    return code === "VET-2023";
197  }
198
199  private validateResearcherAccess(code: string): boolean {
200    return code === "RESEARCH-2023";
201  }
202
203  // Publiczne metody bezpieczeństwa
204  public getPublicInfo(): object {
205    return {
206      id: this.id,
207      name: this.name,
208      hasAccess: this.enclosureAccess,
209      lastUpdate: new Date().toISOString()
210    };
211  }
212}
213
214const secureDino = new SecureDinosaur("SEC-001", "Alpha", "CLASSIFIED-DNA");
215
216console.log("Publiczne info:", secureDino.getPublicInfo());
217
218// Test kontroli dostępu
219console.log("\nTest dostępu weterynaryjnego:");
220secureDino.addMedicalRecord("Rutynowe badanie", "VET-2023"); // Autoryzowany
221secureDino.addMedicalRecord("Szczepienie", "FAKE-CODE"); // Nieautoryzowany
222
223console.log("\nTest dostępu badawczego:");
224secureDino.updateBehaviorPattern("Agresja", 7, "RESEARCH-2023"); // Autoryzowany
225secureDino.updateBehaviorPattern("Spokój", 3, "HACKER-CODE"); // Nieautoryzowany
226
227console.log("\n");
228
229// ===========================================
230// 4. Wzorzec Builder z modyfikatorami dostępu
231// ===========================================
232console.log("=== 4. WZORZEC BUILDER Z MODYFIKATORAMI ===");
233
234class DinosaurBuilder {
235  private dinosaur: {
236    name?: string;
237    species?: string;
238    diet?: string;
239    weight?: number;
240    dangerLevel?: number;
241  };
242
243  constructor() {
244    this.dinosaur = {};
245  }
246
247  public setName(name: string): DinosaurBuilder {
248    this.dinosaur.name = name;
249    return this;
250  }
251
252  public setSpecies(species: string): DinosaurBuilder {
253    this.dinosaur.species = species;
254    return this;
255  }
256
257  public setDiet(diet: string): DinosaurBuilder {
258    this.dinosaur.diet = diet;
259    return this;
260  }
261
262  public setWeight(weight: number): DinosaurBuilder {
263    this.dinosaur.weight = weight;
264    return this;
265  }
266
267  public setDangerLevel(level: number): DinosaurBuilder {
268    this.dinosaur.dangerLevel = level;
269    return this;
270  }
271
272  // Prywatna walidacja
273  private validateDinosaur(): boolean {
274    const required = ['name', 'species', 'diet', 'weight', 'dangerLevel'];
275    return required.every(field => this.dinosaur[field as keyof typeof this.dinosaur] !== undefined);
276  }
277
278  public build(): CompleteDinosaur | null {
279    if (!this.validateDinosaur()) {
280      console.log("Validation failed - missing required fields");
281      return null;
282    }
283
284    return new CompleteDinosaur(
285      this.dinosaur.name!,
286      this.dinosaur.species!,
287      this.dinosaur.diet!,
288      this.dinosaur.weight!,
289      this.dinosaur.dangerLevel!
290    );
291  }
292}
293
294class CompleteDinosaur {
295  public readonly name: string;
296  public readonly species: string;
297  private readonly diet: string;
298  private readonly weight: number;
299  private readonly dangerLevel: number;
300
301  constructor(name: string, species: string, diet: string, weight: number, dangerLevel: number) {
302    this.name = name;
303    this.species = species;
304    this.diet = diet;
305    this.weight = weight;
306    this.dangerLevel = dangerLevel;
307  }
308
309  public getPublicProfile(): object {
310    return {
311      name: this.name,
312      species: this.species
313    };
314  }
315
316  public getDetailedInfo(securityClearance: number): object | null {
317    if (securityClearance >= this.dangerLevel) {
318      return {
319        name: this.name,
320        species: this.species,
321        diet: this.diet,
322        weight: this.weight,
323        dangerLevel: this.dangerLevel
324      };
325    }
326
327    console.log(`Insufficient security clearance. Required: ${this.dangerLevel}, Provided: ${securityClearance}`);
328    return null;
329  }
330}
331
332// Użycie Builder pattern
333const builder = new DinosaurBuilder();
334const completeDino = builder
335  .setName("Indominus Rex")
336  .setSpecies("Indominus Rex")
337  .setDiet("carnivore")
338  .setWeight(5500)
339  .setDangerLevel(10)
340  .build();
341
342if (completeDino) {
343  console.log("Publiczny profil:", completeDino.getPublicProfile());
344  console.log("\nPróba dostępu z clearance 5:", completeDino.getDetailedInfo(5));
345  console.log("Próba dostępu z clearance 10:", completeDino.getDetailedInfo(10));
346}
347
348console.log("\n");
349
350// ===========================================
351// 5. System uprawnień i ról
352// ===========================================
353console.log("=== 5. SYSTEM UPRAWNIEŃ I RÓL ===");
354
355enum UserRole {
356  Visitor = 1,
357  Staff = 2,
358  Veterinarian = 3,
359  Researcher = 4,
360  Security = 5,
361  Administrator = 6
362}
363
364abstract class ParkUser {
365  protected name: string;
366  protected role: UserRole;
367  protected accessLevel: number;
368
369  constructor(name: string, role: UserRole) {
370    this.name = name;
371    this.role = role;
372    this.accessLevel = role;
373  }
374
375  public getName(): string {
376    return this.name;
377  }
378
379  public getRole(): string {
380    return UserRole[this.role];
381  }
382
383  // Abstract metoda - musi być zaimplementowana w klasach pochodnych
384  public abstract canAccess(resource: string): boolean;
385
386  protected hasMinimumAccess(requiredLevel: number): boolean {
387    return this.accessLevel >= requiredLevel;
388  }
389}
390
391class ParkStaff extends ParkUser {
392  private department: string;
393
394  constructor(name: string, role: UserRole, department: string) {
395    super(name, role);
396    this.department = department;
397  }
398
399  public canAccess(resource: string): boolean {
400    const resourcePermissions: { [key: string]: number } = {
401      "public_areas": UserRole.Visitor,
402      "staff_rooms": UserRole.Staff,
403      "dinosaur_basic_info": UserRole.Staff,
404      "medical_records": UserRole.Veterinarian,
405      "research_data": UserRole.Researcher,
406      "security_systems": UserRole.Security,
407      "admin_panel": UserRole.Administrator
408    };
409
410    const requiredLevel = resourcePermissions[resource];
411    if (requiredLevel === undefined) {
412      console.log(`Unknown resource: ${resource}`);
413      return false;
414    }
415
416    const hasAccess = this.hasMinimumAccess(requiredLevel);
417    console.log(`${this.name} (${this.getRole()}) accessing ${resource}: ${hasAccess ? 'GRANTED' : 'DENIED'}`);
418    return hasAccess;
419  }
420
421  public getDepartment(): string {
422    return this.department;
423  }
424}
425
426// Test systemu uprawnień
427const visitor = new ParkStaff("John Doe", UserRole.Visitor, "Tourist");
428const veterinarian = new ParkStaff("Dr. Sarah Connor", UserRole.Veterinarian, "Medical");
429const researcher = new ParkStaff("Dr. Alan Grant", UserRole.Researcher, "Paleontology");
430const admin = new ParkStaff("Robert Muldoon", UserRole.Administrator, "Operations");
431
432console.log("\nTest kontroli dostępu:");
433
434const resources = ["public_areas", "dinosaur_basic_info", "medical_records", "research_data", "admin_panel"];
435const users = [visitor, veterinarian, researcher, admin];
436
437users.forEach(user => {
438  console.log(`\n${user.getName()} (${user.getRole()}):`);
439  resources.forEach(resource => {
440    user.canAccess(resource);
441  });
442});
443
444console.log("\n");
445
446// ===========================================
447// 6. Gettery i settery z kontrolą dostępu
448// ===========================================
449console.log("=== 6. GETTERY I SETTERY Z KONTROLĄ DOSTĘPU ===");
450
451class MonitoredDinosaur {
452  private _name: string;
453  private _health: number;
454  private _feedingSchedule: Date[];
455  private _isQuarantined: boolean;
456  private _lastHealthCheck: Date;
457
458  constructor(name: string) {
459    this._name = name;
460    this._health = 100;
461    this._feedingSchedule = [];
462    this._isQuarantined = false;
463    this._lastHealthCheck = new Date();
464  }
465
466  // Getter z logowaniem dostępu
467  get name(): string {
468    console.log(`Access logged: Reading name of ${this._name}`);
469    return this._name;
470  }
471
472  // Getter z walidacją uprawnień
473  get health(): number {
474    console.log(`Health data accessed for ${this._name}`);
475    return this._health;
476  }
477
478  // Setter z walidacją
479  set health(value: number) {
480    if (value < 0 || value > 100) {
481      console.log(`Invalid health value: ${value}. Must be 0-100`);
482      return;
483    }
484
485    const oldHealth = this._health;
486    this._health = value;
487    this._lastHealthCheck = new Date();
488
489    console.log(`Health updated for ${this._name}: ${oldHealth} → ${value}`);
490
491    // Automatyczna kwarantanna przy niskim zdrowiu
492    if (value < 30 && !this._isQuarantined) {
493      this._isQuarantined = true;
494      console.log(`${this._name} automatically quarantined due to low health`);
495    }
496  }
497
498  // Computed property
499  get status(): string {
500    if (this._isQuarantined) return "Quarantined";
501    if (this._health < 30) return "Critical";
502    if (this._health < 60) return "Poor";
503    if (this._health < 80) return "Good";
504    return "Excellent";
505  }
506
507  // Setter z business logic
508  set isQuarantined(value: boolean) {
509    if (value !== this._isQuarantined) {
510      this._isQuarantined = value;
511      console.log(`${this._name} quarantine status: ${value ? 'ACTIVE' : 'INACTIVE'}`);
512
513      if (!value && this._health < 50) {
514        console.log(`Warning: Releasing ${this._name} from quarantine with health ${this._health}`);
515      }
516    }
517  }
518
519  get isQuarantined(): boolean {
520    return this._isQuarantined;
521  }
522
523  // Metoda z kontrolowanym dostępem do historii
524  public getFeedingHistory(accessLevel: number): Date[] | null {
525    if (accessLevel < UserRole.Staff) {
526      console.log(`Insufficient access to feeding history`);
527      return null;
528    }
529
530    return [...this._feedingSchedule];
531  }
532
533  public scheduleFeed(time: Date): void {
534    this._feedingSchedule.push(time);
535    console.log(`Feeding scheduled for ${this._name} at ${time.toLocaleTimeString()}`);
536  }
537}
538
539const monitoredDino = new MonitoredDinosaur("Alpha-7");
540
541console.log("\nTest monitorowanego dinozaura:");
542console.log("Name:", monitoredDino.name);
543console.log("Initial health:", monitoredDino.health);
544console.log("Initial status:", monitoredDino.status);
545
546// Test setterów
547monitoredDino.health = 25; // Automatyczna kwarantanna
548console.log("Status after health drop:", monitoredDino.status);
549
550monitoredDino.health = 85; // Poprawa zdrowia
551console.log("Status after recovery:", monitoredDino.status);
552
553monitoredDino.isQuarantined = false; // Ręczne zwolnienie z kwarantanny
554
555// Test planowania karmienia
556monitoredDino.scheduleFeed(new Date(Date.now() + 3600000)); // Za godzinę
557
558console.log("\n");
559
560// ===========================================
561// 7. Podsumowanie modyfikatorów dostępu
562// ===========================================
563console.log("=== 7. PODSUMOWANIE MODYFIKATORÓW DOSTĘPU ===");
564
565console.log("PUBLIC:");
566console.log("  • Dostępne z każdego miejsca");
567console.log("  • Domyślny modyfikator w TypeScript");
568console.log("  • Używaj do API publicznego klasy\n");
569
570console.log("PRIVATE:");
571console.log("  • Dostępne tylko w tej samej klasie");
572console.log("  • Enkapsulacja implementacji");
573console.log("  • Ochrona danych wrażliwych\n");
574
575console.log("PROTECTED:");
576console.log("  • Dostępne w klasie i klasach dziedziczących");
577console.log("  • Współdzielenie między hierarchią klas");
578console.log("  • Kontrolowany dostęp w dziedziczeniu\n");
579
580console.log("READONLY:");
581console.log("  • Można przypisać tylko w konstruktorze");
582console.log("  • Ochrona przed modyfikacją");
583console.log("  • Immutable fields\n");
584
585console.log("Modyfikatory dostępu zapewniają bezpieczeństwo w Parku Jurajskim!");

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Który modyfikator dostępu w TypeScript pozwala na dostęp do właściwości tylko wewnątrz klasy i jej klas pochodnych?

Przydatne artykuły