Chociaż jest to pytanie ogólne, jest ono również specyficzne dla problemu, którego obecnie doświadczam. Obecnie w interfejsie mam określony interfejs o nazwie
public interface IContextProvider
{
IDataContext { get; set; }
IAreaContext { get; set; }
}
Ten interfejs jest często używany w całym programie, dlatego mam łatwy dostęp do potrzebnych mi obiektów. Jednak na dość niskim poziomie części mojego programu potrzebuję dostępu do innej klasy, która użyje IAreaContext i wykona niektóre operacje poza tym. Więc stworzyłem inny interfejs fabryczny, aby wykonać to stworzenie o nazwie:
public interface IEventContextFactory
{
IEventContext CreateEventContext(int eventId);
}
Mam klasę, która implementuje IContextProvider i jest wstrzykiwana przy użyciu NinJect. Problem, który mam, polega na tym, że obszar, w którym muszę użyć tego IEventContextFactory, ma dostęp tylko do IContextProvider i sam używa innej klasy, która będzie potrzebowała tego nowego interfejsu. Nie chcę tworzyć instancji tej implementacji IEventContextFactory na niskim poziomie i wolę raczej pracować z interfejsem IEventContextFactory . Jednak nie chcę też wstrzykiwać innego parametru przez konstruktory, aby przekazać go do klasy, która tego potrzebuje, tj
// example of problem
public class MyClass
{
public MyClass(IContextProvider context, IEventContextFactory event)
{
_context = context;
_event = event;
}
public void DoSomething()
{
// the only place _event is used in the class is to pass it through
var myClass = new MyChildClass(_event);
myClass.PerformCalculation();
}
}
Więc moje główne pytanie brzmi: czy byłoby to do przyjęcia, czy jest to nawet powszechna lub dobra praktyka, aby zrobić coś takiego (interfejs rozszerza inny interfejs):
public interface IContextProvider : IEventContextFactory
czy powinienem rozważyć lepszą alternatywę dla osiągnięcia tego, czego potrzebuję. Jeśli nie podałem wystarczających informacji, aby podać sugestie, daj mi znać, a mogę podać więcej.
źródło
Odpowiedzi:
Chociaż interfejsy opisują tylko zachowanie bez żadnej implementacji, nadal mogą uczestniczyć w hierarchiach dziedziczenia. Jako przykład wyobraź sobie, że masz, powiedzmy, wspólną ideę
Collection
. Ten interfejs zapewnia kilka rzeczy, które są wspólne dla wszystkich kolekcji, na przykład metodę iteracji elementów. Następnie możesz pomyśleć o bardziej szczegółowych kolekcjach obiektów, takich jak aList
lub aSet
. Każdy z nich dziedziczy wszystkie wymagania dotyczące zachowaniaCollection
, ale dodaje dodatkowe wymagania specyficzne dla tego konkretnego typu kolekcji.Ilekroć ma sens tworzenie hierarchii dziedziczenia dla interfejsów, powinieneś to zrobić. Jeśli dwa interfejsy zawsze będą znajdować się razem, prawdopodobnie powinny zostać połączone.
źródło
Tak, ale tylko jeśli ma to sens. Zadaj sobie następujące pytania, a jeśli jedno lub więcej ma zastosowanie, wówczas można odziedziczyć interfejs od innego.
W twoim przykładzie nie sądzę, że odpowiedź na którekolwiek z nich brzmi „tak”. Lepiej dodaj ją jako inną właściwość:
źródło
IContextProvider
rozszerzyszIEventContextFactory
,ContextProvider
implementacja będzie musiała również zaimplementować tę metodę. Jeśli dodasz go jako właściwość, mówisz, żeContextProvider
ma,IEventContextFactory
ale tak naprawdę nie zna szczegółów implementacji.Tak, dobrze jest rozszerzyć jeden interfejs z drugiego.
Pytanie, czy jest to właściwe w konkretnym przypadku, zależy od tego, czy istnieje prawdziwy związek między nimi.
Co jest wymagane do prawidłowego związku „jest-a”?
Po pierwsze, musi istnieć prawdziwa różnica między dwoma interfejsami, tzn. Muszą istnieć instancje, które implementują interfejs nadrzędny, ale nie interfejs podrzędny. Jeśli nie ma i nigdy nie będzie instancji, które nie implementują obu interfejsów, wówczas dwa interfejsy powinny zostać połączone.
Po drugie, musi się zdarzyć, że każda instancja interfejsu potomnego musi koniecznie być instancją nadrzędną: nie ma (rozsądnej) logiki, w której stworzyłbyś instancję interfejsu potomnego, która nie miałaby sensu jako instancja interfejs nadrzędny.
Jeśli oba są prawdziwe, to dziedziczenie jest właściwą relacją dla dwóch interfejsów.
źródło
IReadOnlyList
może być przydatny, nawet jeśli implementują wszystkie moje klasy listIList
(z których pochodziIReadOnlyList
).Jeśli jeden interfejs zawsze chce wymagać / udostępniać drugi, to śmiało. Widziałem to w kilku przypadkach z
IDisposible
które miały sens. Ogólnie rzecz biorąc, powinieneś jednak upewnić się, że przynajmniej jeden z interfejsów zachowuje się bardziej jak cecha niż odpowiedzialność.W twoim przykładzie nie jest jasne. Jeśli oba są zawsze razem, po prostu stwórz jeden interfejs. Jeśli jedno zawsze potrzebuje drugiego, martwiłbym się dziedziczeniem „X i 1”, które bardzo często prowadzi do wielu obowiązków i / lub innych nieszczelnych problemów związanych z abstrakcją.
źródło