Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds

Definicja interfejsów

Witaj z powrotem w Parku Jurajskim! Dziś, pod czujnym okiem Dr. Henry Wu, będziemy zgłębiać fundamentalną koncepcję TypeScript, która jest równie kluczowa dla organizacji kodu, jak protokoły bezpieczeństwa są dla prawidłowego funkcjonowania parku - interfejsy.

Czym są interfejsy w TypeScript?

Interfejsy w TypeScript definiują kontrakty w kodzie oraz opisują strukturę obiektów. Są one jak szczegółowe protokoły, które określają, co powinno zawierać dany obiekt. W kontekście Parku Jurajskiego, interfejsy można porównać do standardów bezpieczeństwa i protokołów obsługi dla różnych gatunków dinozaurów - każdy gatunek ma swoje specyficzne wymagania i cechy.

Podstawowa składnia interfejsu

Interfejs definiujemy za pomocą słowa kluczowego

interface
:

1interface Dinozaur {
2  id: number;
3  nazwa: string;
4  gatunek: string;
5  wiek: number;
6  waga: number;
7}
8
9// Tworzymy obiekt, który musi spełniać wymagania interfejsu
10const rex: Dinozaur = {
11  id: 1,
12  nazwa: "Rex",
13  gatunek: "Tyrannosaurus",
14  wiek: 7,
15  waga: 8000
16};
17
18// Poniższy kod spowodowałby błąd, ponieważ brakuje wymaganych właściwości
19// const raptor: Dinozaur = {
20//   id: 2,
21//   nazwa: "Blue"
22// };

Właściwości opcjonalne

Niektóre właściwości w interfejsie mogą być opcjonalne, oznaczane przez dodanie znaku zapytania (

?
). To jak oznaczenie opcjonalnych procedur obsługi dla poszczególnych dinozaurów.

1interface OkazDinozaura {
2  id: number;
3  nazwa: string;
4  gatunek: string;
5  tagRFID?: string;        // Opcjonalny tag RFID
6  ostatnieBadanie?: Date;  // Opcjonalna data ostatniego badania
7  notatki?: string;        // Opcjonalne notatki badawcze
8}
9
10// Obiekt spełniający interfejs, ale bez wszystkich opcjonalnych właściwości
11const okaz1: OkazDinozaura = {
12  id: 101,
13  nazwa: "Specimen A",
14  gatunek: "Velociraptor"
15};
16
17// Obiekt z niektórymi opcjonalnymi właściwościami
18const okaz2: OkazDinozaura = {
19  id: 102,
20  nazwa: "Specimen B",
21  gatunek: "Dilophosaurus",
22  tagRFID: "DILO-102",
23  notatki: "Wykazuje agresywne zachowania w obecności nowych obiektów"
24};

Tylko do odczytu (readonly)

Niektóre właściwości powinny być modyfikowane tylko przy tworzeniu obiektu. W TypeScript możemy to osiągnąć używając modyfikatora

readonly
. To jak procedury bezpieczeństwa, które raz ustalone, nie mogą być zmieniane podczas normalnej operacji.

1interface KodGenetyczny {
2  readonly id: string;
3  readonly sekwencja: string;
4  readonly gatunekDawca: string;
5  modyfikacje: string[];
6  dataIzolacji: Date;
7}
8
9// Tworzymy kod genetyczny
10const kodTRex: KodGenetyczny = {
11  id: "GEN-TREX-001",
12  sekwencja: "ACGTTGCAAGCTA...",
13  gatunekDawca: "Żaba zachodnioafrykańska",
14  modyfikacje: ["Uzupełnienie luk DNA", "Korekta genów agresji"],
15  dataIzolacji: new Date("2022-05-15")
16};
17
18// Możemy dodawać nowe modyfikacje
19kodTRex.modyfikacje.push("Wzmocnienie odporności immunologicznej");
20
21// Ale nie możemy zmienić wartości pól readonly
22// kodTRex.id = "GEN-TREX-002"; // Błąd: Cannot assign to 'id' because it is a read-only property
23// kodTRex.sekwencja = "GCTA..."; // Błąd: Cannot assign to 'sekwencja' because it is a read-only property

