Wzorce niezmienności
Część sekcji Programowanie obiektowe ścieżki C# w Coddy. Lekcja 34 z 70.
Niemutowalność doprowadza hermetyzację do jej logicznego ekstremum — gdy obiekt zostanie utworzony, nigdy nie może się zmienić. Eliminuje to całe kategorie błędów, ponieważ nie musisz się martwić, że coś nieoczekiwanie zmodyfikuje Twoje dane.
Najprostszym sposobem na utworzenie niezmiennej klasy jest użycie pól readonly z konstruktorem, który ustawia wszystkie wartości, i nieudostępnianie setterów:
public class Point
{
public readonly int X;
public readonly int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
}Po utworzeniu obiektu Point jego współrzędne nie mogą się zmienić. Co jednak, jeśli potrzebujesz zmodyfikowanej wersji? Zamiast zmieniać istniejący obiekt, zwracasz nowy:
public Point Move(int deltaX, int deltaY)
{
return new Point(X + deltaX, Y + deltaY);
}Pierwotny punkt pozostaje niezmieniony, a Ty otrzymujesz nowy punkt ze zaktualizowanymi wartościami. Ten wzorzec jest stosowany w całym .NET — na przykład ciągi znaków są niezmienne. Gdy wywołujesz ToUpper(), otrzymujesz nowy ciąg znaków, zamiast modyfikować oryginalny.
Obiekty niezmienne są z natury bezpieczne wątkowo, ponieważ żaden kod nie może ich zmodyfikować po utworzeniu. Łatwiej też zrozumieć ich działanie, ponieważ wiesz, że ich stan nigdy nie zmieni się nieoczekiwanie.
Wyzwanie
ŁatwyZbudujmy system obsługi pieniędzy z niezmiennymi obiektami, który pokazuje, jak projektować obiekty tak, aby nie zmieniały się po utworzeniu. Utworzysz klasę Money, w której każda operacja zwraca nowy obiekt zamiast modyfikować oryginalny — to wzorzec, dzięki któremu kod jest bezpieczniejszy i łatwiejszy do zrozumienia.
Podzielisz kod na dwa pliki:
Money.cs: Utwórz niezmienną klasęMoneyw przestrzeni nazwFinance. Obiekt przechowujący pieniądze powinien mieć:- Dwa pola
readonly:Amount(decimal) iCurrency(string) - Konstruktor, który ustawia obie wartości — po utworzeniu obiekt Money nie może się już zmienić
- Metodę
Add(decimal value), która zwraca nowy obiektMoneyze zwiększoną kwotą (ta sama waluta) - Metodę
Subtract(decimal value), która zwraca nowy obiektMoneyze zmniejszoną kwotą (ta sama waluta) - Metodę
Convert(string newCurrency, decimal rate), która zwraca nowy obiektMoneyz kwotą pomnożoną przez kurs i nową walutą - Metodę
GetDisplay(), która zwraca ciąg znaków w formacie"{Amount} {Currency}"
Money!- Dwa pola
Program.cs: W głównym pliku pokaż, jak działa niezmienność. Utwórz początkowy obiektMoney, a następnie wykonaj na nim operacje, zachowując odwołania zarówno do oryginału, jak i do wyników. Dzięki temu udowodnisz, że oryginalny obiekt nigdy się nie zmienia, nawet po wielu operacjach.
Otrzymasz cztery dane wejściowe:
- Początkowa kwota (decimal)
- Początkowa waluta (ciąg znaków)
- Kwota do dodania (decimal)
- Kurs wymiany na EUR (decimal)
Wyświetl wynik w tym formacie:
Original: {GetDisplay()}
After add: {GetDisplay()}
Original unchanged: {GetDisplay()}
Converted: {GetDisplay()}Na przykład, jeśli dane wejściowe to 100.00, USD, 50.00 i 0.85, wynik powinien być następujący:
Original: 100.00 USD
After add: 150.00 USD
Original unchanged: 100.00 USD
Converted: 127.50 EURZwróć uwagę, że oryginalny obiekt Money nadal pokazuje 100.00 USD nawet po wywołaniu Add() — na tym polega niezmienność! Metoda Add() utworzyła zupełnie nowy obiekt z wartością 150.00 USD, pozostawiając oryginał bez zmian. Konwersja jest wykonywana na wyniku operacji dodawania.
Spróbuj swoich sił
using System;
using Finance;
class Program
{
public static void Main(string[] args)
{
// Wczytaj dane wejściowe
decimal initialAmount = Convert.ToDecimal(Console.ReadLine());
string initialCurrency = Console.ReadLine();
decimal amountToAdd = Convert.ToDecimal(Console.ReadLine());
decimal conversionRate = Convert.ToDecimal(Console.ReadLine());
// TODO: Utwórz początkowy obiekt Money z początkową kwotą i walutą
// TODO: Wyświetl początkową kwotę za pomocą GetDisplay()
// TODO: Wywołaj Add() na początkowym obiekcie i zapisz wynik w nowej zmiennej
// TODO: Wyświetl wynik po dodaniu za pomocą GetDisplay()
// TODO: Ponownie wyświetl początkową kwotę, aby pokazać, że nie uległa zmianie
// TODO: Przelicz wynik dodawania na EUR, używając kursu wymiany
// TODO: Wyświetl przeliczoną kwotę za pomocą GetDisplay()
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrznePrzestrzenie nazw i dyrektywyWprowadzenie do klas i obiektówSłowo kluczowe „this”Metody i parametryPola a właściwościKonstruktoryInicjalizatory obiektówPodsumowanie — prosty kalkulator4Dziedziczenie
Podstawowa składnia dziedziczenia (:)Słowo kluczowe 'base'Słowa kluczowe virtual i overrideKlasy sealedBazowa klasa 'object'Powtórzenie – hierarchia pracowników7Zaawansowane funkcje
Przeciążanie operatorówIndeksery (this[])Nadpisywanie ToString()Metody rozszerzającePodsumowanie – własna lista2Właściwości i składowe statyczne
Właściwości automatyczneWłaściwości tylko do odczytu i zapisuPola i metody statyczneKlasy statyczneSkładowe z ciałem wyrażeniowym5Polimorfizm i interfejsy
Polimorfizm czasu kompilacji a czasu wykonaniaInterfejs a klasa abstrakcyjnaWiele interfejsówJawne implementowanie interfejsówRzutowanie w górę i w dółPowtórka — kalkulator kształtów8Zaawansowane koncepcje OOP
Kompozycja zamiast dziedziczeniaTypy generyczne (klasy i metody)Delegaty i zdarzeniaAtrybuty i refleksjaIDisposable i instrukcja usingPodstawy wstrzykiwania zależności11Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytu3Architektura klas
Dane instancji a dane statyczneSłowa kluczowe 'readonly' i 'const'Pola zapasowePowtórzenie — menedżer kont bankowych6Hermetyzacja
Modyfikatory dostępuWłaściwości w hermetyzacjiImplementacja ukrywania danychWzorce niezmiennościPowtórka — dane studentów9Argumenty zmiennej liczby
Słowo kluczowe „params”Parametry opcjonalneArgumenty nazwanePrzeciążanie metodPoćwicz samodzielnie: Kompilator C# online