Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Przeciążanie funkcji

"W Parku Jurajskim precyzja jest kluczowa," mówi Dr. Henry Wu, patrząc na swój wysoce wyspecjalizowany sprzęt laboratoryjny. "Ten sam analizator DNA może badać próbki w różnych formatach - od krwi dinozaurów, przez skamieniałości, po bursztyn. Podobnie w TypeScript, jedna funkcja może obsługiwać różne zestawy parametrów i zwracać różne typy danych."

Witaj w świecie przeciążania funkcji (function overloading) - potężnego mechanizmu TypeScript, który pozwala na definiowanie funkcji przyjmujących różne rodzaje parametrów i zwracających różne typy wyników.

Czym jest przeciążanie funkcji?

Przeciążanie funkcji to mechanizm, który umożliwia definiowanie wielu sygnatur dla jednej funkcji. W ten sposób możemy stworzyć funkcję, która reaguje różnie w zależności od liczby, typów i struktury przekazanych parametrów.

Innymi słowy, tak jak naukowcy w Parku Jurajskim mogą wykorzystywać ten sam sprzęt do różnych procedur, tak my możemy wykorzystać jedną funkcję do różnych operacji, zachowując przy tym pełne bezpieczeństwo typów.

Podstawy przeciążania funkcji

W TypeScript przeciążanie funkcji składa się z dwóch części:

  1. Sygnatury przeciążeń (overload signatures) - definicje typów parametrów i zwracanych wartości
  2. Sygnatura implementacji (implementation signature) - faktyczna implementacja funkcji, która musi być kompatybilna ze wszystkimi sygnaturami przeciążeń

Przyjrzyjmy się prostemu przykładowi:

1// Sygnatury przeciążeń
2function analizujPróbkę(id: number): string;
3function analizujPróbkę(nazwa: string): string;
4
5// Sygnatura implementacji
6function analizujPróbkę(argument: number | string): string {
7  if (typeof argument === "number") {
8    return `Analizowanie próbki o ID: ${argument}`;
9  } else {
10    return `Analizowanie próbki: ${argument}`;
11  }
12}
13
14// Użycie funkcji
15const wynik1 = analizujPróbkę(42);         // TypeScript wie, że argument to number
16const wynik2 = analizujPróbkę("Raptor-01"); // TypeScript wie, że argument to string
17// const błąd = analizujPróbkę(true);      // Błąd, boolean nie jest akceptowanym typem

W powyższym przykładzie:

  1. Zdefiniowaliśmy dwie sygnatury przeciążeń - jedna przyjmująca
    number
    , druga przyjmująca
    string
  2. Stworzyliśmy jedną implementację, która obsługuje oba typy
  3. TypeScript dba o to, aby funkcja była wywoływana tylko z argumentami zgodnych typów

Przeciążanie z różnymi typami zwracanymi

Prawdziwa moc przeciążania ujawnia się, gdy funkcja może zwracać różne typy w zależności od parametrów wejściowych:

1// Sygnatury przeciążeń z różnymi typami zwracanymi
2function pobierzDaneDinozaura(id: number): { id: number; gatunek: string; wiek: number };
3function pobierzDaneDinozaura(nazwa: string): { nazwa: string; gatunek: string };
4
5// Sygnatura implementacji
6function pobierzDaneDinozaura(identyfikator: number | string): any {
7  if (typeof identyfikator === "number") {
8    // Pobieranie pełnych danych na podstawie ID
9    return {
10      id: identyfikator,
11      gatunek: "Tyrannosaurus",
12      wiek: 7
13    };
14  } else {
15    // Pobieranie podstawowych informacji na podstawie nazwy
16    return {
17      nazwa: identyfikator,
18      gatunek: "Velociraptor"
19    };
20  }
21}
22
23// Użycie z pełną informacją o typach
24const dinozaurPoId = pobierzDaneDinozaura(42);
25console.log(dinozaurPoId.wiek);  // OK, TypeScript wie, że właściwość 'wiek' istnieje
26
27const dinozaurPoNazwie = pobierzDaneDinozaura("Blue");
28// console.log(dinozaurPoNazwie.wiek);  // Błąd, TypeScript wie, że obiekt nie ma właściwości 'wiek'
29console.log(dinozaurPoNazwie.gatunek);  // OK

Zwróć uwagę, że mimo użycia

any
w sygnaturze implementacji, TypeScript zapewnia pełne bezpieczeństwo typów podczas korzystania z funkcji, bazując na sygnaturach przeciążeń.