Interfejsy funkcji

Interfejsy mogą również definiować typy funkcji, co jest przydatne przy opisywaniu funkcji zwrotnych lub przekazywanych jako parametry. W kontekście parku, to jak definiowanie standardowych procedur obsługi różnych sytuacji.

1// Interfejs funkcji bezpieczeństwa
2interface SprawdzenieBezpieczeństwa {
3  (poziomZagrożenia: number, strefaParku: string): boolean;
4}
5
6// Implementacja funkcji zgodnej z interfejsem
7const sprawdźDostęp: SprawdzenieBezpieczeństwa = function(poziom, strefa) {
8  // Logika sprawdzania dostępu
9  if (strefa === "Wybieg T-Rex" && poziom < 5) {
10    return false; // Odmowa dostępu do strefy T-Rex dla pracowników poniżej poziomu 5
11  }
12
13  if (strefa.includes("Laboratorium") && poziom < 4) {
14    return false; // Odmowa dostępu do laboratoriów dla pracowników poniżej poziomu 4
15  }
16
17  return true; // Dostęp dozwolony dla pozostałych przypadków
18};
19
20// Użycie funkcji
21console.log(sprawdźDostęp(3, "Wybieg T-Rex")); // false
22console.log(sprawdźDostęp(5, "Wybieg T-Rex")); // true
23console.log(sprawdźDostęp(3, "Obszar publiczny")); // true

Indeksowalne typy

Interfejsy mogą definiować typy indeksowalne - użyteczne, gdy mamy obiekty z dynamiczną liczbą właściwości tego samego typu. W kontekście parku, może to być baza danych dinozaurów indeksowana po ich identyfikatorach.

1// Interfejs dla bazy danych dinozaurów
2interface BazaDanychDinozaurów {
3  [id: string]: {
4    nazwa: string;
5    gatunek: string;
6    wiek: number;
7    zdrowie: "dobre" | "wymaga uwagi" | "krytyczne";
8  };
9}
10
11// Tworzenie bazy danych
12const bazaDanych: BazaDanychDinozaurów = {
13  "TREX-001": {
14    nazwa: "Rex",
15    gatunek: "Tyrannosaurus",
16    wiek: 7,
17    zdrowie: "dobre"
18  },
19  "VELOC-001": {
20    nazwa: "Blue",
21    gatunek: "Velociraptor",
22    wiek: 4,
23    zdrowie: "dobre"
24  },
25  "TRIC-003": {
26    nazwa: "Trike",
27    gatunek: "Triceratops",
28    wiek: 10,
29    zdrowie: "wymaga uwagi"
30  }
31};
32
33// Dostęp do danych
34console.log(bazaDanych["VELOC-001"].nazwa); // "Blue"
35
36// Dodawanie nowego wpisu
37bazaDanych["STEGO-002"] = {
38  nazwa: "Spike",
39  gatunek: "Stegosaurus",
40  wiek: 6,
41  zdrowie: "dobre"
42};

Interfejsy klas

Interfejsy mogą również definiować kontrakty, które klasy muszą implementować. To jak standardy operacyjne, które każdy departament w parku musi spełniać.

