Wszystko, co implementuje I3, będzie musiało zaimplementować I1 i I2, a obiekty z różnych klas dziedziczących I3 mogą być następnie używane zamiennie (patrz poniżej), więc czy słusznie jest nazywać I3 pustym? Jeśli tak, jaki byłby lepszy sposób na architekturę?
„Wiązanie” I1i I2do I3oferuje ładny (?) Skrót lub jakiś cukier składniowy, gdziekolwiek chcesz, aby oba były zaimplementowane.
Problem z tym podejściem polega na tym, że nie można powstrzymać innych programistów przed bezpośrednim wdrożeniem I1 i jednoczesnym wdrażaniem I2, ale nie działa to w obie strony - chociaż : I3jest równoważne : I1, I2, : I1, I2nie jest równoważne z : I3. Nawet jeśli są funkcjonalnie identyczne, klasa obsługuje oba te elementy I1i I2nie zostanie wykryta jako obsługująca I3.
Oznacza to, że oferujesz dwa sposoby osiągnięcia tego samego - „ręczny” i „słodki” (z cukrem składniowym). I może to mieć zły wpływ na spójność kodu. Oferowanie 2 różnych sposobów, aby osiągnąć to samo tutaj, tam, w innym miejscu, daje już 8 możliwych kombinacji :)
void foo() {
I1 something = new A();
something = new B();
something.SomeI1Function();
something.SomeI2Function(); // we can't do this without casting first
}
OK, nie możesz. Ale może to właściwie dobry znak?
Jeśli I1i I2pociągają za sobą różne obowiązki (w przeciwnym razie nie byłoby potrzeby ich rozdzielania w pierwszej kolejności), to może foo()błędem jest próba somethingwykonania dwóch różnych obowiązków za jednym razem?
Innymi słowy, może być tak, że foo()samo narusza zasadę pojedynczej odpowiedzialności, a fakt, że wymaga sprawdzenia typu rzutowania i wykonania w celu jego uruchomienia, ostrzega nas o tym czerwoną flagą.
EDYTOWAĆ:
Jeśli nalegałeś, aby upewnić się, że foopobiera coś, co implementuje I1i I2, możesz przekazać je jako osobne parametry:
void foo(I1 i1, I2 i2)
I mogą być tym samym przedmiotem:
foo(something, something);
Co fooobchodzi, czy są to te same wystąpienia, czy nie?
Ale jeśli to obchodzi (np. Ponieważ nie są bezpaństwowcami) lub po prostu uważasz, że to wygląda brzydko, zamiast tego można użyć ogólnych:
void Foo<T>(T something) where T: I1, I2
Teraz ogólne ograniczenia dbają o pożądany efekt, nie zanieczyszczając jednak świata zewnętrznego niczym. Jest to idiomatyczny język C #, oparty na kontrolach czasu kompilacji, i ogólnie wydaje się bardziej odpowiedni.
Widzę I3 : I2, I1trochę jako próba naśladować typów związków w języku, który nie obsługuje go z pudełka (C # lub Java nie, natomiast typy związkowi istnieć w językach funkcyjnych).
Próba emulacji konstrukcji, która tak naprawdę nie jest obsługiwana przez język, jest często niezręczna. Podobnie w języku C # możesz użyć metod rozszerzenia i interfejsów, jeśli chcesz emulować mixiny . Możliwy? Tak. Dobry pomysł? Nie jestem pewny.