Przeciążanie z różną liczbą parametrów

Możemy również przeciążać funkcje, które przyjmują różną liczbę parametrów:

1// Przeciążenie z różną liczbą parametrów
2function monitorujDinozaura(id: number): string;
3function monitorujDinozaura(id: number, poziomSzczegółów: "podstawowy" | "pełny"): Object;
4function monitorujDinozaura(id: number, poziomSzczegółów?: "podstawowy" | "pełny"): string | Object {
5  if (!poziomSzczegółów) {
6    return `Monitorowanie dinozaura o ID ${id}...`;
7  } else if (poziomSzczegółów === "podstawowy") {
8    return {
9      status: "aktywny",
10      lokalizacja: "Sektor B"
11    };
12  } else {
13    return {
14      status: "aktywny",
15      lokalizacja: "Sektor B",
16      prędkość: "2 km/h",
17      tętno: 80,
18      temperaturaCiała: 38,
19      ostatniPosiłek: "2 godziny temu",
20      poziomAgresji: "niski"
21    };
22  }
23}
24
25// Użycie
26const komunikat = monitorujDinozaura(5);  // Zwraca string
27const podstawoweDane = monitorujDinozaura(5, "podstawowy");  // Zwraca prosty obiekt
28const szczegółoweDane = monitorujDinozaura(5, "pełny");  // Zwraca rozbudowany obiekt

Przeciążanie metod w klasach

Przeciążanie funkcji może być również stosowane do metod w klasach:

1class EkipaBadawcza {
2  // Sygnatury przeciążeń dla metody wyslijZespol
3  wyslijZespol(cel: { x: number, y: number }): string;
4  wyslijZespol(sektor: string): string;
5  wyslijZespol(cel: string, wielkosc: number): string;
6
7  // Implementacja metody
8  wyslijZespol(cel: { x: number, y: number } | string, wielkosc?: number): string {
9    if (typeof cel === "object") {
10      return `Wysyłanie zespołu na współrzędne: ${cel.x}, ${cel.y}`;
11    } else if (wielkosc !== undefined) {
12      return `Wysyłanie zespołu ${wielkosc}-osobowego do sektora: ${cel}`;
13    } else {
14      return `Wysyłanie standardowego zespołu do sektora: ${cel}`;
15    }
16  }
17}
18
19const ekipa = new EkipaBadawcza();
20console.log(ekipa.wyslijZespol({ x: 123, y: 456 }));  // Użycie współrzędnych
21console.log(ekipa.wyslijZespol("B-5"));  // Użycie nazwy sektora
22console.log(ekipa.wyslijZespol("C-7", 5));  // Użycie nazwy sektora i wielkości zespołu

Zaawansowane przykłady przeciążania funkcji

Różne typy dla różnych kombinacji parametrów

1// Sygnatury przeciążeń dla złożonej analizy
2function analizujZachowanie(
3  gatunekId: number,
4  daneBehawioralne: number[]
5): { poziomAgresji: number; ryzyko: string };
6
7function analizujZachowanie(
8  gatunekId: number,
9  daneBehawioralne: number[],
10  historyczneDane: boolean
11): { poziomAgresji: number; ryzyko: string; porównanie: string };
12
13// Implementacja
14function analizujZachowanie(
15  gatunekId: number,
16  daneBehawioralne: number[],
17  historyczneDane?: boolean
18): any {
19  // Podstawowa analiza
20  const średniaAktywność = daneBehawioralne.reduce((a, b) => a + b, 0) / daneBehawioralne.length;
21  const poziomAgresji = Math.round(średniaAktywność * 10) / 10;
22
23  let ryzyko = "niskie";
24  if (poziomAgresji > 7) ryzyko = "wysokie";
25  else if (poziomAgresji > 4) ryzyko = "średnie";
26
27  // Podstawowy wynik
28  const wynik = { poziomAgresji, ryzyko };
29
30  // Rozszerzony wynik jeśli żądane są dane historyczne
31  if (historyczneDane) {
32    // Symulacja pobrania danych historycznych
33    const historycznyPoziom = 3.5;
34    const zmiana = poziomAgresji - historycznyPoziom;
35    const porównanie = zmiana > 0
36      ? `Wzrost o ${zmiana.toFixed(1)} pkt`
37      : `Spadek o ${Math.abs(zmiana).toFixed(1)} pkt`;
38
39    return { ...wynik, porównanie };
40  }
41
42  return wynik;
43}
44
45// Użycie
46const podstawowaAnaliza = analizujZachowanie(1, [2, 4, 3, 6, 1]);
47console.log(podstawowaAnaliza.ryzyko);  // "średnie"
48
49const rozszerzonaAnaliza = analizujZachowanie(1, [2, 4, 3, 6, 1], true);
50console.log(rozszerzonaAnaliza.porównanie);  // "Wzrost o 0.8 pkt"