1// Interfejs dla systemów bezpieczeństwa
2interface SystemBezpieczeństwa {
3  nazwa: string;
4  czyAktywny: boolean;
5  poziomBezpieczeństwa: number;
6
7  aktywuj(): void;
8  dezaktywuj(): void;
9  sprawdźStan(): string;
10}
11
12// Klasa implementująca interfejs
13class SystemElektronicznychOgrodzeń implements SystemBezpieczeństwa {
14  nazwa: string;
15  czyAktywny: boolean;
16  poziomBezpieczeństwa: number;
17  napięcie: number; // Dodatkowa właściwość specyficzna dla tej klasy
18
19  constructor(nazwa: string, poziomBezpieczeństwa: number, napięcie: number) {
20    this.nazwa = nazwa;
21    this.czyAktywny = false; // Początkowo nieaktywny
22    this.poziomBezpieczeństwa = poziomBezpieczeństwa;
23    this.napięcie = napięcie;
24  }
25
26  aktywuj(): void {
27    this.czyAktywny = true;
28    console.log(`System ${this.nazwa} został aktywowany. Napięcie: ${this.napięcie}V`);
29  }
30
31  dezaktywuj(): void {
32    this.czyAktywny = false;
33    console.log(`System ${this.nazwa} został dezaktywowany.`);
34  }
35
36  sprawdźStan(): string {
37    return this.czyAktywny
38      ? `${this.nazwa}: AKTYWNY (Poziom ${this.poziomBezpieczeństwa})`
39      : `${this.nazwa}: NIEAKTYWNY`;
40  }
41
42  // Metoda specyficzna dla tej klasy
43  zwiększNapięcie(o: number): void {
44    this.napięcie += o;
45    console.log(`Zwiększono napięcie do ${this.napięcie}V`);
46  }
47}
48
49// Użycie klasy
50const ogrodzenieTRex = new SystemElektronicznychOgrodzeń("Ogrodzenie T-Rex", 5, 10000);
51ogrodzenieTRex.aktywuj();
52console.log(ogrodzenieTRex.sprawdźStan());
53ogrodzenieTRex.zwiększNapięcie(2000);

Rozszerzanie interfejsów

Interfejsy mogą rozszerzać inne interfejsy, podobnie jak w biologii, gdzie gatunki dzielą wspólne cechy, ale mają też swoje unikalne właściwości.

1// Podstawowy interfejs dla wszystkich zwierząt w parku
2interface Zwierzę {
3  id: number;
4  gatunek: string;
5  wiek: number;
6  waga: number;
7}
8
9// Interfejs dla dinozaurów rozszerzający interfejs Zwierzę
10interface Dinozaur extends Zwierzę {
11  era: string; // np. "Kreda", "Jura"
12  jestDrapieżnikiem: boolean;
13  dietaGłówna: string[];
14}
15
16// Interfejs dla dinozaurów drapieżnych rozszerzający interfejs Dinozaur
17interface DrapieżnyDinozaur extends Dinozaur {
18  siłaUgryzienia: number;  // w PSI
19  prędkośćBiegu: number;   // w km/h
20  zachowaniePodczasPolowania: string[];
21}
22
23// Tworzenie obiektu zgodnego z interfejsem DrapieżnyDinozaur
24const velociraptor: DrapieżnyDinozaur = {
25  id: 101,
26  gatunek: "Velociraptor",
27  wiek: 4,
28  waga: 70,
29  era: "Kreda",
30  jestDrapieżnikiem: true,
31  dietaGłówna: ["małe ssaki", "jaszczurki", "inne małe dinozaury"],
32  siłaUgryzienia: 1000,
33  prędkośćBiegu: 60,
34  zachowaniePodczasPolowania: ["polowanie w stadzie", "zasadzka", "pościg"]
35};

Łączenie deklaracji interfejsów

W TypeScript możliwe jest definiowanie tego samego interfejsu wielokrotnie - deklaracje zostaną połączone. To przydatne, gdy chcemy dodać nowe właściwości do istniejącego interfejsu.

1// Początkowa definicja protokołu bezpieczeństwa
2interface ProtokółBezpieczeństwa {
3  nazwa: string;
4  poziomZagrożenia: number;
5  proceduraEwakuacji: string;
6}
7
8// Rozszerzenie protokołu o dodatkowe właściwości
9interface ProtokółBezpieczeństwa {
10  personelOdpowiedzialny: string[];
11  czasReakcji: number; // w minutach
12}
13
14// Obiekt musi zawierać wszystkie właściwości z obu deklaracji
15const protokółAlfa: ProtokółBezpieczeństwa = {
16  nazwa: "Protokół Alfa",
17  poziomZagrożenia: 3,
18  proceduraEwakuacji: "Ewakuacja w kierunku głównego centrum kontroli",
19  personelOdpowiedzialny: ["Dr. Wu", "Owen Grady", "Claire Dearing"],
20  czasReakcji: 15
21};

