"W Parku Jurajskim mamy protokoły i standardy, które obowiązują wszystkie obiekty danej klasy," wyjaśnia Dr. Henry Wu, główny genetyk parku. "Na przykład, wszystkie nasze wybiegi dla dinozaurów mają tę samą procedurę bezpieczeństwa, niezależnie od tego, który konkretny wybieg rozpatrujemy. Z drugiej strony, każdy gatunek dinozaura ma swój własny, unikalny wzór DNA, ale wszyscy wiemy, że DNA musi istnieć - nawet jeśli szczegóły różnią się między gatunkami."
W programowaniu obiektowym TypeScript oferuje dwie potężne koncepcje, które odzwierciedlają te idee:
W tym ćwiczeniu zagłębimy się w te koncepcje, tworząc zaawansowany system zarządzania genetyką i protokołami bezpieczeństwa Parku Jurajskiego.
Elementy statyczne w klasie należą do samej klasy, a nie do jej instancji. Oznacza to, że:
Przyjrzyjmy się, jak można wykorzystać elementy statyczne w kontekście Parku Jurajskiego:
1class ProtokółBezpieczeństwa {
2 // Właściwość statyczna - współdzielona przez wszystkie instancje
3 static readonly POZIOM_KRYTYCZNY: number = 5;
4 static readonly POZIOM_WYSOKI: number = 4;
5 static readonly POZIOM_ŚREDNI: number = 3;
6 static readonly POZIOM_NISKI: number = 2;
7 static readonly POZIOM_MINIMALNY: number = 1;
8
9 // Statyczna właściwość przechowująca aktualny globalny poziom zagrożenia parku
10 private static _aktualnyPoziomZagrożenia: number = ProtokółBezpieczeństwa.POZIOM_NISKI;
11
12 // Właściwości instancji
13 private readonly obszar: string;
14 private lokalnyPoziomZagrożenia: number;
15
16 constructor(obszar: string) {
17 this.obszar = obszar;
18 this.lokalnyPoziomZagrożenia = ProtokółBezpieczeństwa._aktualnyPoziomZagrożenia;
19 console.log(`Utworzono protokół bezpieczeństwa dla obszaru: ${obszar}`);
20 }
21
22 // Metoda statyczna dostępna bezpośrednio na klasie
23 static podnieśGlobalnyPoziomZagrożenia(): void {
24 if (this._aktualnyPoziomZagrożenia < this.POZIOM_KRYTYCZNY) {
25 this._aktualnyPoziomZagrożenia++;
26 console.log(`UWAGA: Podniesiono globalny poziom zagrożenia do ${this._aktualnyPoziomZagrożenia}`);
27
28 // Wywołanie innej metody statycznej
29 this.powiadomOpoziomieZagrożenia();
30 } else {
31 console.log("UWAGA: Osiągnięto już maksymalny poziom zagrożenia!");
32 }
33 }
34
35 // Inna metoda statyczna
36 static obniżGlobalnyPoziomZagrożenia(): void {
37 if (this._aktualnyPoziomZagrożenia > this.POZIOM_MINIMALNY) {
38 this._aktualnyPoziomZagrożenia--;
39 console.log(`Obniżono globalny poziom zagrożenia do ${this._aktualnyPoziomZagrożenia}`);
40 } else {
41 console.log("Poziom zagrożenia jest już minimalny.");
42 }
43 }
44
45 // Statyczny getter
46 static get aktualnyPoziomZagrożenia(): number {
47 return this._aktualnyPoziomZagrożenia;
48 }
49
50 // Prywatna metoda statyczna
51 private static powiadomOpoziomieZagrożenia(): void {
52 console.log(`Powiadomienie: Aktualny poziom zagrożenia parku: ${this._aktualnyPoziomZagrożenia}`);
53 }
54
55 // Metoda instancji korzystająca z właściwości statycznych
56 aktualizujLokalnyPoziomZagrożenia(): void {
57 // Aktualizacja lokalnego poziomu na podstawie globalnego
58 this.lokalnyPoziomZagrożenia = ProtokółBezpieczeństwa._aktualnyPoziomZagrożenia;
59 console.log(`Obszar ${this.obszar}: zaktualizowano poziom zagrożenia do ${this.lokalnyPoziomZagrożenia}`);
60 }
61
62 get statusBezpieczeństwa(): string {
63 const poziomy = [
64 "NIEZNANY",
65 "MINIMALNY",
66 "NISKI",
67 "ŚREDNI",
68 "WYSOKI",
69 "KRYTYCZNY"
70 ];
71
72 return `Obszar: ${this.obszar}, Poziom zagrożenia: ${poziomy[this.lokalnyPoziomZagrożenia]}`;
73 }
74}
75
76// Użycie właściwości i metod statycznych - bez tworzenia instancji!
77console.log("Domyślny poziom zagrożenia:", ProtokółBezpieczeństwa.aktualnyPoziomZagrożenia);
78
79// Wywołanie metody statycznej bezpośrednio na klasie
80ProtokółBezpieczeństwa.podnieśGlobalnyPoziomZagrożenia();
81ProtokółBezpieczeństwa.podnieśGlobalnyPoziomZagrożenia();
82
83// Tworzenie instancji klasy
84const protokółCentralny = new ProtokółBezpieczeństwa("Centrum Operacyjne");
85const protokółWybieg = new ProtokółBezpieczeństwa("Wybieg T-Rex");
86
87// Wyświetlenie statusu bezpieczeństwa
88console.log(protokółCentralny.statusBezpieczeństwa);
89console.log(protokółWybieg.statusBezpieczeństwa);
90
91// Ponowne podniesienie globalnego poziomu zagrożenia
92ProtokółBezpieczeństwa.podnieśGlobalnyPoziomZagrożenia();
93
94// Aktualizacja lokalnych poziomów zagrożenia
95protokółCentralny.aktualizujLokalnyPoziomZagrożenia();
96protokółWybieg.aktualizujLokalnyPoziomZagrożenia();
97
98// Sprawdzenie zaktualizowanych statusów
99console.log(protokółCentralny.statusBezpieczeństwa);
100console.log(protokółWybieg.statusBezpieczeństwa);W powyższym przykładzie:
_aktualnyPoziomZagrożenia jest współdzielona przez wszystkie instancjeElementy statyczne są szczególnie przydatne w następujących przypadkach:
Oto bardziej rozbudowany przykład pokazujący różne zastosowania elementów statycznych:
1class GenetycznyRejestr {
2 // Stałe statyczne
3 static readonly BAZA_NUKLEOTYDÓW = ["A", "C", "G", "T"];
4 static readonly MIN_DŁUGOŚĆ_DNA = 1000000;
5
6 // Stan globalny
7 private static _liczbaZarejestrowanychGatunków: number = 0;
8 private static _rejestracjeGatunków: Map<string, string> = new Map();
9
10 // Właściwości instancji
11 private id: string;
12 private sekwencjaDNA: string;
13 private nazwa: string;
14
15 constructor(nazwa: string, sekwencjaDNA: string) {
16 if (sekwencjaDNA.length < GenetycznyRejestr.MIN_DŁUGOŚĆ_DNA) {
17 throw new Error(`Sekwencja DNA jest za krótka. Wymagane minimum: ${GenetycznyRejestr.MIN_DŁUGOŚĆ_DNA} par zasad.`);
18 }
19
20 this.nazwa = nazwa;
21 this.sekwencjaDNA = sekwencjaDNA;
22 this.id = GenetycznyRejestr.wygenerujId(nazwa);
23
24 // Rejestracja nowego gatunku
25 GenetycznyRejestr._rejestracjeGatunków.set(this.id, nazwa);
26 GenetycznyRejestr._liczbaZarejestrowanychGatunków++;
27 }
28
29 // Metoda narzędziowa - tworzy ID na podstawie nazwy
30 private static wygenerujId(nazwa: string): string {
31 const timestamp = Date.now();
32 const cleanName = nazwa.toLowerCase().replace(/[^a-z0-9]/g, "");
33 return `DNO-${cleanName}-${timestamp}`;
34 }
35
36 // Metoda fabryczna - tworzy instancję z losowym DNA
37 static utwórzTestowyGatunek(nazwa: string): GenetycznyRejestr {
38 const długośćDNA = this.MIN_DŁUGOŚĆ_DNA + Math.floor(Math.random() * 500000);
39 let sekwencja = "";
40
41 for (let i = 0; i < długośćDNA; i++) {
42 const randomIndex = Math.floor(Math.random() * this.BAZA_NUKLEOTYDÓW.length);
43 sekwencja += this.BAZA_NUKLEOTYDÓW[randomIndex];
44 }
45
46 return new GenetycznyRejestr(nazwa, sekwencja);
47 }
48
49 // Metoda narzędziowa - weryfikuje poprawność sekwencji DNA
50 static weryfikujSekwencję(sekwencja: string): boolean {
51 if (sekwencja.length < this.MIN_DŁUGOŚĆ_DNA) return false;
52
53 // Sprawdza, czy sekwencja zawiera tylko dozwolone nukleotydy
54 for (const nukleotyd of sekwencja) {
55 if (!this.BAZA_NUKLEOTYDÓW.includes(nukleotyd)) {
56 return false;
57 }
58 }
59
60 return true;
61 }
62
63 // Getter dla statycznej właściwości
64 static get liczbaZarejestrowanychGatunków(): number {
65 return this._liczbaZarejestrowanychGatunków;
66 }
67
68 // Statyczna metoda zwracająca wszystkie zarejestrowane gatunki
69 static pobierzWszystkieGatunki(): string[] {
70 return Array.from(this._rejestracjeGatunków.values());
71 }
72
73 // Instancyjna metoda używająca statycznych pól
74 analizujSekwencję(): { proporcje: Record<string, number>, długość: number } {
75 const proporcje: Record<string, number> = {};
76
77 // Inicjalizacja liczników dla każdego nukleotydu
78 GenetycznyRejestr.BAZA_NUKLEOTYDÓW.forEach(n => {
79 proporcje[n] = 0;
80 });
81
82 // Zliczanie nukleotydów
83 for (const nukleotyd of this.sekwencjaDNA) {
84 proporcje[nukleotyd]++;
85 }
86
87 // Konwersja na procenty
88 GenetycznyRejestr.BAZA_NUKLEOTYDÓW.forEach(n => {
89 proporcje[n] = Number((proporcje[n] / this.sekwencjaDNA.length * 100).toFixed(2));
90 });
91
92 return {
93 proporcje,
94 długość: this.sekwencjaDNA.length
95 };
96 }
97
98 // Gettery instancji
99 get identyfikator(): string {
100 return this.id;
101 }
102
103 get gatunekNazwa(): string {
104 return this.nazwa;
105 }
106}
107
108// Korzystanie z metod i właściwości statycznych
109console.log("Minimalna długość DNA:", GenetycznyRejestr.MIN_DŁUGOŚĆ_DNA);
110console.log("Dostępne nukleotydy:", GenetycznyRejestr.BAZA_NUKLEOTYDÓW.join(", "));
111
112// Weryfikacja przykładowej sekwencji
113const przykładowaSekwencja = "ACGTACGTACGT".repeat(100000); // Powielenie, aby osiągnąć min. długość
114console.log("Weryfikacja sekwencji:", GenetycznyRejestr.weryfikujSekwencję(przykładowaSekwencja));
115
116// Tworzenie instancji za pomocą metody fabrycznej
117const trex = GenetycznyRejestr.utwórzTestowyGatunek("Tyrannosaurus Rex");
118const raptor = GenetycznyRejestr.utwórzTestowyGatunek("Velociraptor");
119const trike = GenetycznyRejestr.utwórzTestowyGatunek("Triceratops");
120
121// Sprawdzenie, ile gatunków zostało zarejestrowanych
122console.log("Liczba zarejestrowanych gatunków:", GenetycznyRejestr.liczbaZarejestrowanychGatunków);
123console.log("Lista gatunków:", GenetycznyRejestr.pobierzWszystkieGatunki());
124
125// Użycie metod instancji
126const analizaTrex = trex.analizujSekwencję();
127console.log(`Analiza ${trex.gatunekNazwa} (ID: ${trex.identyfikator}):`);
128console.log(`Długość sekwencji: ${analizaTrex.długość} par zasad`);
129console.log("Proporcje nukleotydów:", analizaTrex.proporcje);Ten przykład pokazuje:
BAZA_NUKLEOTYDÓW, MIN_DŁUGOŚĆ_DNA)weryfikujSekwencję i wygenerujIdutwórzTestowyGatunek tworzącą instancje z losowymi danymiKlasy abstrakcyjne w TypeScript definiują szablon, który musi być zaimplementowany przez klasy dziedziczące. Nie można tworzyć instancji klasy abstrakcyjnej bezpośrednio - służy ona jako bazowy "wzorzec" dla innych klas.
Metody abstrakcyjne, określone w klasie abstrakcyjnej, muszą być zaimplementowane przez klasy dziedziczące. Definiują one kontrakt, określający, jakie metody muszą być dostępne, ale nie zawierają własnej implementacji.
Zobaczmy, jak wykorzystać klasy abstrakcyjne w kontekście Parku Jurajskiego:
1// Abstrakcyjna klasa bazowa dla wszystkich dinozaurów
2abstract class Dinozaur {
3 // Właściwości wspólne dla wszystkich dinozaurów
4 private _id: string;
5 protected _nazwa: string;
6 protected _gatunek: string;
7 protected _wiek: number;
8 protected _waga: number;
9
10 constructor(id: string, nazwa: string, gatunek: string, wiek: number, waga: number) {
11 this._id = id;
12 this._nazwa = nazwa;
13 this._gatunek = gatunek;
14 this._wiek = wiek;
15 this._waga = waga;
16 }
17
18 // Gettery
19 get id(): string { return this._id; }
20 get nazwa(): string { return this._nazwa; }
21 get gatunek(): string { return this._gatunek; }
22 get wiek(): number { return this._wiek; }
23 get waga(): number { return this._waga; }
24
25 // Metody implementowane we wszystkich klasach pochodnych
26 abstract obliczZapotrzebowanieKaloryczne(): number;
27 abstract określPoziomZagrożenia(): number;
28
29 // Abstrakcyjna właściwość - musi być zaimplementowana w klasach pochodnych
30 abstract get typDiety(): string;
31
32 // Konkretna metoda (nie abstrakcyjna) - dostępna we wszystkich klasach pochodnych
33 pobierzPodstawoweDane(): string {
34 return `${this._nazwa} (${this._gatunek}) - ID: ${this._id}, Wiek: ${this._wiek} lat, Waga: ${this._waga} kg`;
35 }
36
37 // Metoda korzystająca z metod abstrakcyjnych
38 wygenerujRaportDinozaura(): string {
39 const kalorie = this.obliczZapotrzebowanieKaloryczne();
40 const poziomZagrożenia = this.określPoziomZagrożenia();
41
42 return `
43=== RAPORT DINOZAURA ===
44${this.pobierzPodstawoweDane()}
45Typ diety: ${this.typDiety}
46Dzienne zapotrzebowanie kaloryczne: ${kalorie} kcal
47Poziom zagrożenia: ${poziomZagrożenia}/10
48======================
49 `;
50 }
51}
52
53// Klasa dziedzicząca po klasie abstrakcyjnej
54class DrapieżnyDinozaur extends Dinozaur {
55 private _siłaUgryzienia: number;
56 private _prędkośćBiegu: number;
57
58 constructor(
59 id: string,
60 nazwa: string,
61 gatunek: string,
62 wiek: number,
63 waga: number,
64 siłaUgryzienia: number,
65 prędkośćBiegu: number
66 ) {
67 super(id, nazwa, gatunek, wiek, waga);
68 this._siłaUgryzienia = siłaUgryzienia;
69 this._prędkośćBiegu = prędkośćBiegu;
70 }
71
72 // Gettery
73 get siłaUgryzienia(): number { return this._siłaUgryzienia; }
74 get prędkośćBiegu(): number { return this._prędkośćBiegu; }
75
76 // Implementacja abstrakcyjnej właściwości
77 get typDiety(): string {
78 return "mięsożerny";
79 }
80
81 // Implementacje metod abstrakcyjnych
82 obliczZapotrzebowanieKaloryczne(): number {
83 // Dla drapieżników używamy złożonego wzoru
84 return Math.round(this._waga * 0.6 * (this._prędkośćBiegu * 0.2));
85 }
86
87 określPoziomZagrożenia(): number {
88 // Poziom zagrożenia zależy od siły ugryzienia, prędkości i wagi
89 const bazowy = (this._siłaUgryzienia * 0.7 + this._prędkośćBiegu * 0.3) / 100;
90 return Math.min(10, Math.max(1, Math.round(bazowy)));
91 }
92
93 // Metoda specyficzna dla drapieżników
94 poluj(): string {
95 return `${this._nazwa} poluje z prędkością ${this._prędkośćBiegu} km/h i siłą ugryzienia ${this._siłaUgryzienia} PSI`;
96 }
97}
98
99// Inna klasa dziedzicząca
100class RoślinożernyDinozaur extends Dinozaur {
101 private _długośćSzyi: number;
102 private _typPancerza: "brak" | "lekki" | "średni" | "ciężki";
103
104 constructor(
105 id: string,
106 nazwa: string,
107 gatunek: string,
108 wiek: number,
109 waga: number,
110 długośćSzyi: number,
111 typPancerza: "brak" | "lekki" | "średni" | "ciężki"
112 ) {
113 super(id, nazwa, gatunek, wiek, waga);
114 this._długośćSzyi = długośćSzyi;
115 this._typPancerza = typPancerza;
116 }
117
118 // Gettery
119 get długośćSzyi(): number { return this._długośćSzyi; }
120 get typPancerza(): string { return this._typPancerza; }
121
122 // Implementacja abstrakcyjnej właściwości
123 get typDiety(): string {
124 return "roślinożerny";
125 }
126
127 // Implementacje metod abstrakcyjnych
128 obliczZapotrzebowanieKaloryczne(): number {
129 // Dla roślinożerców używamy prostszego wzoru - potrzebują więcej kalorii na kilogram
130 return Math.round(this._waga * 0.8);
131 }
132
133 określPoziomZagrożenia(): number {
134 // Poziom zagrożenia jest niższy dla roślinożerców, ale zależy od wagi i typu pancerza
135 let bazowy = this._waga / 1000;
136
137 // Modyfikator zależny od pancerza
138 const modyfikatoryPancerza = {
139 "brak": 0,
140 "lekki": 1,
141 "średni": 2,
142 "ciężki": 3
143 };
144
145 bazowy += modyfikatoryPancerza[this._typPancerza];
146
147 return Math.min(7, Math.max(1, Math.round(bazowy))); // Maks. 7 dla roślinożerców
148 }
149
150 // Metoda specyficzna dla roślinożerców
151 pasięSię(): string {
152 const wysokośćSięgania = this._długośćSzyi > 2 ? "wysokich" : "niskich";
153 return `${this._nazwa} pasie się, zjadając rośliny z ${wysokośćSięgania} partii drzew`;
154 }
155}
156
157// Nie możemy utworzyć instancji klasy abstrakcyjnej
158// const nieprawidłowy = new Dinozaur("ID", "Nazwa", "Gatunek", 1, 100); // Błąd!
159
160// Tworzenie instancji klas pochodnych
161const trex = new DrapieżnyDinozaur("T-REX-1", "Rex", "Tyrannosaurus", 8, 7000, 12800, 32);
162const stegozaur = new RoślinożernyDinozaur("STEG-1", "Spike", "Stegosaurus", 12, 5000, 1.2, "ciężki");
163const diplodok = new RoślinożernyDinozaur("DIPL-1", "Dippy", "Diplodocus", 15, 16000, 6.5, "brak");
164
165// Korzystanie z metod zaimplementowanych w klasach pochodnych
166console.log(trex.wygenerujRaportDinozaura());
167console.log(stegozaur.wygenerujRaportDinozaura());
168console.log(diplodok.wygenerujRaportDinozaura());
169
170// Korzystanie z metod specyficznych dla danej klasy
171console.log(trex.poluj());
172console.log(stegozaur.pasięSię());
173
174// Dla obu typów dinozaurów możemy korzystać z metod klasy bazowej
175console.log(trex.pobierzPodstawoweDane());
176console.log(diplodok.pobierzPodstawoweDane());W powyższym przykładzie:
Dinozaur to klasa abstrakcyjna zawierająca wspólne właściwości i metodyobliczZapotrzebowanieKaloryczne() i określPoziomZagrożenia() muszą być zaimplementowane przez klasy dziedziczącetypDiety musi być zdefiniowana w klasach pochodnychDrapieżnyDinozaur i RoślinożernyDinozaur implementują abstrakcyjne metody i właściwościDinozaur bezpośrednioKlasy abstrakcyjne są szczególnie przydatne w następujących przypadkach:
Zobaczmy bardziej zaawansowany przykład, łączący abstrakcyjne klasy i metody z elementami statycznymi w systemie zarządzania protokołami bezpieczeństwa Parku Jurajskiego:
1// Typy pomocnicze
2type PoziomAlertów = "normalny" | "podwyższony" | "wysoki" | "krytyczny";
3type StatusSystem = "aktywny" | "nieaktywny" | "w_konserwacji" | "awaria";
4
5// Interfejs dla systemów monitorujących
6interface SystemMonitorujący {
7 rozpocznijMonitorowanie(): void;
8 zakończMonitorowanie(): void;
9 pobierzAktualnyStatus(): StatusSystem;
10}
11
12// Abstrakcyjna klasa bazowa dla wszystkich systemów bezpieczeństwa
13abstract class SystemBezpieczeństwa implements SystemMonitorujący {
14 // Statyczne właściwości - dla wszystkich systemów bezpieczeństwa
15 private static _poziomAlertówParku: PoziomAlertów = "normalny";
16 private static _aktywneSystemy: Set<string> = new Set();
17 private static _incydenty: Array<{data: Date, typSystemu: string, lokalizacja: string, opis: string}> = [];
18
19 // Zwykłe właściwości
20 protected _id: string;
21 protected _lokalizacja: string;
22 protected _status: StatusSystem;
23 protected _czyMonitorowany: boolean;
24 protected _dataPrzeglądu: Date;
25
26 constructor(id: string, lokalizacja: string) {
27 this._id = id;
28 this._lokalizacja = lokalizacja;
29 this._status = "nieaktywny";
30 this._czyMonitorowany = false;
31 this._dataPrzeglądu = new Date();
32
33 // Rejestracja systemu
34 SystemBezpieczeństwa.zarejestrujSystem(id, this.constructor.name);
35 }
36
37 // Statyczne metody
38 static zarejestrujSystem(id: string, typSystemu: string): void {
39 console.log(`Zarejestrowano nowy system bezpieczeństwa: ${typSystemu} (ID: ${id})`);
40 }
41
42 static zgłośIncydent(typSystemu: string, lokalizacja: string, opis: string): void {
43 const incydent = {
44 data: new Date(),
45 typSystemu,
46 lokalizacja,
47 opis
48 };
49
50 this._incydenty.push(incydent);
51 console.log(`ALERT: Zgłoszono incydent w lokalizacji ${lokalizacja}: ${opis}`);
52
53 // Automatycznie podnosimy poziom alertów
54 if (this._poziomAlertówParku === "normalny") {
55 this.zmieńPoziomAlertów("podwyższony");
56 } else if (this._poziomAlertówParku === "podwyższony") {
57 this.zmieńPoziomAlertów("wysoki");
58 } else if (this._poziomAlertówParku === "wysoki") {
59 this.zmieńPoziomAlertów("krytyczny");
60 }
61 }
62
63 static zmieńPoziomAlertów(nowyPoziom: PoziomAlertów): void {
64 this._poziomAlertówParku = nowyPoziom;
65 console.log(`UWAGA: Zmieniono poziom alertów parku na: ${nowyPoziom.toUpperCase()}`);
66 }
67
68 static get poziomAlertów(): PoziomAlertów {
69 return this._poziomAlertówParku;
70 }
71
72 static get ostatnieIncydenty(): Array<{data: Date, typSystemu: string, lokalizacja: string, opis: string}> {
73 // Zwracamy 5 ostatnich incydentów
74 return [...this._incydenty].slice(-5);
75 }
76
77 // Gettery instancji
78 get id(): string { return this._id; }
79 get lokalizacja(): string { return this._lokalizacja; }
80 get status(): StatusSystem { return this._status; }
81
82 // Implementacja interfejsu SystemMonitorujący
83 rozpocznijMonitorowanie(): void {
84 this._czyMonitorowany = true;
85 SystemBezpieczeństwa._aktywneSystemy.add(this._id);
86 console.log(`Rozpoczęto monitorowanie systemu ${this.constructor.name} w lokalizacji ${this._lokalizacja}`);
87 }
88
89 zakończMonitorowanie(): void {
90 this._czyMonitorowany = false;
91 SystemBezpieczeństwa._aktywneSystemy.delete(this._id);
92 console.log(`Zakończono monitorowanie systemu ${this.constructor.name} w lokalizacji ${this._lokalizacja}`);
93 }
94
95 pobierzAktualnyStatus(): StatusSystem {
96 return this._status;
97 }
98
99 // Abstrakcyjne metody, które muszą być zaimplementowane
100 abstract uruchom(): void;
101 abstract zatrzymaj(): void;
102 abstract wykonajPrzegląd(): void;
103
104 // Abstrakcyjne właściwości
105 abstract get typSystemu(): string;
106 abstract get poziomPriorytetuAlarmowego(): number; // 1-10
107
108 // Metoda wykorzystująca abstrakcyjne właściwości i metody
109 wygenerujRaportStatusu(): string {
110 return `
111=== RAPORT SYSTEMU BEZPIECZEŃSTWA ===
112ID: ${this._id}
113Typ: ${this.typSystemu}
114Lokalizacja: ${this._lokalizacja}
115Status: ${this._status}
116Monitorowany: ${this._czyMonitorowany ? "TAK" : "NIE"}
117Priorytet alarmowy: ${this.poziomPriorytetuAlarmowego}/10
118Ostatni przegląd: ${this._dataPrzeglądu.toLocaleDateString()}
119Globalny poziom alertów: ${SystemBezpieczeństwa._poziomAlertówParku.toUpperCase()}
120=================================
121 `;
122 }
123}
124
125// Klasa pochodna - System Ogrodzenia Elektrycznego
126class SystemOgrodzeniaElektrycznego extends SystemBezpieczeństwa {
127 private _napięcie: number; // w woltach
128 private _długośćOgrodzenia: number; // w metrach
129 private _typZabezpieczenia: "standardowe" | "wzmocnione" | "ultrawzmocnione";
130
131 constructor(
132 id: string,
133 lokalizacja: string,
134 długośćOgrodzenia: number,
135 typZabezpieczenia: "standardowe" | "wzmocnione" | "ultrawzmocnione"
136 ) {
137 super(id, lokalizacja);
138 this._napięcie = 0;
139 this._długośćOgrodzenia = długośćOgrodzenia;
140 this._typZabezpieczenia = typZabezpieczenia;
141 }
142
143 // Implementacja abstrakcyjnych właściwości
144 get typSystemu(): string {
145 return "Ogrodzenie Elektryczne";
146 }
147
148 get poziomPriorytetuAlarmowego(): number {
149 // Priorytet zależy od typu zabezpieczenia
150 const bazowy = this._typZabezpieczenia === "standardowe" ? 7 :
151 this._typZabezpieczenia === "wzmocnione" ? 8 : 10;
152
153 // Uwzględniamy długość ogrodzenia - dłuższe mają wyższy priorytet
154 const modyfikator = Math.min(2, Math.floor(this._długośćOgrodzenia / 1000));
155
156 return Math.min(10, bazowy + modyfikator);
157 }
158
159 // Implementacja abstrakcyjnych metod
160 uruchom(): void {
161 if (this._status === "w_konserwacji") {
162 console.log(`BŁĄD: Nie można uruchomić systemu ${this._id} - jest w trakcie konserwacji`);
163 return;
164 }
165
166 this._status = "aktywny";
167
168 // Napięcie zależy od typu zabezpieczenia
169 this._napięcie = this._typZabezpieczenia === "standardowe" ? 10000 :
170 this._typZabezpieczenia === "wzmocnione" ? 20000 : 30000;
171
172 console.log(`Uruchomiono system ogrodzenia ${this._id} w lokalizacji ${this._lokalizacja}`);
173 console.log(`Napięcie: ${this._napięcie}V, Długość: ${this._długośćOgrodzenia}m`);
174 }
175
176 zatrzymaj(): void {
177 this._status = "nieaktywny";
178 this._napięcie = 0;
179 console.log(`Zatrzymano system ogrodzenia ${this._id} w lokalizacji ${this._lokalizacja}`);
180
181 // Zgłoszenie incydentu przy wysokim poziomie alertów
182 if (SystemBezpieczeństwa.poziomAlertów === "wysoki" || SystemBezpieczeństwa.poziomAlertów === "krytyczny") {
183 SystemBezpieczeństwa.zgłośIncydent(
184 this.typSystemu,
185 this._lokalizacja,
186 `Wyłączenie ogrodzenia przy wysokim poziomie alertów!`
187 );
188 }
189 }
190
191 wykonajPrzegląd(): void {
192 const poprzedniStatus = this._status;
193 this._status = "w_konserwacji";
194 console.log(`Rozpoczęto przegląd systemu ogrodzenia ${this._id}`);
195
196 // Symulacja przeglądu
197 setTimeout(() => {
198 this._dataPrzeglądu = new Date();
199 this._status = poprzedniStatus;
200 console.log(`Zakończono przegląd systemu ogrodzenia ${this._id}`);
201 }, 2000);
202 }
203
204 // Metody specyficzne dla tego typu systemu
205 zmieńNapięcie(noweNapięcie: number): void {
206 if (this._status !== "aktywny") {
207 console.log(`BŁĄD: Nie można zmienić napięcia - system nie jest aktywny`);
208 return;
209 }
210
211 this._napięcie = noweNapięcie;
212 console.log(`Zmieniono napięcie ogrodzenia ${this._id} na ${noweNapięcie}V`);
213 }
214
215 zgłośPrzerwęWOgrodzeniu(): void {
216 this._status = "awaria";
217 SystemBezpieczeństwa.zgłośIncydent(
218 this.typSystemu,
219 this._lokalizacja,
220 `Wykryto przerwę w ogrodzeniu elektrycznym!`
221 );
222 }
223}
224
225// Inna klasa pochodna - System Monitoringu Wideo
226class SystemMonitoringuWideo extends SystemBezpieczeństwa {
227 private _liczbaKamer: number;
228 private _rozdzielczość: "SD" | "HD" | "4K";
229 private _trybNocny: boolean;
230
231 constructor(
232 id: string,
233 lokalizacja: string,
234 liczbaKamer: number,
235 rozdzielczość: "SD" | "HD" | "4K"
236 ) {
237 super(id, lokalizacja);
238 this._liczbaKamer = liczbaKamer;
239 this._rozdzielczość = rozdzielczość;
240 this._trybNocny = false;
241 }
242
243 // Implementacja abstrakcyjnych właściwości
244 get typSystemu(): string {
245 return "Monitoring Wideo";
246 }
247
248 get poziomPriorytetuAlarmowego(): number {
249 // Priorytet zależy od liczby kamer i rozdzielczości
250 const bazowy = 5;
251
252 // Modyfikator rozdzielczości
253 const modyfikatorRozdzielczości =
254 this._rozdzielczość === "SD" ? 0 :
255 this._rozdzielczość === "HD" ? 1 : 2;
256
257 // Modyfikator liczby kamer
258 const modyfikatorKamer = Math.min(2, Math.floor(this._liczbaKamer / 10));
259
260 return Math.min(10, bazowy + modyfikatorRozdzielczości + modyfikatorKamer);
261 }
262
263 // Implementacja abstrakcyjnych metod
264 uruchom(): void {
265 this._status = "aktywny";
266 console.log(`Uruchomiono system monitoringu ${this._id} w lokalizacji ${this._lokalizacja}`);
267 console.log(`Liczba kamer: ${this._liczbaKamer}, Rozdzielczość: ${this._rozdzielczość}`);
268 }
269
270 zatrzymaj(): void {
271 this._status = "nieaktywny";
272 console.log(`Zatrzymano system monitoringu ${this._id} w lokalizacji ${this._lokalizacja}`);
273 }
274
275 wykonajPrzegląd(): void {
276 this._status = "w_konserwacji";
277 console.log(`Rozpoczęto przegląd systemu monitoringu ${this._id}`);
278
279 // W tym przypadku przegląd kończy się natychmiast
280 this._dataPrzeglądu = new Date();
281 this._status = "aktywny";
282 console.log(`Zakończono przegląd systemu monitoringu ${this._id}`);
283 }
284
285 // Metody specyficzne dla tego typu systemu
286 włączTrybNocny(): void {
287 this._trybNocny = true;
288 console.log(`Włączono tryb nocny w systemie monitoringu ${this._id}`);
289 }
290
291 wyłączTrybNocny(): void {
292 this._trybNocny = false;
293 console.log(`Wyłączono tryb nocny w systemie monitoringu ${this._id}`);
294 }
295
296 wykryjRuch(obszar: string): void {
297 console.log(`System ${this._id}: Wykryto ruch w obszarze ${obszar}`);
298
299 if (SystemBezpieczeństwa.poziomAlertów !== "normalny") {
300 SystemBezpieczeństwa.zgłośIncydent(
301 this.typSystemu,
302 `${this._lokalizacja} - obszar ${obszar}`,
303 `Wykryto nieautoryzowany ruch!`
304 );
305 }
306 }
307}
308
309// Demonstracja systemu bezpieczeństwa Parku Jurajskiego
310function testSystemuBezpieczeństwa() {
311 console.log("=== INICJALIZACJA SYSTEMÓW BEZPIECZEŃSTWA PARKU JURAJSKIEGO ===");
312
313 // Tworzenie systemów ogrodzenia
314 const ogrodzenieGłówne = new SystemOgrodzeniaElektrycznego("FNC-001", "Sektor A - Wejście główne", 2500, "ultrawzmocnione");
315 const ogrodzenieTRex = new SystemOgrodzeniaElektrycznego("FNC-002", "Sektor B - Wybieg T-Rex", 1200, "ultrawzmocnione");
316 const ogrodzenieRaptory = new SystemOgrodzeniaElektrycznego("FNC-003", "Sektor C - Wybieg Raptorów", 800, "wzmocnione");
317
318 // Tworzenie systemów monitoringu
319 const monitoringGłówny = new SystemMonitoringuWideo("CAM-001", "Centrum kontroli", 20, "4K");
320 const monitoringTRex = new SystemMonitoringuWideo("CAM-002", "Sektor B - Wybieg T-Rex", 12, "HD");
321 const monitoringRaptory = new SystemMonitoringuWideo("CAM-003", "Sektor C - Wybieg Raptorów", 8, "HD");
322
323 // Uruchamianie wszystkich systemów
324 console.log("\n=== URUCHAMIANIE SYSTEMÓW ===");
325 ogrodzenieGłówne.uruchom();
326 ogrodzenieTRex.uruchom();
327 ogrodzenieRaptory.uruchom();
328
329 monitoringGłówny.uruchom();
330 monitoringTRex.uruchom();
331 monitoringRaptory.uruchom();
332
333 // Rozpoczęcie monitorowania
334 console.log("\n=== AKTYWACJA MONITOROWANIA ===");
335 ogrodzenieGłówne.rozpocznijMonitorowanie();
336 ogrodzenieTRex.rozpocznijMonitorowanie();
337 ogrodzenieRaptory.rozpocznijMonitorowanie();
338
339 monitoringGłówny.rozpocznijMonitorowanie();
340 monitoringTRex.rozpocznijMonitorowanie();
341 monitoringRaptory.rozpocznijMonitorowanie();
342
343 // Sprawdzenie statusów
344 console.log("\n=== RAPORTY SYSTEMÓW ===");
345 console.log(ogrodzenieGłówne.wygenerujRaportStatusu());
346 console.log(monitoringTRex.wygenerujRaportStatusu());
347
348 // Symulacja incydentu
349 console.log("\n=== SYMULACJA INCYDENTU ===");
350 console.log("Aktualny poziom alertów:", SystemBezpieczeństwa.poziomAlertów);
351
352 monitoringRaptory.wykryjRuch("Południowy narożnik");
353 ogrodzenieRaptory.zgłośPrzerwęWOgrodzeniu();
354
355 // Sprawdzenie poziom alertów po incydentach
356 console.log("\nAktualny poziom alertów po incydentach:", SystemBezpieczeństwa.poziomAlertów);
357
358 // Wyświetlenie ostatnich incydentów
359 console.log("\n=== OSTATNIE INCYDENTY ===");
360 SystemBezpieczeństwa.ostatnieIncydenty.forEach((incydent, index) => {
361 console.log(`${index + 1}. [${incydent.data.toLocaleString()}] ${incydent.typSystemu} w ${incydent.lokalizacja}: ${incydent.opis}`);
362 });
363
364 // Przeprowadzenie przeglądu
365 console.log("\n=== PRZEGLĄD SYSTEMÓW ===");
366 ogrodzenieTRex.wykonajPrzegląd();
367 monitoringGłówny.wykonajPrzegląd();
368
369 // Ręczna zmiana poziomu alertów
370 console.log("\n=== NORMALIZACJA SYTUACJI ===");
371 SystemBezpieczeństwa.zmieńPoziomAlertów("normalny");
372}
373
374// Uruchomienie testu
375testSystemuBezpieczeństwa();W tym zaawansowanym przykładzie:
SystemBezpieczeństwa jako bazę dla wszystkich systemów bezpieczeństwaSystemOgrodzeniaElektrycznego i SystemMonitoringuWideo) implementują abstrakcyjne elementyElementy statyczne i abstrakcyjne mogą być potężnym narzędziem, gdy są używane razem w dobrze zaprojektowanej hierarchii klas. Oto kilka wzorców i przykładów ich zastosowania:
1abstract class GatunekDinozaura {
2 // Właściwości statyczne
3 private static _zarejestrowaneDinozaury: Map<string, GatunekDinozaura> = new Map();
4
5 // Właściwości instancji
6 private _nazwa: string;
7 private _eraDominocenitryczna: string;
8 protected _dietaTyp: "mięsożerny" | "roślinożerny" | "wszystkożerny";
9
10 constructor(nazwa: string, era: string, dieta: "mięsożerny" | "roślinożerny" | "wszystkożerny") {
11 this._nazwa = nazwa;
12 this._eraDominocenitryczna = era;
13 this._dietaTyp = dieta;
14
15 // Dodanie do rejestru
16 GatunekDinozaura._zarejestrowaneDinozaury.set(nazwa, this);
17 }
18
19 // Statyczna metoda fabryczna - tworzy odpowiedni gatunek na podstawie diety
20 static stwórzGatunek(
21 nazwa: string,
22 era: string,
23 dieta: "mięsożerny" | "roślinożerny" | "wszystkożerny",
24 parametryDodatkowe: Record<string, any>
25 ): GatunekDinozaura {
26 if (dieta === "mięsożerny") {
27 return new MięsożernyGatunek(
28 nazwa,
29 era,
30 parametryDodatkowe.siłaUgryzienia,
31 parametryDodatkowe.prędkośćBiegu
32 );
33 } else {
34 return new RoślinożernyGatunek(
35 nazwa,
36 era,
37 parametryDodatkowe.długośćSzyi || 0,
38 parametryDodatkowe.posiadaPancerz || false
39 );
40 }
41 }
42
43 // Statyczna metoda do pobierania zarejestrowanych gatunków
44 static pobierzGatunek(nazwa: string): GatunekDinozaura | undefined {
45 return this._zarejestrowaneDinozaury.get(nazwa);
46 }
47
48 static get wszystkieGatunki(): string[] {
49 return Array.from(this._zarejestrowaneDinozaury.keys());
50 }
51
52 // Gettery instancji
53 get nazwa(): string { return this._nazwa; }
54 get era(): string { return this._eraDominocenitryczna; }
55 get typDiety(): string { return this._dietaTyp; }
56
57 // Abstrakcyjne metody
58 abstract opisZachowania(): string;
59 abstract obliczSzacowanąWagę(długość: number): number;
60
61 // Metoda wykorzystująca abstrakcyjne metody
62 generujOpis(): string {
63 return `${this._nazwa} - dinozaur ${this._dietaTyp} z ery ${this._eraDominocenitryczna}.
64${this.opisZachowania()}`;
65 }
66}
67
68// Klasy pochodne
69class MięsożernyGatunek extends GatunekDinozaura {
70 private _siłaUgryzienia: number; // w PSI
71 private _prędkośćBiegu: number; // w km/h
72
73 constructor(nazwa: string, era: string, siłaUgryzienia: number, prędkośćBiegu: number) {
74 super(nazwa, era, "mięsożerny");
75 this._siłaUgryzienia = siłaUgryzienia;
76 this._prędkośćBiegu = prędkośćBiegu;
77 }
78
79 // Gettery
80 get siłaUgryzienia(): number { return this._siłaUgryzienia; }
81 get prędkośćBiegu(): number { return this._prędkośćBiegu; }
82
83 // Implementacja abstrakcyjnych metod
84 opisZachowania(): string {
85 return `Poluje z prędkością ${this._prędkośćBiegu} km/h i siłą ugryzienia ${this._siłaUgryzienia} PSI.`;
86 }
87
88 obliczSzacowanąWagę(długość: number): number {
89 // Przybliżony wzór dla drapieżników
90 return długość * długość * 100;
91 }
92}
93
94class RoślinożernyGatunek extends GatunekDinozaura {
95 private _długośćSzyi: number; // w metrach
96 private _posiadaPancerz: boolean;
97
98 constructor(nazwa: string, era: string, długośćSzyi: number, posiadaPancerz: boolean) {
99 super(nazwa, era, "roślinożerny");
100 this._długośćSzyi = długośćSzyi;
101 this._posiadaPancerz = posiadaPancerz;
102 }
103
104 // Gettery
105 get długośćSzyi(): number { return this._długośćSzyi; }
106 get posiadaPancerz(): boolean { return this._posiadaPancerz; }
107
108 // Implementacja abstrakcyjnych metod
109 opisZachowania(): string {
110 const opisSzyi = this._długośćSzyi > 3
111 ? `Posiada długą szyję (${this._długośćSzyi}m) umożliwiającą zjadanie wysokiej roślinności.`
112 : "Żywi się niską roślinnością.";
113
114 const opisPancerza = this._posiadaPancerz
115 ? "Posiada pancerz chroniący przed drapieżnikami."
116 : "Nie posiada pancerza ochronnego.";
117
118 return `${opisSzyi} ${opisPancerza}`;
119 }
120
121 obliczSzacowanąWagę(długość: number): number {
122 // Przybliżony wzór dla roślinożerców (zwykle cięższe)
123 let waga = długość * długość * 150;
124
125 // Dodatkowa waga za pancerz
126 if (this._posiadaPancerz) {
127 waga *= 1.3;
128 }
129
130 return waga;
131 }
132}
133
134// Użycie
135function testGatunkówDinozaurów() {
136 // Tworzenie gatunków za pomocą metody fabrycznej
137 GatunekDinozaura.stwórzGatunek(
138 "Tyrannosaurus Rex",
139 "późna kreda",
140 "mięsożerny",
141 { siłaUgryzienia: 12800, prędkośćBiegu: 32 }
142 );
143
144 GatunekDinozaura.stwórzGatunek(
145 "Velociraptor",
146 "późna kreda",
147 "mięsożerny",
148 { siłaUgryzienia: 2000, prędkośćBiegu: 64 }
149 );
150
151 GatunekDinozaura.stwórzGatunek(
152 "Brachiosaurus",
153 "późna jura",
154 "roślinożerny",
155 { długośćSzyi: 9, posiadaPancerz: false }
156 );
157
158 GatunekDinozaura.stwórzGatunek(
159 "Stegosaurus",
160 "późna jura",
161 "roślinożerny",
162 { długośćSzyi: 0.5, posiadaPancerz: true }
163 );
164
165 // Dostęp do zarejestrowanych gatunków
166 console.log("Dostępne gatunki:", GatunekDinozaura.wszystkieGatunki);
167
168 // Pobieranie informacji o gatunkach
169 const trex = GatunekDinozaura.pobierzGatunek("Tyrannosaurus Rex");
170 const brach = GatunekDinozaura.pobierzGatunek("Brachiosaurus");
171
172 if (trex) {
173 console.log(trex.generujOpis());
174 console.log(`Szacowana waga (12m długości): ${trex.obliczSzacowanąWagę(12)} kg`);
175 }
176
177 if (brach) {
178 console.log(brach.generujOpis());
179 console.log(`Szacowana waga (25m długości): ${brach.obliczSzacowanąWagę(25)} kg`);
180 }
181
182 // Sprawdzanie typu
183 const steg = GatunekDinozaura.pobierzGatunek("Stegosaurus") as RoślinożernyGatunek;
184 if (steg && steg instanceof RoślinożernyGatunek) {
185 console.log(`Stegosaurus posiada pancerz: ${steg.posiadaPancerz ? "TAK" : "NIE"}`);
186 }
187
188 const raptor = GatunekDinozaura.pobierzGatunek("Velociraptor") as MięsożernyGatunek;
189 if (raptor && raptor instanceof MięsożernyGatunek) {
190 console.log(`Velociraptor - prędkość biegu: ${raptor.prędkośćBiegu} km/h`);
191 }
192}
193
194testGatunkówDinozaurów();W TypeScript od wersji 4.0 możliwe jest definiowanie abstrakcyjnych metod statycznych. Umożliwia to tworzenie hierarchii klas, gdzie klasy pochodne muszą implementować określone metody statyczne:
1abstract class SystemLokacji {
2 // Właściwości instancji
3 protected _id: string;
4 protected _nazwa: string;
5
6 constructor(id: string, nazwa: string) {
7 this._id = id;
8 this._nazwa = nazwa;
9 }
10
11 // Abstrakcyjna statyczna metoda, którą muszą zaimplementować klasy pochodne
12 static abstract generujIdentyfikatorLokacji(typ: string, numer: number): string;
13
14 // Zwykła abstrakcyjna metoda instancji
15 abstract pobierzInformacjeOLokacji(): Record<string, any>;
16
17 // Metoda instancji
18 wygenerujRaport(): string {
19 const info = this.pobierzInformacjeOLokacji();
20
21 let raport = `=== RAPORT LOKACJI: ${this._nazwa} (ID: ${this._id}) ===\n`;
22
23 for (const [klucz, wartość] of Object.entries(info)) {
24 raport += `${klucz}: ${wartość}\n`;
25 }
26
27 return raport;
28 }
29}
30
31class WybiegDinozaurów extends SystemLokacji {
32 private _gatunek: string;
33 private _powierzchnia: number; // w hektarach
34 private _liczbaOsobników: number;
35
36 constructor(numer: number, nazwa: string, gatunek: string, powierzchnia: number, liczbaOsobników: number) {
37 // Generowanie ID dla wybiegu
38 const id = WybiegDinozaurów.generujIdentyfikatorLokacji("wybieg", numer);
39 super(id, nazwa);
40
41 this._gatunek = gatunek;
42 this._powierzchnia = powierzchnia;
43 this._liczbaOsobników = liczbaOsobników;
44 }
45
46 // Implementacja abstrakcyjnej statycznej metody
47 static generujIdentyfikatorLokacji(typ: string, numer: number): string {
48 return `${typ.toUpperCase()}-${numer.toString().padStart(3, '0')}`;
49 }
50
51 // Implementacja abstrakcyjnej metody instancji
52 pobierzInformacjeOLokacji(): Record<string, any> {
53 return {
54 typ: "Wybieg dinozaurów",
55 gatunek: this._gatunek,
56 powierzchnia: `${this._powierzchnia} ha`,
57 liczbaOsobników: this._liczbaOsobników,
58 gęstośćZaludnienia: `${(this._liczbaOsobników / this._powierzchnia).toFixed(2)} os./ha`
59 };
60 }
61
62 // Metody specyficzne dla tej klasy
63 dodajOsobnika(): void {
64 this._liczbaOsobników++;
65 console.log(`Dodano nowego osobnika do wybiegu ${this._nazwa}. Nowa liczba: ${this._liczbaOsobników}`);
66 }
67
68 usuńOsobnika(): boolean {
69 if (this._liczbaOsobników > 0) {
70 this._liczbaOsobników--;
71 console.log(`Usunięto osobnika z wybiegu ${this._nazwa}. Nowa liczba: ${this._liczbaOsobników}`);
72 return true;
73 }
74
75 console.log(`Błąd: Nie można usunąć osobnika z wybiegu ${this._nazwa} - brak osobników.`);
76 return false;
77 }
78}
79
80class AtrakcjaTurystyczna extends SystemLokacji {
81 private _typAtrakcji: string;
82 private _pojemność: number;
83 private _czasOczekiwania: number; // w minutach
84
85 constructor(numer: number, nazwa: string, typAtrakcji: string, pojemność: number) {
86 // Generowanie ID dla atrakcji
87 const id = AtrakcjaTurystyczna.generujIdentyfikatorLokacji("atrakcja", numer);
88 super(id, nazwa);
89
90 this._typAtrakcji = typAtrakcji;
91 this._pojemność = pojemność;
92 this._czasOczekiwania = 0;
93 }
94
95 // Implementacja abstrakcyjnej statycznej metody
96 static generujIdentyfikatorLokacji(typ: string, numer: number): string {
97 // Inny format ID dla atrakcji
98 return `${typ.substring(0, 3).toUpperCase()}${numer.toString().padStart(4, '0')}`;
99 }
100
101 // Implementacja abstrakcyjnej metody instancji
102 pobierzInformacjeOLokacji(): Record<string, any> {
103 return {
104 typ: "Atrakcja turystyczna",
105 kategoria: this._typAtrakcji,
106 pojemność: this._pojemność,
107 czasOczekiwania: `${this._czasOczekiwania} min`
108 };
109 }
110
111 // Metody specyficzne dla tej klasy
112 aktualizujCzasOczekiwania(nowyOczekiwanieCzas: number): void {
113 this._czasOczekiwania = nowyOczekiwanieCzas;
114 console.log(`Zaktualizowano czas oczekiwania dla ${this._nazwa}: ${this._czasOczekiwania} min`);
115 }
116}
117
118// Zastosowanie
119function testSystemLokacji() {
120 // Tworzenie wybiegów
121 const wybiegTRex = new WybiegDinozaurów(1, "Królestwo T-Rexa", "Tyrannosaurus Rex", 5, 1);
122 const wybiegRaptorów = new WybiegDinozaurów(2, "Dolina Raptorów", "Velociraptor", 3, 6);
123
124 // Tworzenie atrakcji
125 const centrumOdwiedzających = new AtrakcjaTurystyczna(101, "Centrum Genetyki", "edukacyjna", 500);
126 const safariJeepem = new AtrakcjaTurystyczna(102, "Safari Jurajskie", "przejażdżka", 50);
127
128 // Wyświetlanie raportów
129 console.log(wybiegTRex.wygenerujRaport());
130 console.log(wybiegRaptorów.wygenerujRaport());
131 console.log(centrumOdwiedzających.wygenerujRaport());
132 console.log(safariJeepem.wygenerujRaport());
133
134 // Wykonywanie operacji
135 wybiegRaptorów.dodajOsobnika();
136 wybiegRaptorów.dodajOsobnika();
137 safariJeepem.aktualizujCzasOczekiwania(45);
138
139 // Sprawdzenie zaktualizowanych danych
140 console.log(wybiegRaptorów.wygenerujRaport());
141 console.log(safariJeepem.wygenerujRaport());
142
143 // Demonstracja różnych formatów ID generowanych przez implementacje statycznych metod
144 console.log("Format ID wybiegu:", WybiegDinozaurów.generujIdentyfikatorLokacji("wybieg", 42));
145 console.log("Format ID atrakcji:", AtrakcjaTurystyczna.generujIdentyfikatorLokacji("atrakcja", 42));
146}
147
148testSystemLokacji();W tym ćwiczeniu poznaliśmy dwa potężne mechanizmy TypeScript:
Właściwości i metody statyczne:
Klasy i metody abstrakcyjne:
Łącząc te koncepcje, możemy tworzyć eleganckie, modułowe i typowo bezpieczne systemy, które idealnie nadają się do modelowania złożonych relacji i procesów, takich jak te w systemie zarządzania Parkiem Jurajskim.
Dr. Henry Wu podsumowałby to następująco: "W naturze, podobnie jak w programowaniu, istnieje piękno w strukturze i porządku. Elementy statyczne zapewniają spójność dla całego gatunku, podczas gdy abstrakcyjne koncepcje stanowią podstawowy plan, który każdy organizm musi zaimplementować na swój własny sposób. Razem tworzą doskonałą harmonię - z odpowiednimi zabezpieczeniami, oczywiście... w większości przypadków."