Generyczne przeciążenia funkcji

Przeciążenia funkcji mogą być jeszcze potężniejsze, gdy połączymy je z generycznymi typami:

1// Generyczne przeciążenia funkcji
2function pobierzDane<T extends { id: number }>(id: number): T;
3function pobierzDane<T extends { nazwa: string }>(nazwa: string): T;
4function pobierzDane<T>(identyfikator: number | string): T {
5  // Implementacja komunikująca się z bazą danych i zwracająca odpowiedni typ
6  if (typeof identyfikator === "number") {
7    // Symulacja różnych typów danych dla różnych ID
8    if (identyfikator < 100) {
9      // Dinozaur
10      return {
11        id: identyfikator,
12        gatunek: "Tyrannosaurus",
13        wiek: 7,
14        waga: 7500
15      } as unknown as T;
16    } else {
17      // Pracownik
18      return {
19        id: identyfikator,
20        imie: "Owen",
21        nazwisko: "Grady",
22        stanowisko: "Treser"
23      } as unknown as T;
24    }
25  } else {
26    // Wyszukiwanie po nazwie
27    if (identyfikator.includes("REX")) {
28      return {
29        nazwa: identyfikator,
30        gatunek: "Tyrannosaurus",
31        populacja: 1
32      } as unknown as T;
33    } else {
34      return {
35        nazwa: identyfikator,
36        typ: "Sektor",
37        pojemność: 12
38      } as unknown as T;
39    }
40  }
41}
42
43// Interfejsy do typowania
44interface Dinozaur {
45  id: number;
46  gatunek: string;
47  wiek: number;
48  waga: number;
49}
50
51interface Pracownik {
52  id: number;
53  imie: string;
54  nazwisko: string;
55  stanowisko: string;
56}
57
58interface Gatunek {
59  nazwa: string;
60  gatunek: string;
61  populacja: number;
62}
63
64// Użycie z jawnym określeniem typu generycznego
65const trex = pobierzDane<Dinozaur>(1);
66console.log(trex.wiek);  // OK, TypeScript wie, że to Dinozaur
67
68const pracownik = pobierzDane<Pracownik>(101);
69console.log(pracownik.stanowisko);  // OK, TypeScript wie, że to Pracownik
70
71const gatunek = pobierzDane<Gatunek>("T-REX-01");
72console.log(gatunek.populacja);  // OK, TypeScript wie, że to Gatunek

Dobre praktyki i pułapki

1. Sygnatura implementacji musi być kompatybilna z wszystkimi sygnaturami przeciążeń

Parametry w sygnaturze implementacji muszą być tak zdefiniowane, aby mogły obsłużyć wszystkie możliwe kombinacje z sygnatur przeciążeń:

1// DOBRZE:
2function testuj(a: string): number;
3function testuj(a: number, b: boolean): string;
4function testuj(a: string | number, b?: boolean): number | string {
5  // Implementacja
6  return 0 as any;
7}
8
9// ŹLE:
10function zle(a: string): number;
11function zle(a: number, b: boolean): string;
12// Błąd: parametr 'b' w implementacji musi być opcjonalny
13function zle(a: string | number, b: boolean): number | string {
14  return 0 as any;
15}

2. Sygnatury przeciążeń są sprawdzane po kolei

TypeScript sprawdza sygnatury przeciążeń od góry do dołu, więc bardziej specyficzne sygnatury powinny być definiowane przed bardziej ogólnymi:

1// ŹLE - bardziej ogólna sygnatura przesłania bardziej szczegółową
2function niewłaściwaKolejność(dane: any[]): number;
3function niewłaściwaKolejność(dane: number[]): string;  // Ta sygnatura nigdy nie zostanie dopasowana
4function niewłaściwaKolejność(dane: any[]): number | string {
5  if (Array.isArray(dane) && dane.every(d => typeof d === "number")) {
6    return "Tablica liczb";
7  } else {
8    return dane.length;
9  }
10}
11
12// DOBRZE - bardziej specyficzna sygnatura jest pierwsza
13function właściwaKolejność(dane: number[]): string;
14function właściwaKolejność(dane: any[]): number;
15function właściwaKolejność(dane: any[]): number | string {
16  if (Array.isArray(dane) && dane.every(d => typeof d === "number")) {
17    return "Tablica liczb";
18  } else {
19    return dane.length;
20  }
21}