Interfejsy vs typy

TypeScript oferuje również słowo kluczowe

type
do definiowania typów. Choć często interfejsy i typy mogą być używane zamiennie, istnieją między nimi różnice:

1// Definicja za pomocą interface
2interface PlanEwakuacji {
3  trasa: string[];
4  czasSzacunkowy: number;
5  punktyZbiórek: string[];
6}
7
8// Równoważna definicja za pomocą type
9type PlanEwakuacjiType = {
10  trasa: string[];
11  czasSzacunkowy: number;
12  punktyZbiórek: string[];
13};
14
15// Oba działają podobnie przy podstawowym użyciu
16const planA: PlanEwakuacji = {
17  trasa: ["Centrum operacyjne", "Main Street", "Hala ewakuacyjna"],
18  czasSzacunkowy: 25,
19  punktyZbiórek: ["Centrum operacyjne", "Hala ewakuacyjna"]
20};
21
22const planB: PlanEwakuacjiType = {
23  trasa: ["Wybieg Gallimimus", "Innovation Center", "Hala ewakuacyjna"],
24  czasSzacunkowy: 30,
25  punktyZbiórek: ["Innovation Center", "Hala ewakuacyjna"]
26};

Główne różnice między interface a type:

  1. Rozszerzalność:

    • Interfejsy można rozszerzać przez wielokrotne deklaracje o tej samej nazwie
    • Typy nie mogą być "otwarte" - raz zadeklarowany typ nie może być rozszerzony
  2. Złożone typy:

    • Typy mogą reprezentować bezpośrednio typy proste, unie, przecięcia
    • Interfejsy są bardziej ukierunkowane na definiowanie kształtu obiektów
1// Możliwe tylko z typami
2type IDDinozaura = string | number;
3type KoordynatyWybiegu = [number, number, string]; // Tuple
4type StatusDinozaura = "aktywny" | "odpoczywający" | "śpiący"; // Union of literals
5
6// Bardziej naturalne z interfejsami
7interface Procedura {
8  nazwa: string;
9  kroki: string[];
10  czasWykonania: number;
11  personelOdpowiedzialny: string[];
12}

Praktyczne zastosowanie interfejsów

Przyjrzyjmy się, jak możemy wykorzystać interfejsy do modelowania systemu zarządzania Parkiem Jurajskim:

