Kurs JavaScript i TypeScript · Moduł 7: Zaawansowany TypeScript
Modyfikatory dostępu (public, private, protected)
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:
- public - dostęp z dowolnego miejsca w kodzie (domyślny)
- private - dostęp tylko wewnątrz danej klasy
- 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 metodyWł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 privatePrywatne 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 protectedChronione 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 privateTa 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:
- Abstrakcyjna klasa bazowa (ZwierzęParku) definiuje wspólne właściwości i zachowania dla wszystkich zwierząt w parku
- Prywatne właściwości (_id, _dataUtworzenia, _ostatniaAktualizacja) są dostępne tylko wewnątrz klasy
- Chronione właściwości (_gatunek, _wiek, _waga, _status) są dostępne w klasie bazowej i klasach dziedziczących
- Publiczne gettery i settery zapewniają kontrolowany dostęp do właściwości
- Prywatne metody (logZmianyStatusu, walidujPoziomAgresji) są używane jako pomocnicze implementacje dostępne tylko wewnątrz klasy
- Chronione metody (aktualizujWagę) są dostępne dla klas dziedziczących
- Abstrakcyjna metoda (pobierzOpisKliniczny) wymusza implementację w klasach dziedziczących
- 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:
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
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
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. Który modyfikator dostępu w TypeScript pozwala na dostęp do właściwości tylko wewnątrz klasy i jej klas pochodnych?