3. Wyważenie między przeciążaniem a parametrami opcjonalnymi i typami unii

Czasami lepszym rozwiązaniem mogą być parametry opcjonalne lub typy unii, zamiast rozbudowanych przeciążeń:

1// Zamiast wielu przeciążeń:
2function opcja1(id: number): void;
3function opcja1(id: number, tryb: string): void;
4function opcja1(id: number, tryb: string, extra: boolean): void;
5// ...i wiele więcej wersji
6
7// Lepiej użyć parametrów opcjonalnych:
8function opcja2(id: number, tryb?: string, extra?: boolean): void {
9  // Implementacja
10}

Praktyczny przykład: System zarządzania Parkiem Jurajskim

Zobaczmy kompleksowy przykład, jak przeciążanie funkcji można wykorzystać w systemie zarządzania Parkiem Jurajskim:

1// Definiowanie typów używanych w systemie
2type StatusDinozaura = "zdrowy" | "chory" | "w leczeniu" | "w śpiączce";
3type GatunekDinozaura = "Tyrannosaurus" | "Velociraptor" | "Triceratops" | "Brachiosaurus";
4type PoziomZagrożenia = 1 | 2 | 3 | 4 | 5;
5
6interface DanePodstawowe {
7  id: number;
8  nazwa: string;
9  gatunek: GatunekDinozaura;
10  status: StatusDinozaura;
11  poziomZagrożenia: PoziomZagrożenia;
12}
13
14interface DaneZdrowotne {
15  temperaturaCiała: number;
16  tętno: number;
17  czynnikStresowy: number;
18  ostatniPosiłek: Date;
19  wagaKg: number;
20}
21
22interface DaneLokalizacyjne {
23  sektor: string;
24  współrzędneGPS: [number, number];
25  ostatniaAktualizacja: Date;
26}
27
28interface RaportBezpieczeństwa {
29  statusOgrodzenia: "sprawne" | "uszkodzone" | "w naprawie";
30  zasilanieAktywne: boolean;
31  osobyWSektorze: number;
32  alertyAktywne: string[];
33}
34
35// Klasa do zarządzania dinozaurami z przeciążonymi metodami
36class SystemZarządzaniaDinozaurami {
37  private bazaDanych: Map<number, {
38    podstawowe: DanePodstawowe;
39    zdrowotne?: DaneZdrowotne;
40    lokalizacyjne?: DaneLokalizacyjne;
41    bezpieczeństwo?: RaportBezpieczeństwa;
42  }> = new Map();
43
44  // Metoda dodająca dinozaura do systemu
45  dodajDinozaura(dane: DanePodstawowe): number;
46  dodajDinozaura(dane: DanePodstawowe, zdrowotne: DaneZdrowotne): number;
47  dodajDinozaura(dane: DanePodstawowe, zdrowotne: DaneZdrowotne, lokalizacja: DaneLokalizacyjne): number;
48  dodajDinozaura(
49    dane: DanePodstawowe,
50    zdrowotne?: DaneZdrowotne,
51    lokalizacja?: DaneLokalizacyjne
52  ): number {
53    const nowyRekord = {
54      podstawowe: dane,
55      zdrowotne,
56      lokalizacyjne: lokalizacja
57    };
58
59    this.bazaDanych.set(dane.id, nowyRekord);
60    console.log(`Dodano dinozaura: ${dane.nazwa} (ID: ${dane.id})`);
61    return dane.id;
62  }
63
64  // Przeciążona metoda do pobierania danych o dinozaurze
65  pobierzDane(id: number): DanePodstawowe;
66  pobierzDane(id: number, typ: "zdrowie"): DaneZdrowotne | undefined;
67  pobierzDane(id: number, typ: "lokalizacja"): DaneLokalizacyjne | undefined;
68  pobierzDane(id: number, typ: "bezpieczeństwo"): RaportBezpieczeństwa | undefined;
69  pobierzDane(id: number, typ: "wszystko"): {
70    podstawowe: DanePodstawowe;
71    zdrowotne?: DaneZdrowotne;
72    lokalizacyjne?: DaneLokalizacyjne;
73    bezpieczeństwo?: RaportBezpieczeństwa;
74  };
75  pobierzDane(id: number, typ?: "zdrowie" | "lokalizacja" | "bezpieczeństwo" | "wszystko"): any {
76    const rekord = this.bazaDanych.get(id);
77
78    if (!rekord) {
79      throw new Error(`Dinozaur o ID ${id} nie istnieje w systemie.`);
80    }
81
82    if (!typ) {
83      return rekord.podstawowe;
84    }
85
86    switch (typ) {
87      case "zdrowie":
88        return rekord.zdrowotne;
89      case "lokalizacja":
90        return rekord.lokalizacyjne;
91      case "bezpieczeństwo":
92        return rekord.bezpieczeństwo;
93      case "wszystko":
94        return rekord;
95    }
96  }
97
98  // Przeciążona metoda do aktualizacji danych
99  aktualizujDane(id: number, dane: Partial<DanePodstawowe>): boolean;
100  aktualizujDane(id: number, dane: DaneZdrowotne, typ: "zdrowie"): boolean;
101  aktualizujDane(id: number, dane: DaneLokalizacyjne, typ: "lokalizacja"): boolean;
102  aktualizujDane(id: number, dane: RaportBezpieczeństwa, typ: "bezpieczeństwo"): boolean;
103  aktualizujDane(
104    id: number,
105    dane: Partial<DanePodstawowe> | DaneZdrowotne | DaneLokalizacyjne | RaportBezpieczeństwa,
106    typ?: "zdrowie" | "lokalizacja" | "bezpieczeństwo"
107  ): boolean {
108    const rekord = this.bazaDanych.get(id);
109
110    if (!rekord) {
111      console.error(`Dinozaur o ID ${id} nie istnieje w systemie.`);
112      return false;
113    }
114
115    if (!typ) {
116      // Aktualizacja danych podstawowych
117      rekord.podstawowe = { ...rekord.podstawowe, ...dane as Partial<DanePodstawowe> };
118      console.log(`Zaktualizowano podstawowe dane dinozaura ID: ${id}`);
119    } else {
120      switch (typ) {
121        case "zdrowie":
122          rekord.zdrowotne = { ...(rekord.zdrowotne || {}), ...dane as DaneZdrowotne };
123          console.log(`Zaktualizowano dane zdrowotne dinozaura ID: ${id}`);
124          break;
125        case "lokalizacja":
126          rekord.lokalizacyjne = { ...(rekord.lokalizacyjne || {}), ...dane as DaneLokalizacyjne };
127          console.log(`Zaktualizowano dane lokalizacyjne dinozaura ID: ${id}`);
128          break;
129        case "bezpieczeństwo":
130          rekord.bezpieczeństwo = { ...(rekord.bezpieczeństwo || {}), ...dane as RaportBezpieczeństwa };
131          console.log(`Zaktualizowano dane bezpieczeństwa dla dinozaura ID: ${id}`);
132          break;
133      }
134    }
135
136    this.bazaDanych.set(id, rekord);
137    return true;
138  }
139
140  // Przeciążona metoda do generowania raportów
141  generujRaport(): string; // Raport dla wszystkich dinozaurów
142  generujRaport(gatunek: GatunekDinozaura): string; // Raport dla wybranego gatunku
143  generujRaport(poziomZagrożenia: PoziomZagrożenia): string; // Raport dla poziomu zagrożenia
144  generujRaport(filter?: GatunekDinozaura | PoziomZagrożenia): string {
145    let dinozaury = Array.from(this.bazaDanych.values()).map(d => d.podstawowe);
146
147    if (filter) {
148      if (typeof filter === "string") {
149        // Filtrowanie po gatunku
150        dinozaury = dinozaury.filter(d => d.gatunek === filter);
151        return this.formatujRaport(`Raport dla gatunku: ${filter}`, dinozaury);
152      } else {
153        // Filtrowanie po poziomie zagrożenia
154        dinozaury = dinozaury.filter(d => d.poziomZagrożenia === filter);
155        return this.formatujRaport(`Raport dla poziomu zagrożenia: ${filter}`, dinozaury);
156      }
157    }
158
159    return this.formatujRaport("Pełny raport wszystkich dinozaurów", dinozaury);
160  }
161
162  private formatujRaport(tytuł: string, dinozaury: DanePodstawowe[]): string {
163    let raport = `=== ${tytuł} ===
164`;
165    raport += `Data: ${new Date().toLocaleString()}
166`;
167    raport += `Liczba dinozaurów: ${dinozaury.length}
168
169`;
170
171    dinozaury.forEach(d => {
172      raport += `ID: ${d.id} | ${d.nazwa} | ${d.gatunek} | Status: ${d.status} | Zagrożenie: ${d.poziomZagrożenia}
173`;
174    });
175
176    return raport;
177  }
178}
179
180// Przykładowe użycie systemu
181const system = new SystemZarządzaniaDinozaurami();
182
183// Dodawanie dinozaurów z różnymi zestawami danych
184const rexId = system.dodajDinozaura({
185  id: 1,
186  nazwa: "Rexy",
187  gatunek: "Tyrannosaurus",
188  status: "zdrowy",
189  poziomZagrożenia: 5
190});
191
192const raptorId = system.dodajDinozaura(
193  {
194    id: 2,
195    nazwa: "Blue",
196    gatunek: "Velociraptor",
197    status: "zdrowy",
198    poziomZagrożenia: 4
199  },
200  {
201    temperaturaCiała: 38.5,
202    tętno: 85,
203    czynnikStresowy: 2.1,
204    ostatniPosiłek: new Date(Date.now() - 3600000), // 1 godzina temu
205    wagaKg: 160
206  }
207);
208
209const triceratopsId = system.dodajDinozaura(
210  {
211    id: 3,
212    nazwa: "Tricy",
213    gatunek: "Triceratops",
214    status: "zdrowy",
215    poziomZagrożenia: 2
216  },
217  {
218    temperaturaCiała: 37.2,
219    tętno: 45,
220    czynnikStresowy: 1.3,
221    ostatniPosiłek: new Date(Date.now() - 7200000), // 2 godziny temu
222    wagaKg: 8500
223  },
224  {
225    sektor: "B-7",
226    współrzędneGPS: [34.5678, -118.2345],
227    ostatniaAktualizacja: new Date()
228  }
229);
230
231// Pobieranie różnych typów danych
232const rexPodstawowe = system.pobierzDane(rexId);
233console.log(`Dinozaur ${rexPodstawowe.nazwa} ma poziom zagrożenia ${rexPodstawowe.poziomZagrożenia}`);
234
235const raptorZdrowie = system.pobierzDane(raptorId, "zdrowie");
236if (raptorZdrowie) {
237  console.log(`Temperatura ciała raptora: ${raptorZdrowie.temperaturaCiała}°C`);
238}
239
240const triceratopsLokalizacja = system.pobierzDane(triceratopsId, "lokalizacja");
241if (triceratopsLokalizacja) {
242  console.log(`Triceratops znajduje się w sektorze: ${triceratopsLokalizacja.sektor}`);
243}
244
245// Aktualizacja danych
246system.aktualizujDane(rexId, { status: "w leczeniu" });
247
248system.aktualizujDane(raptorId,
249  {
250    temperaturaCiała: 39.2,
251    tętno: 95,
252    czynnikStresowy: 4.5,
253    ostatniPosiłek: new Date(),
254    wagaKg: 162
255  },
256  "zdrowie"
257);
258
259// Generowanie raportów
260console.log(system.generujRaport());
261console.log(system.generujRaport("Velociraptor"));
262console.log(system.generujRaport(5));