1// Podstawowe interfejsy
2interface OsobaWParku {
3  id: string;
4  imię: string;
5  nazwisko: string;
6  poziomDostępu: 1 | 2 | 3 | 4 | 5;
7  dataZatrudnienia: Date;
8}
9
10interface Lokalizacja {
11  id: string;
12  nazwa: string;
13  typ: "wybieg" | "laboratorium" | "centrum operacyjne" | "strefa publiczna";
14  współrzędne: [number, number]; // [szerokość, długość]
15  wymaganyPoziomDostępu: 1 | 2 | 3 | 4 | 5;
16}
17
18interface GatunekDinozaura {
19  nazwa: string;
20  eraGeologiczna: string;
21  typ: "roślinożerca" | "drapieżnik" | "wszystkożerca";
22  przeciętnaWaga: number;
23  przeciętnyWiek: number;
24  poziomNiebezpieczeństwa: 1 | 2 | 3 | 4 | 5;
25  wymaganiaDiety: string[];
26  wymaganiaHabitatu: string[];
27}
28
29interface OkazDinozaura {
30  id: string;
31  gatunek: string;
32  imię?: string;
33  wiek: number;
34  waga: number;
35  płeć: "samiec" | "samica";
36  lokalizacja: string; // ID lokalizacji
37  stanZdrowia: "doskonały" | "dobry" | "wymaga uwagi" | "chory" | "krytyczny";
38  ostatnieBadanie: Date;
39  tagRFID: string;
40  notatki?: string;
41}
42
43// Interfejsy dla systemów zarządzania
44interface SystemŚledzeniaOkazów {
45  pobierzPozycję(idOkazu: string): [number, number];
46  śledźRuch(idOkazu: string, czasŚledzenia: number): [number, number][];
47  sprawdźCzyWStrefie(idOkazu: string, idStrefy: string): boolean;
48  alertGdyPozaStrefą(idOkazu: string, callback: (okaz: OkazDinozaura) => void): void;
49}
50
51interface SystemZarządzaniaPracownikami {
52  dodajPracownika(pracownik: OsobaWParku): void;
53  usuńPracownika(id: string): boolean;
54  przyznajDostęp(idPracownika: string, poziom: 1 | 2 | 3 | 4 | 5): boolean;
55  sprawdźObecnośćWStrefie(idPracownika: string, idStrefy: string): boolean;
56}
57
58// Przykład implementacji
59class SystemZarządzaniaParkiem {
60  private pracownicy: OsobaWParku[] = [];
61  private lokalizacje: Lokalizacja[] = [];
62  private gatunki: GatunekDinozaura[] = [];
63  private okazy: OkazDinozaura[] = [];
64
65  // Metody dla pracowników
66  dodajPracownika(pracownik: OsobaWParku): void {
67    this.pracownicy.push(pracownik);
68    console.log(`Dodano pracownika: ${pracownik.imię} ${pracownik.nazwisko}`);
69  }
70
71  // Metody dla lokalizacji
72  dodajLokalizację(lokalizacja: Lokalizacja): void {
73    this.lokalizacje.push(lokalizacja);
74    console.log(`Dodano lokalizację: ${lokalizacja.nazwa}`);
75  }
76
77  // Metody dla gatunków
78  dodajGatunek(gatunek: GatunekDinozaura): void {
79    this.gatunki.push(gatunek);
80    console.log(`Dodano gatunek: ${gatunek.nazwa}`);
81  }
82
83  // Metody dla okazów
84  dodajOkaz(okaz: OkazDinozaura): void {
85    this.okazy.push(okaz);
86    console.log(`Dodano okaz: ${okaz.id} (${okaz.gatunek})`);
87  }
88
89  // Metody zarządzania
90  sprawdźDostęp(idPracownika: string, idLokalizacji: string): boolean {
91    const pracownik = this.pracownicy.find(p => p.id === idPracownika);
92    const lokalizacja = this.lokalizacje.find(l => l.id === idLokalizacji);
93
94    if (!pracownik || !lokalizacja) return false;
95
96    return pracownik.poziomDostępu >= lokalizacja.wymaganyPoziomDostępu;
97  }
98
99  znajdźOkazyGatunku(nazwaGatunku: string): OkazDinozaura[] {
100    return this.okazy.filter(o => o.gatunek === nazwaGatunku);
101  }
102
103  znajdźOkazyWStrefie(idStrefy: string): OkazDinozaura[] {
104    return this.okazy.filter(o => o.lokalizacja === idStrefy);
105  }
106
107  generujRaportStanuParku(): {
108    liczbaPracowników: number;
109    liczbaLokalizacji: number;
110    liczbaGatunków: number;
111    liczbaOkazów: number;
112    stanZdrowiaOkazów: {
113      doskonały: number;
114      dobry: number;
115      wymagaUwagi: number;
116      chory: number;
117      krytyczny: number;
118    }
119  } {
120    // Analiza stanu zdrowia wszystkich okazów
121    const stanZdrowia = {
122      doskonały: 0,
123      dobry: 0,
124      wymagaUwagi: 0,
125      chory: 0,
126      krytyczny: 0
127    };
128
129    this.okazy.forEach(okaz => {
130      if (okaz.stanZdrowia === "doskonały") stanZdrowia.doskonały++;
131      else if (okaz.stanZdrowia === "dobry") stanZdrowia.dobry++;
132      else if (okaz.stanZdrowia === "wymaga uwagi") stanZdrowia.wymagaUwagi++;
133      else if (okaz.stanZdrowia === "chory") stanZdrowia.chory++;
134      else if (okaz.stanZdrowia === "krytyczny") stanZdrowia.krytyczny++;
135    });
136
137    return {
138      liczbaPracowników: this.pracownicy.length,
139      liczbaLokalizacji: this.lokalizacje.length,
140      liczbaGatunków: this.gatunki.length,
141      liczbaOkazów: this.okazy.length,
142      stanZdrowiaOkazów: stanZdrowia
143    };
144  }
145}
146
147// Przykład użycia
148const parkJurajski = new SystemZarządzaniaParkiem();
149
150// Dodanie lokalizacji
151parkJurajski.dodajLokalizację({
152  id: "LOC-001",
153  nazwa: "Wybieg T-Rex",
154  typ: "wybieg",
155  współrzędne: [23.45, -67.89],
156  wymaganyPoziomDostępu: 4
157});
158
159// Dodanie gatunku
160parkJurajski.dodajGatunek({
161  nazwa: "Tyrannosaurus Rex",
162  eraGeologiczna: "Kreda",
163  typ: "drapieżnik",
164  przeciętnaWaga: 7000,
165  przeciętnyWiek: 30,
166  poziomNiebezpieczeństwa: 5,
167  wymaganiaDiety: ["duże ilości mięsa", "uzupełnianie wapnia"],
168  wymaganiaHabitatu: ["duży obszar", "naturalna roślinność", "dostęp do wody"]
169});
170
171// Dodanie pracownika
172parkJurajski.dodajPracownika({
173  id: "EMP-001",
174  imię: "Owen",
175  nazwisko: "Grady",
176  poziomDostępu: 5,
177  dataZatrudnienia: new Date("2010-06-15")
178});
179
180// Dodanie okazu
181parkJurajski.dodajOkaz({
182  id: "DINO-001",
183  gatunek: "Tyrannosaurus Rex",
184  imię: "Rexy",
185  wiek: 7,
186  waga: 7500,
187  płeć: "samica",
188  lokalizacja: "LOC-001",
189  stanZdrowia: "dobry",
190  ostatnieBadanie: new Date("2023-03-20"),
191  tagRFID: "RFID-TREX-001"
192});
193
194// Generowanie raportu
195const raport = parkJurajski.generujRaportStanuParku();
196console.log("Raport stanu Parku Jurajskiego:");
197console.log(`Liczba pracowników: ${raport.liczbaPracowników}`);
198console.log(`Liczba lokalizacji: ${raport.liczbaLokalizacji}`);
199console.log(`Liczba gatunków: ${raport.liczbaGatunków}`);
200console.log(`Liczba okazów: ${raport.liczbaOkazów}`);
201console.log("Stan zdrowia okazów:", raport.stanZdrowiaOkazów);

