W naszym Parku Jurajskim, bezpieczeństwo i kontrola są kluczowe. Możemy mieć najlepszych genetyków i najnowocześniejsze wybiegi, ale jeśli nie potrafimy prawidłowo zarządzać dostępem do danych i kontrolować, jak informacje są modyfikowane, możemy szybko stracić kontrolę nad parkiem. Właśnie w tym miejscu gettery i settery przychodzą z pomocą!
Gettery i settery to specjalne metody, które pozwalają na kontrolowany dostęp do właściwości obiektów. Zamiast bezpośrednio odczytywać czy modyfikować dane, używamy specjalnych metod, które mogą zawierać dodatkową logikę, walidację czy transformację danych.
W JavaScript ES6, gettery i settery są zdefiniowane za pomocą słów kluczowych
get i set.Getter to metoda, która służy do pobierania wartości właściwości. Definiujemy go za pomocą słowa kluczowego
get:1class Dinosaur {
2 constructor(name, species, dangerLevel) {
3 this._name = name;
4 this._species = species;
5 this._dangerLevel = dangerLevel;
6 this._isContained = true;
7 }
8
9 // Getter dla właściwości name
10 get name() {
11 return this._name;
12 }
13
14 // Getter dla właściwości species
15 get species() {
16 return this._species;
17 }
18
19 // Getter dla właściwości dangerLevel
20 get dangerLevel() {
21 return this._dangerLevel;
22 }
23
24 // Getter dla właściwości isContained
25 get isContained() {
26 return this._isContained;
27 }
28}
29
30const velociraptor = new Dinosaur("Blue", "Velociraptor", 8);
31
32// Używanie getterów - zauważ, że wyglądają jak zwykłe właściwości!
33console.log(velociraptor.name); // "Blue"
34console.log(velociraptor.species); // "Velociraptor"
35console.log(velociraptor.dangerLevel); // 8
36console.log(velociraptor.isContained); // trueZwróć uwagę na konwencję nazywania prywatnych właściwości z podkreślnikiem (_). Chociaż JavaScript nie ma prawdziwych prywatnych właściwości w tradycyjnym sensie (choć nowsze wersje wprowadzają prywatne pola z symbolem #), ta konwencja jest powszechnie stosowana, aby oznaczyć, że właściwość powinna być używana tylko wewnątrz klasy.
Setter to metoda, która służy do ustawiania wartości właściwości. Definiujemy go za pomocą słowa kluczowego
set:1class Dinosaur {
2 constructor(name, species, dangerLevel) {
3 this._name = name;
4 this._species = species;
5 this._dangerLevel = dangerLevel;
6 this._isContained = true;
7 }
8
9 // Gettery...
10
11 // Setter dla właściwości name
12 set name(newName) {
13 if (newName.length > 0) {
14 this._name = newName;
15 } else {
16 console.error("Nazwa nie może być pusta!");
17 }
18 }
19
20 // Setter dla właściwości dangerLevel
21 set dangerLevel(level) {
22 if (level >= 1 && level <= 10) {
23 this._dangerLevel = level;
24 } else {
25 console.error("Poziom zagrożenia musi być między 1 a 10!");
26 }
27 }
28
29 // Setter dla właściwości isContained
30 set isContained(status) {
31 // Tylko autoryzowany personel może zmienić status zamknięcia dinozaura
32 if (ParkSecurity.hasAuthorization("enclosure_control")) {
33 this._isContained = status;
34
35 // Jeśli dinozaur nie jest już zamknięty, aktywuj alarm
36 if (!status) {
37 ParkSecurity.activateAlarm(this.species, this.name);
38 }
39 } else {
40 console.error("Brak autoryzacji do kontroli zamknięcia! Incydent zostanie zgłoszony!");
41 ParkSecurity.reportUnauthorizedAccess();
42 }
43 }
44}
45
46const velociraptor = new Dinosaur("Blue", "Velociraptor", 8);
47
48// Używanie setterów
49velociraptor.name = "Echo"; // Zmienia nazwę na "Echo"
50console.log(velociraptor.name); // "Echo"
51
52velociraptor.dangerLevel = 9; // Zmienia poziom zagrożenia na 9
53console.log(velociraptor.dangerLevel); // 9
54
55velociraptor.dangerLevel = 11; // Błąd: "Poziom zagrożenia musi być między 1 a 10!"
56console.log(velociraptor.dangerLevel); // 9 (bez zmian)
57
58// Bez autoryzacji, nie możesz zmienić statusu zamknięcia
59velociraptor.isContained = false; // Błąd: "Brak autoryzacji do kontroli zamknięcia! Incydent zostanie zgłoszony!"W tym przykładzie, settery dodają warstwę walidacji i kontroli dostępu przed zmianą wartości właściwości. Zapewniają, że dane są spójne i odpowiadają naszym wymaganiom biznesowym.
Gettery mogą zawierać bardziej złożoną logikę i zwracać obliczone wartości:
1class DinosaurEnclosure {
2 constructor(id, capacity) {
3 this._id = id;
4 this._capacity = capacity;
5 this._dinosaurs = [];
6 }
7
8 addDinosaur(dino) {
9 if (this._dinosaurs.length < this._capacity) {
10 this._dinosaurs.push(dino);
11 return true;
12 }
13 return false;
14 }
15
16 get id() {
17 return this._id;
18 }
19
20 get dinosaurs() {
21 return [...this._dinosaurs]; // Zwraca kopię tablicy, aby oryginał nie mógł być modyfikowany
22 }
23
24 get capacity() {
25 return this._capacity;
26 }
27
28 // Getter obliczanej właściwości
29 get occupancyRate() {
30 return (this._dinosaurs.length / this._capacity) * 100;
31 }
32
33 // Getter obliczanej właściwości
34 get dangerLevel() {
35 if (this._dinosaurs.length === 0) return 0;
36
37 // Obliczamy średni poziom zagrożenia wszystkich dinozaurów w tym wybiegu
38 const totalDanger = this._dinosaurs.reduce((sum, dino) => sum + dino.dangerLevel, 0);
39 const averageDanger = totalDanger / this._dinosaurs.length;
40
41 // Jeśli mamy więcej niż 80% pojemności, zwiększamy poziom zagrożenia
42 if (this.occupancyRate > 80) {
43 return Math.min(10, averageDanger * 1.5); // Maksymalnie 10
44 }
45
46 return averageDanger;
47 }
48
49 // Getter obliczanej właściwości
50 get securityRequirements() {
51 const baseRequirement = {
52 guards: 1,
53 cameras: 2,
54 sensors: 5
55 };
56
57 // Zwiększamy wymagania bezpieczeństwa w zależności od poziomu zagrożenia
58 const dangerMultiplier = Math.ceil(this.dangerLevel / 2);
59
60 return {
61 guards: baseRequirement.guards * dangerMultiplier,
62 cameras: baseRequirement.cameras * dangerMultiplier,
63 sensors: baseRequirement.sensors * dangerMultiplier
64 };
65 }
66}
67
68// Tworzenie wybiegu i dodawanie dinozaurów
69const raptorEnclosure = new DinosaurEnclosure("RE-01", 4);
70
71const blue = new Dinosaur("Blue", "Velociraptor", 8);
72const delta = new Dinosaur("Delta", "Velociraptor", 7);
73const echo = new Dinosaur("Echo", "Velociraptor", 7);
74
75raptorEnclosure.addDinosaur(blue);
76raptorEnclosure.addDinosaur(delta);
77raptorEnclosure.addDinosaur(echo);
78
79// Używanie getterów
80console.log(raptorEnclosure.occupancyRate); // 75
81console.log(raptorEnclosure.dangerLevel); // 7.33...
82console.log(raptorEnclosure.securityRequirements); // { guards: 4, cameras: 8, sensors: 20 }W powyższym przykładzie, gettery
occupancyRate, dangerLevel i securityRequirements dynamicznie obliczają wartości na podstawie aktualnego stanu obiektu.Settery mogą również implementować złożoną logikę biznesową:
1class ParkVisitor {
2 constructor(name, age) {
3 this._name = name;
4 this._age = age;
5 this._visitedAttractions = new Set();
6 this._riskTolerance = age < 18 ? "low" : "medium";
7 this._accessLevel = "standard";
8 }
9
10 get name() {
11 return this._name;
12 }
13
14 get age() {
15 return this._age;
16 }
17
18 get riskTolerance() {
19 return this._riskTolerance;
20 }
21
22 get accessLevel() {
23 return this._accessLevel;
24 }
25
26 get visitedAttractions() {
27 return [...this._visitedAttractions];
28 }
29
30 // Setter dla poziomu ryzyka z walidacją
31 set riskTolerance(level) {
32 const validLevels = ["low", "medium", "high", "extreme"];
33
34 if (!validLevels.includes(level)) {
35 throw new Error("Nieprawidłowy poziom tolerancji ryzyka!");
36 }
37
38 // Niepełnoletni goście mogą mieć co najwyżej średni poziom ryzyka
39 if (this._age < 18 && (level === "high" || level === "extreme")) {
40 console.warn("Niepełnoletni gość nie może mieć wysokiego lub ekstremalnego poziomu ryzyka!");
41 this._riskTolerance = "medium";
42 } else {
43 this._riskTolerance = level;
44 }
45
46 // Automatycznie aktualizujemy poziom dostępu na podstawie tolerancji ryzyka
47 this._updateAccessLevel();
48 }
49
50 // Prywatna metoda do aktualizacji poziomu dostępu
51 _updateAccessLevel() {
52 switch (this._riskTolerance) {
53 case "low":
54 this._accessLevel = "standard";
55 break;
56 case "medium":
57 this._accessLevel = "advanced";
58 break;
59 case "high":
60 this._accessLevel = "premium";
61 break;
62 case "extreme":
63 this._accessLevel = "all-access";
64 break;
65 }
66 }
67
68 // Setter dla wieku, który aktualizuje również tolerancję ryzyka
69 set age(newAge) {
70 if (newAge <= 0 || newAge > 120) {
71 throw new Error("Nieprawidłowy wiek!");
72 }
73
74 const wasUnderage = this._age < 18;
75 const isUnderage = newAge < 18;
76
77 this._age = newAge;
78
79 // Jeśli gość stał się niepełnoletni, dostosowujemy tolerancję ryzyka
80 if (!wasUnderage && isUnderage && (this._riskTolerance === "high" || this._riskTolerance === "extreme")) {
81 console.warn("Dostosowanie tolerancji ryzyka dla niepełnoletniego gościa.");
82 this._riskTolerance = "medium";
83 this._updateAccessLevel();
84 }
85 }
86}
87
88// Tworzenie gościa
89const visitor = new ParkVisitor("John Hammond", 68);
90
91// Używanie getterów i setterów
92console.log(visitor.riskTolerance); // "medium"
93console.log(visitor.accessLevel); // "advanced"
94
95visitor.riskTolerance = "high";
96console.log(visitor.riskTolerance); // "high"
97console.log(visitor.accessLevel); // "premium"
98
99visitor.age = 16; // Zmiana wieku na niepełnoletni
100console.log(visitor.age); // 16
101console.log(visitor.riskTolerance); // "medium" (automatycznie zmienione)
102console.log(visitor.accessLevel); // "advanced" (automatycznie zmienione)
103
104try {
105 visitor.riskTolerance = "extreme"; // Próba ustawienia ekstremalnego poziomu dla niepełnoletniego
106} catch (error) {
107 console.error(error.message);
108}
109console.log(visitor.riskTolerance); // "medium" (bez zmian)W tym przykładzie, setter
riskTolerance zawiera logikę biznesową, która zapewnia, że niepełnoletni goście nie mogą mieć wysokiego lub ekstremalnego poziomu tolerancji ryzyka. Dodatkowo, zmiana tolerancji ryzyka automatycznie aktualizuje poziom dostępu gościa.Gettery i settery to potężne narzędzia w programowaniu obiektowym, które pozwalają na lepszą kontrolę nad danymi i zachowaniem obiektów. W kontekście Parku Jurajskiego, odpowiednie użycie getterów i setterów może być różnicą między bezpiecznym parkiem a... no cóż, sceną z filmu, gdy T-Rex biega swobodnie po parku!
W następnym ćwiczeniu, poznamy dziedziczenie klas, które pozwoli nam tworzyć hierarchie różnych typów dinozaurów i systemów parku.