Podsumowanie

Przeciążanie funkcji to potężny mechanizm TypeScript, który pozwala na tworzenie elastycznych interfejsów API, zachowując jednocześnie pełne bezpieczeństwo typów. Podobnie jak naukowcy w Parku Jurajskim muszą dostosowywać swoje procedury do różnych gatunków dinozaurów, tak programiści mogą tworzyć funkcje, które dostosowują się do różnych zestawów parametrów.

Najważniejsze zasady przeciążania funkcji:

  1. Definiuj sygnatury przeciążeń przed sygnaturą implementacji
  2. Sygnatura implementacji musi być kompatybilna ze wszystkimi sygnaturami przeciążeń
  3. Bardziej szczegółowe sygnatury umieszczaj przed bardziej ogólnymi
  4. Używaj przeciążeń, gdy chcesz mieć różne typy zwracane dla różnych parametrów wejściowych
  5. Rozważ alternatywy (parametry opcjonalne, typy unii) dla prostszych przypadków

"W programowaniu, podobnie jak w genetyce dinozaurów," mówi Dr. Wu z uśmiechem, "prawdziwa moc leży w zdolności do adaptacji. Przeciążanie funkcji to jak tworzenie adaptacyjnych sekwencji DNA - ta sama funkcja może wyewoluować, by służyć różnym celom, zachowując przy tym swoją integralność i bezpieczeństwo."

Przejdź do CodeWorlds