2008-12-04 11 views
10

Rozumiem, że programowanie do interfejsów pomaga w luźnym sprzężeniu. Czy istnieje jednak wytyczna, która wyjaśnia, kiedy jest najbardziej skuteczna?Programowanie do interfejsu. Jak zdecydować, gdzie jest to potrzebne?

Na przykład mam prostą aplikację internetową, która zbiera dane o pracownikach, ich planach szkoleniowych, wydatkach i oblicza ich wydatki na rok itp. Jest to dość prosta aplikacja i mogę oczywiście użyć interfejsu, ale zastanawiam się, czy byłoby jakiekolwiek wykorzystanie. Będę go używał ze względu na jego użycie.

Zawsze można argumentować, że wraz ze wzrostem złożoności aplikacji i przekazywaniem obiektów bardziej sensowny byłby przekazanie typu (interfejsu) niż implementacja. Czy powinienem poczekać, aż aplikacja stanie się skomplikowana lub wykorzystam ją od razu? Zastanawiam się, że najlepsza praktyka może okazać się "ten facet jest ponad robienie rzeczy".

Odpowiedz

7

Powodem programowania na interfejsie jest izolowanie klas wyższych od zmian w klasach niższego poziomu. Jeśli nie spodziewasz się zmiany klasy niższego poziomu, to rozsądne jest, aby , a nie programować do interfejsu w tym przypadku. Ten article (PDF) rozwija pomysł, tworząc pojęcie Zasada Inwersja zależności.

+0

Dzięki. Wygląda podobnie do DI wiosny. – Shaw

3

Jeśli istnieje duża szansa, że ​​aplikacja stanie się bardziej złożona, łatwiej będzie skonfigurować rusztowanie wcześniej niż później. Jeśli jednak aplikacja nie jest skomplikowana i jest mało prawdopodobne, nie stanie się skomplikowana, może nie być ROI. Zawsze możesz później dokonać refaktoryzacji.

+0

Dokładnie. Mogę ustawić część rusztowania, aby później uniknąć niepotrzebnej pracy. Ale czy będzie to nazywane złą praktyką - z powodu nadmiernego używania lub niewłaściwego stosowania dobrej praktyki? – Shaw

+0

Myślę, że jest to wyrok w oparciu o to, co wykonujesz. Jednym z przykładów tego, co powiedziałbym, aby zawsze programować interfejsy, jest tworzenie warstwy DAO, ponieważ interfejsy będą użyczać różnych źródeł danych i tworzyć fałszywe dane dla testów jednostkowych. – rich

1

Musisz zobaczyć interfejs jako kontrakt. Niniejsza umowa określa zestaw zasad i operacji, które muszą być spełnione przez tych, którzy podpisali niniejszą umowę, bez względu na sposób jej wykonania.

4

Prostym przykładem, który otworzył oczy interfejsów jest to

class SimpleClass 
{ 
    public int A { get; set; } 
    public int B { get; set; } 
    public int C { get; set; } 
} 

List<SimpleClass> Calc(IEnumerable<SimpleClass> list) 
{ 
    foreach(SimpleClass item in list) 
    { 
    item.C = item.A * item.C: 
    } 
    return list.ToList(); 
} 

Wskazówka parametr wejściowy IEnumerable. Za pomocą tego mogę przejść w każdej kolekcji, która implementuje IEnumerable jako parametr in. List<SimpleClass>, SimpleClass[], Collection<SimpleClass>.

Interfejs IEnumerable gwarantuje, że mogę korzystać z pętli foreach i w ten sposób sprawiłem, że moja funkcja jest trochę bardziej ogólna, i będzie mniej szans, że będę musiał zmienić ten kod, ponieważ szansa, że ​​IEnumerable zmienia się jest mniejsza niż np. Wyświetl zmiany.

+0

Jestem świadomy zalet wbudowanych typów i zawsze staram się z nich korzystać. Ale nie jestem pewien co do typów niestandardowych - szczególnie moje pytanie dotyczyło tego. Dziękuję za odpowiedź i kod :) – Shaw

+0

Zasada, dlaczego używanie interfejsów jest tak samo ważna jak dla twórców bibliotek .net, więc jeśli zrozumiesz, dlaczego ich używasz, zrozumiesz, dlaczego i gdzie powinieneś ich używać. Oczywiście platforma skorzysta znacznie więcej z interfejsów. – terjetyl

3

Programowanie interfejsu w prostej aplikacji, w której nie planuje się implementacji zamiany, może wydawać się przesadą.

Po przejściu do Testowania Jednostki będziesz bardzo szczęśliwy, że programujesz w kierunku interfejsów, ponieważ daje to możliwość łatwiejszego wyłudzania rzeczywistego obiektu za pomocą testu podwójnego.

+1

Bardzo prawdziwe. Często słyszałam stwierdzenie: "Nigdy nie używaj interfejsu na czymś z tylko jedną implementacją". Cóż, kiedy testujesz jednostkę, tworzysz drugą implementację klas pozwanych. Przy dobrym zestawie testów jednostkowych ZAWSZE masz więcej niż jedną implementację. –

7

Jeśli kiedykolwiek zainteresuje Cię Test Driven Development, potrzeba "programowania do interfejsu" staje się naprawdę widoczna. Jeśli chcesz przetestować klasę w izolacji jej zależności, musisz przekazać interfejsy zamiast obiektów.

+0

Jest to główny powód, dla którego uniknąłem rozwoju opartego na testach. Na pierwszy rzut oka TDD to świetny pomysł, ale uważam, że koszt jest zbyt wysoki, ponieważ wymaga on zbytniej architektury większości klas aplikacji. – benjismith

