Zaczynamy standardowo od czystej definicji zasady:
-
Kod z warstw z wyższego poziomu nie powinien zależeć od kodu z niższych warstw. Obie warstwy za to powinny być zależne od abstrakcji.
-
Abstrakcje nie powinny zależeć od szczegółów (konkretnej implementacji). Z kolei szczegóły (implementacja) powinna zależeć od abstrakcji.
Najlepiej rozważmy to na przykładzie aplikacji enterprise. Kodem z niższej warstwy może być np. DAL (warstwa dostępu do danych) a z wyższej – warstwa biznesowa. Według zasady, warstwa biznesowa nie może zawierać bezpośrednio referencji do DAL:
class BusinessLayer { private DataContext m_DataContext=new DataContext(); public void SubmitOrder() { m_DataContext.Orders.Add( new Order()); } }
Wyobraźmy sobie, że pewnego dnia zmienia się dostawca warstwy dostępowej (np. z nHibernate na EntityFramework). Wtedy warstwa wyższego poziomu (biznesowa) musi również zostać zaktualizowana. Ponadto ze względu na zmianę kodu ponownie należy przetestować klasę. Zgodnie z zasadą kod powinien być zależny wyłącznie od abstrakcji a nie szczegółów:
class BusinessLayer { private IDataContext m_DataContext=null; public BusinessLayer(IDataContext context) { m_DataContext=context; } public void SubmitOrder() { m_DataContext.Orders.Add( new Order()); } }
Ewentualna zmiana DAL wymaga tylko wstrzyknięcia poprzez konstruktor konkretnej implementacji. Innym rozwiązaniem jest skorzystanie z wzorca fabryka, plugin lub lokalizator.
Czy należy stosować interface’y w ramach tej samej warstwy? Różne źródła różnie do tego podchodzą. Chodzi przykładowo o sytuację, gdzie w relacji mamy pracownika i pracodawcę.