W Javie i C # można utworzyć obiekt z właściwościami, które można ustawić przy inicjalizacji, poprzez zdefiniowanie konstruktora za pomocą parametrów, zdefiniowanie każdej właściwości po skonstruowaniu obiektu lub użycie wzorca interfejsu konstruktora / płynu. Jednak C # 3 wprowadził inicjatory obiektów i kolekcji, co oznaczało, że wzorzec konstruktora był w dużej mierze bezużyteczny. W języku bez inicjatorów można zaimplementować konstruktor, a następnie użyć go w następujący sposób:
Vehicle v = new Vehicle.Builder()
.manufacturer("Toyota")
.model("Camry")
.year(1997)
.colour(CarColours.Red)
.addSpecialFeature(new Feature.CDPlayer())
.addSpecialFeature(new Feature.SeatWarmer(4))
.build();
I odwrotnie, w języku C # można napisać:
var vehicle = new Vehicle {
Manufacturer = "Toyota",
Model = "Camry",
Year = 1997,
Colour = CarColours.Red,
SpecialFeatures = new List<SpecialFeature> {
new Feature.CDPlayer(),
new Feature.SeatWarmer { Seats = 4 }
}
}
... eliminując potrzebę konstruktora, jak widać w poprzednim przykładzie.
Czy na podstawie tych przykładów konstruktory są nadal przydatne w języku C #, czy też zostały całkowicie zastąpione przez inicjatory?
c#
object-oriented
builder-pattern
svbnet
źródło
źródło
are builders useful
dlaczego ciągle widzę tego rodzaju pytania? Builder to wzorzec projektowy - na pewno może zostać ujawniony jako funkcja językowa, ale na koniec jest to tylko wzorzec projektowy. Wzorzec projektowy nie może być „zły”. Może, ale nie musi spełnić twojego przypadku użycia, ale to nie czyni całego wzoru błędnym, tylko jego zastosowanie. Pamiętaj, że pod koniec dnia wzorce projektowe rozwiązują określone problemy - jeśli nie napotykasz problemu, to dlaczego miałbyś go rozwiązać?Odpowiedzi:
Jak porusza @ user248215, prawdziwym problemem jest niezmienność. Powodem użycia konstruktora w języku C # byłoby zachowanie jawności inicjalizatora bez konieczności ujawniania ustawialnych właściwości. To nie jest kwestia enkapsulacji, dlatego napisałem własną odpowiedź. Hermetyzacja jest dość ortogonalna, ponieważ wywoływanie settera nie implikuje tego, co faktycznie robi setter, ani nie wiąże cię z jego implementacją.
Następna wersja C #, 8.0, prawdopodobnie wprowadzi
with
słowo kluczowe, które pozwoli na jasne i zwięzłe zainicjowanie niezmiennych obiektów bez konieczności pisania programów budujących.Inną interesującą rzeczą, którą możesz zrobić z konstruktorami, w przeciwieństwie do inicjalizatorów, jest to, że mogą one dawać różne typy obiektów w zależności od sekwencji wywoływanych metod.
Na przykład
Aby wyjaśnić powyższy przykład, jest to biblioteka dopasowywania wzorców, która używa wzorca konstruktora do zbudowania „dopasowania” poprzez określenie przypadków. Przypadki są dodawane przez wywołanie
Case
metody przekazującej jej funkcję. Jeślivalue
można go przypisać do typu parametru funkcji, jest on wywoływany. Pełny kod źródłowy można znaleźć na GitHub, a ponieważ komentarze XML są trudne do odczytania zwykłym tekstem, oto link do dokumentacji zbudowanej przez SandCastle (zobacz sekcję Uwagi )źródło
IEnumerable<Func<T, TResult>>
elementu.TResult
. Ostatecznie wnioskowanie typu miało z tym wiele wspólnego. Chciałem też, żeby „wyglądała” jak struktura kontrolna. Nie chciałem też używać inicjatora, ponieważ pozwoliłoby to na mutację.Inicjatory obiektów wymagają, aby właściwości były dostępne dla kodu wywołującego. Zagnieżdżeni konstruktorzy mogą uzyskiwać dostęp do prywatnych członków klasy.
Jeśli chcesz
Vehicle
ustawić wartość niezmienną (poprzez uczynienie wszystkich ustawiających prywatnymi), wówczas można użyć zagnieżdżonego konstruktora do ustawienia zmiennych prywatnych.źródło
Wszystkie służą innym celom !!!
Konstruktory mogą inicjować pola, które są oznaczone,
readonly
a także członków prywatnych i chronionych. Masz jednak ograniczone możliwości konstruktora; powinieneś na przykład unikać przechodzeniathis
do jakichkolwiek zewnętrznych metod i powinieneś unikać wywoływania wirtualnych członków, ponieważ mogą one być wykonywane w kontekście klasy pochodnej, która jeszcze się nie skonstruowała. Ponadto konstruktory mają zagwarantowane działanie (chyba że program wywołujący robi coś bardzo nietypowego), więc jeśli ustawisz pola w konstruktorze, kod reszty klasy może założyć, że pola te nie będą miały wartości zerowej.Inicjatory uruchamiane są po zakończeniu budowy. Pozwalają tylko wywoływać nieruchomości publiczne; pola prywatne i / lub tylko do odczytu nie mogą być ustawione. Zgodnie z konwencją czynność ustanawiania właściwości powinna być dość ograniczona, np. Powinna być idempotentna i mieć ograniczone skutki uboczne.
Metody konstruktora są rzeczywistymi metodami i dlatego pozwalają na więcej niż jeden argument, a zgodnie z konwencją mogą mieć skutki uboczne, w tym tworzenie obiektów. Chociaż nie mogą ustawiać pól tylko do odczytu, mogą właściwie robić cokolwiek innego. Ponadto metody mogą być implementowane jako metody rozszerzeń (podobnie jak prawie cała funkcjonalność LINQ).
źródło