+0

Tutaj się nie zgadzam. Moje pokrycie testowe jest tak wysokie, a moje zajęcia są tak dobrze rozłączone, że zmuszony jestem myśleć o dobrym projektowaniu z przodu w bardzo realistyczny sposób. Tworzy bardziej modułową, rozszerzalną architekturę, która ma wystarczającą liczbę testów, aby pozostać niezawodny. Rzadko już piszę błędy. –

1

zazwyczaj myślą w ten sposób - są tylko dwie rzeczy, które oddzielają interfejsu od implementacji:

  1. Obiekt może dziedziczyć z wielu interfejsów, ale tylko jedna klasa bazowa;
  2. Interfejsy nie zezwalają na domyślne implementacje, podczas gdy klasa bazowa ma.

Pomyśl teraz o przedmiotach, które odziedziczą po twojej "strukturze". Co będzie dla nich ważniejsze? Czy skorzystają najbardziej z domyślnych implementacji metod, czy będzie lepiej, jeśli mogą mieć inne klasy bazowe?

W większości przypadków jest całkiem jasne, który z nich jest najważniejszym czynnikiem. Jeśli zdarzy ci się być na cienkiej linii pomiędzy ... ciężko. Microsoft recommends klasy bazowe za pośrednictwem interfejsów.

1

Dla wszystkiego, co jest częścią API, dla klientów zewnętrznych lub innych zespołów, którzy ponownie używają Twojego kodu, używaj interfejsów tak często, jak tylko możesz. Warto, ponieważ ukrywa on jak najwięcej informacji o tym, jak działają twoje wdrożenia i daje większą swobodę w ich ulepszaniu w przyszłości. Wystaw tylko małą klasę betonu (być może statyczną), z której można uzyskać instancje.

W przypadku wewnętrznych części projektu istnieje możliwość rozpoczynania od konkretnych odniesień do klas wszędzie, a także wprowadzanie interfejsów tam, gdzie ma to sens dla projektu.

Ale inną ważną rzeczą do rozważenia jest testowanie jednostkowe. Możesz w pełni "kpić" z interfejsu w CLR bez żadnych problemów technicznych, ale kpiny z innych rzeczy są albo niemożliwe, albo wymagające poważnych oszustw. Tak więc jedną z dziedzin, które należy wziąć pod uwagę, jest rozwijanie oparte na testach: napisz testy kodu, idąc dalej, a odkryjesz, że potrzebujesz pewnych rzeczy do wyrażenia przez interfejsy, abyś mógł przedstawić ich fałszywe wersje.

1

Jeśli chcesz mieć możliwość przetestowania aplikacji i nie musisz używać (a następnie nie musisz budować) pełnego obiektu dla pracowników, możesz utworzyć interfejs IEmployee, a następnie utworzyć przyjaznego, przyjaznego dla użytkownika, testowego pracownika obiekty do przetestowania. Może to być dużym zyskiem, jeśli stworzenie pracownika jest trudne lub wręcz niemożliwe do zrobienia ręcznie lub bez bazy danych.

Innym ważnym powodem jest to, że pomaga określić, na czym dokładnie polegasz w klasie pracownika. Być może pomyślałeś, że używałeś tylko kilku publicznych metod, ale później dowiesz się, że używasz 10 metod i że są one ściślej powiązane, niż ci się wydawało.

Wreszcie, jeśli musisz zmienić kod, aby korzystać z SuperEmployee zamiast z Employee, jeśli używasz interfejsów cały czas, wszystko, co musisz zrobić, to mieć SuperEmployee wdrożyć IEmployee i zostaniesz ustawiony.

1

Zapoznaj się z książką Najpierw głównymi wzorami projektowymi ... zobaczysz dobre argumenty za używaniem interfejsów, które nie mają nic wspólnego z TDD lub polimorfizmem.

Interfejsy pozwalają na zmianę zachowania obiektu w czasie wykonywania ... Pomyśl o miejscach, w których potrzebny jest przewodnik dla określonego zachowania, ale może nie wiedzieć, jakie zachowanie jest potrzebne do czasu wykonania. W przypadku wydatków na komputery dla pracowników ... Kierownicy mogą mieć wyższe koszty na wydatki związane z "rozrywką" niż zwykli pracownicy.

Jeśli Twój pracownik ma odniesienie do interfejsu IExpenseCalculator, możesz mu przydzielić kalkulator menedżera lub kalkulator pracownika w czasie wykonywania. Wywołanie Employee.GetExpenses() da ci inny wynik dla menedżera niż dla zwykłego pracownika. Wewnętrznie, kod wyglądałby następująco:

public class Employee { 
    private IExpenseCalculator expenses; 

    public ExpenseSheet GetExpenses() { 
    return expenses.CalcExpenses(); 
    } 
} 

tym przykładzie założono, że „wydatki” jest wystawiony jako właściwość, a IExpenseCalculator ma metodę CalcExpenses().

Interfejsy są również ściśle związane z pojęciem fabryk obiektów ... pomyśl o warstwie bazy danych. Po skonfigurowaniu warstwy danych jako fabryki można tworzyć obiekty, aby dynamicznie łączyć się z serwerami SQL, Oracle lub MySql w czasie wykonywania na podstawie ustawień konfiguracyjnych. Ale klient potrzebuje konkretnego uchwytu do obiektu warstwy bazy danych ... wpisz Interfejsy.

Interfejsy to potężne narzędzie, często używane niezgodnie z przeznaczeniem. Prawidłowe ich użycie zmieni sposób myślenia, ale może bardzo pomóc w Twojej strukturze aplikacji.

Powiązane problemy