Podsumowanie

Interfejsy w TypeScript są potężnym narzędziem do modelowania struktur danych i definiowania kontraktów w kodzie. W kontekście zarządzania Parkiem Jurajskim, pozwalają nam precyzyjnie określić, jak powinny wyglądać różne systemy, procedury i elementy parku.

| Cecha interfejsu | Zastosowanie w Parku Jurajskim | |------------------|---------------------------------| | Podstawowa definicja | Struktury danych dla dinozaurów, pracowników, lokalizacji | | Właściwości opcjonalne | Dodatkowe informacje, które mogą, ale nie muszą być dostępne | | Właściwości readonly | Dane, które nie powinny być zmieniane, np. ID genetyczne | | Interfejsy funkcji | Definicje procedur bezpieczeństwa i protokołów | | Typy indeksowalne | Dynamiczne bazy danych dinozaurów i zasobów | | Interfejsy klas | Standardy dla systemów bezpieczeństwa i monitoringu | | Rozszerzanie interfejsów | Dziedziczenie cech między gatunkami dinozaurów | | Łączenie deklaracji | Rozszerzanie protokołów o dodatkowe procedury |

Dr. Wu podsumowuje: "Interfejsy w TypeScript, podobnie jak nasze protokoły w parku, zapewniają, że każdy element systemu działa zgodnie z oczekiwaniami. Dzięki nim możemy budować złożone systemy z pewnością, że poszczególne komponenty będą ze sobą współpracować - co jest absolutnie kluczowe, gdy mamy do czynienia z gatunkami, które mogą być niebezpieczne, jeśli coś pójdzie nie tak."

Vai a CodeWorlds