Czy powinienem mieć osobny zespół dla interfejsów?

Obecnie mamy sporo klas w projekcie, a każda z tych klas implementuje interfejs, głównie z powodów DI.

Teraz, moje osobiste odczucie jest takie, że te interfejsy powinny być umieszczone w oddzielnej przestrzeni nazw w ramach tego samego zestawu(więc mamy MyCompany.CoolApp.DataAccess assembly, A wewnątrz niej jest Interfaces przestrzeń nazw dająca MyCompany.CoolApp.DataAccess.Interfaces).

Jednak ktoś zasugerował, że te interfejsy powinny być w ich własnym zestawie. A moje pytanie brzmi-czy mają rację? Widzę, że są pewne korzyści (np. inne projekty będą musiały zużywać tylko zespół interfejsu), ale na koniec dnia wszystkie te zespoły będą musiały zostać załadowane. Wydaje mi się również, że może wystąpić nieco bardziej złożony problem z wdrożeniem, ponieważ Visual Studio nie będzie automatycznie pobierać zestawu implementacyjnego do folderu bin celu.

Czy istnieją wytyczne dotyczące najlepszych praktyk w tym zakresie?

EDIT:

Aby podkreślić mój punkt widzenia trochę jaśniejszy: już oddzieliliśmy interfejs użytkownika, DataAccess, DataModel i inne rzeczy w różne zespoły. Obecnie możemy również bez problemu zamienić naszą implementację na inną, ponieważ mapujemy klasę implementacji do interfejsu za pomocą Unity (IOC framework). Powinienem zauważyć, że nigdy nie piszemy dwóch implementacji tego samego interfejsu, z wyjątkiem powodów polimorfizmu i tworzenia moków do testów jednostkowych. Więc obecnie nie" zamieniamy " implementacji z wyjątkiem jednostki testy.

Jedynym minusem, jaki widzę mając interfejs w tym samym zestawie co implementacja jest to, że cały zestaw (łącznie z nieużywaną implementacją) zostanie załadowany.

Widzę jednak, że posiadanie ich w innym assembly oznacza, że deweloperzy nie będą przypadkowo "nową" klasą implementującą, zamiast tworzyć ją za pomocą IOC wrapper.

Jedna kwestia, której nie zrozumiałam z odpowiedzi, to problem z rozmieszczeniem. If I am tylko w zależności od zestawów interfejsów, będę miał coś w rodzaju następującej struktury:
MyCompany.MyApplication.WebUI
    References:
        MyCompany.MyApplication.Controllers.Interfaces
        MyCompany.MyApplication.Bindings.Interfaces
        etc...

Kiedy to buduję, zespoły, które są automatycznie umieszczane w folderze bin, to tylko te zespoły interfejsu. Jednak moje mapowania typu w unity mapują różne interfejsy do ich rzeczywistych implementacji. W jaki sposób zespoły zawierające moje implementacje trafiają do folderu bin?

Author: Gilles 'SO- stop being evil', 2010-07-29

7 answers

zwykłe oczekiwane ? praktyką jest umieszczanie ich we własnym asemblerze, ponieważ wtedy dany projekt zużywający te interfejsy nie wymaga twardego odniesienia do implementacji tych interfejsów. W teorii oznacza to, że można zamienić implementację z niewielkim lub żadnym bólem.

To powiedziawszy, nie pamiętam, kiedy ostatnio to robiłem, do punktu @ David_001 nie jest to koniecznie "zwykłe". Zazwyczaj Nasze interfejsy są zgodne z implementacją, naszym najbardziej wspólne użycie dla testowanych interfejsów.

Myślę, że są różne postawy do przyjęcia w zależności od tego, co produkujesz. Mam tendencję do tworzenia aplikacji LOB, które muszą współpracować wewnętrznie z innymi aplikacjami i zespołami, więc są niektórzy interesariusze do publicznego API danej aplikacji. Nie jest to jednak tak ekstremalne, jak tworzenie biblioteki lub frameworka dla wielu nieznanych klientów, gdzie publiczne API nagle staje się ważniejsze.

W scenariuszu rozmieszczenia, jeśli zmienisz implementację, możesz teoretycznie wdrożyć tę pojedynczą bibliotekę DLL - pozostawiając w ten sposób, powiedzmy, biblioteki DLL interfejsu i interfejsu. Jeśli skompilowałeś swoje interfejsy i implementację razem, być może będziesz musiał ponownie wdrożyć bibliotekę DLL interfejsu użytkownika...

kolejną korzyścią jest czysta segregacja kodu - posiadanie biblioteki DLL interfaces (lub współdzielonej biblioteki) jawnie określa dowolnemu z zespołu programistów, gdzie umieścić nowe typy itp. nie liczę już tego jako korzyści, ponieważ nie w przypadku jakichkolwiek problemów, które nie są realizowane w ten sposób, zamówienie publiczne jest nadal łatwo dostępne, niezależnie od miejsca, w którym znajdują się interfejsy.

Nie wiem, czy istnieją najlepsze praktyki za czy przeciw, ważne jest prawdopodobnie to, że w kodzie zawsze zużywasz interfejsy i nigdy nie pozwalasz, aby jakikolwiek kod wyciekł do użycia implementacji.

 29
Author: Adam Houldsworth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-11 09:44:49

Dotychczasowe odpowiedzi zdają się mówić, że umieszczanie interfejsów we własnym zestawie jest "zwykłą" praktyką. Nie zgadzam się z umieszczaniem niepowiązanych interfejsów w jednym "współdzielonym" wspólnym zestawie, więc oznaczałoby to, że będę musiał mieć 1 zestaw interfejsu dla każdego" implementacyjnego " zestawu.

Jednak, myśląc o tym dalej, nie mogę myśleć o wielu rzeczywistych przykładach tej praktyki (np. czy log4net lub NUnit zapewniają publiczne zestawy interfejsów, aby konsumenci czy można wtedy decydować o różnych implementacjach? Jeśli tak, to jakiej innej implementacji nunit mogę użyć?). Spędzając wieki patrząc przez google, znalazłem wiele zasobów.

 18
Author: David_001,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-22 23:55:49

Wzorcem, który podążam za tym, co nazywam typami współdzielonymi (I ja też używam DI) jest posiadanie oddzielnego zestawu, który zawiera następujące pojęcia na poziomie aplikacji (zamiast wspólnych pojęć, które przechodzą do wspólnych zestawów):

  1. współdzielone interfejsy.
  2. DTOs.
  3. wyjątki.

W ten sposób można zarządzać zależnościami między klientami a bazowymi bibliotekami aplikacji, ponieważ klienci nie mogą przyjmować zależności od konkretnej implementacji ani bezpośrednio, ani jako niezamierzona konsekwencja dodania bezpośredniego odniesienia do zestawu, a następnie uzyskania dostępu do dowolnego starego typu publicznego.

Następnie mam projekt typu runtime, w którym konfiguruję kontener DI przy starcie aplikacji lub na początku zestawu testów jednostkowych. W ten sposób istnieje wyraźny rozdział między implementacjami i jak mogę je zmieniać za pomocą DI. Moje Moduły klienckie nigdy nie mają bezpośredniego odniesienia do rzeczywistych bibliotek podstawowych, tylko Biblioteka "SharedTypes".

Kluczem do mojego projektu jest posiadanie wspólna koncepcja runtime dla klientów (czy to aplikacja WPF czy NUnit), która ustawia wymagane zależności, np. konkretne implementacje lub jakiś rodzaj mocks\stubs.

Jeśli powyższe typy współdzielone nie są brane pod uwagę, ale zamiast tego klienci dodają odniesienie do złożenia z konkretną implementacją, to klienci mogą bardzo łatwo używać konkretnych implementacji zamiast interfejsów, zarówno w oczywisty, jak i nieoczywisty sposób. Bardzo łatwo jest stopniowo skończyć z nadmierne łączenie się w czasie, które jest prawie niemożliwe do rozwiązania bez wielkiego wysiłku i co ważniejsze czasu.

Update

Aby wyjaśnić za pomocą przykładu, w jaki sposób zależności kończą się w docelowej aplikacji.

W mojej sytuacji mam aplikację kliencką WPF. Używam Prism i Unity (dla DI), gdzie co ważne, pryzmat jest używany do kompozycji aplikacji.

Z Prism Twój zespół aplikacji jest tylko powłoką, rzeczywiste implementacje funkcjonalność znajduje się w zespołach "modułowych" (można mieć osobny zespół dla każdego modułu koncepcyjnego, ale nie jest to wymóg, mam jeden zespół modułów ATM). Zadaniem powłoki jest załadowanie modułów-skład tych modułów jest aplikacją. Moduły używają zestawu SharedTypes, ale powłoka odwołuje się do betonowych zestawów. Omówiony przeze mnie projekt typu runtime odpowiada za inicjalizację zależności, A odbywa się to w Shell.

W ten sposób zespoły modułów, które mają wszystkie funkcjonalności, nie zależą od konkretnych implementacji. Są ładowane przez powłokę, która sortuje zależności. Powłoka odwołuje się do betonowych zespołów i w ten sposób dostają się do katalogu bin.

Szkic Zależności:

Shell.dll <-- Application
  --ModuleA.dll
  --ModuleB.dll
  --SharedTypes.dll
  --Core.dll
  --Common.dll + Unity.dll <-- RuntimeDI

ModuleA.dll
  --SharedTypes.dll
  --Common.dll + Unity.dll <-- RuntimeDI

ModuleB.dll
  --SharedTypes.dll
  --Common.dll + Unity.dll <-- RuntimeDI

SharedTypes.dll
  --...
 10
Author: Tim Lloyd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-29 16:04:01

Zgadzam się z zaznaczoną odpowiedzią. Brawo, David. Właściwie, ulżyło mi, widząc odpowiedź, myślałem, że oszalałem.

Widzę ten ciekawy wzór "długopisów w garnku długopisów" w enterprise C# freelance jobs cały czas, gdzie ludzie podążają za konwencją tłumu i zespół musi się dostosować, a niestosowanie się sprawia kłopoty.

Druga wariacja to jedna przestrzeń nazw na zbiór nonsensów. Więc otrzymujesz SomeBank.SomeApp.Interfaces przestrzeń nazw i Wszystko jest w niej.

Dla ja, oznacza to, że typy są rozrzucone po przestrzeniach nazw, a zespoły zawierające całą masę rzeczy, które mnie nie obchodzą, muszą być odwołane wszędzie.

Co do interfejsów, to nawet nie używam interfejsów w moich prywatnych aplikacjach; di działa na typach, konkretnie z wirtualami, klasami bazowymi lub interfejsami. Wybieram odpowiednio i umieszczam typy w bibliotekach DLL zgodnie z tym, co robią.

Nigdy nie miałem problemu z DI lub zamianą logiki później.

•. NET assemblies są jednostką bezpieczeństwo, zakres API i wdrażanie oraz są niezależne od przestrzeni nazw.

• Jeśli dwa zespoły zależą od siebie, to nie mogą być rozmieszczone i wersjonowane oddzielnie i powinny być połączone.

• posiadanie wielu bibliotek DLL często oznacza upublicznienie wielu rzeczy tak, że trudno jest odróżnić rzeczywiste publiczne API od członków typu, które musiały zostać upublicznione, ponieważ zostały arbitralnie umieszczone we własnym zbiorze.

* czy kod poza moim DLL musi kiedykolwiek używać mojego Typ?

• Start konserwatywny; Zwykle mogę łatwo przenieść typ na warstwę, jest trochę trudniej w drugą stronę.

• Czy Mogę starannie spakować mój obszar funkcji lub framework do pakietu NuGet, tak aby był całkowicie opcjonalny i możliwy do wersjonowania, jak każdy inny pakiet?

* Czy moje typy są zgodne z dostarczeniem funkcji i czy mogą być umieszczone w przestrzeni nazw funkcji?

* wiele prawdziwych bibliotek i frameworków jest oznakowanych, co ułatwia ich omówienie i nie wypalają nazw przestrzeni nazw, które sugerują jej użycie lub są niejednoznaczne, czy mógłbym markować komponenty mojej aplikacji za pomocą "nazw kodowych", takich jak Steelcore, zamiast ogólnych banałów i mylących terminów, errm "usługi"?

Edit

Jest to jedna z niezrozumiałych rzeczy, które widzę w dzisiejszym rozwoju. Jest tak źle.

Masz API, więc umieść wszystkie jego typy w jednym projekcie API. Przenieś je tylko wtedy, gdy musisz je udostępnić / ponownie wykorzystać. Kiedy je wyprowadzasz, przenieś je prosto do pakietu NuGet z jasną nazwą, która nosi intencję i cel pakietu. Jeśli walczysz o nazwę, a biorąc pod uwagę "wspólny", prawdopodobnie dlatego, że tworzysz wysypisko.

Powinieneś uwzględnić pakiet NuGet w rodzinie powiązanych pakietów. Twój Pakiet "core" powinien mieć minimalne zależności od innych pakietów. Typy wewnątrz są powiązane według użycia i zależą od siebie.

Następnie tworzysz nowy pakiet dla więcej wyspecjalizowane typy i podtypy, które wymagają dodatkowych zestawów zależności; jaśniej: bibliotekę dzieli się według zewnętrznych zależności, a nie według rodzaju typu lub interfejsu lub wyjątku.

Faktoring biblioteczny

Więc możesz trzymać wszystkie typy w jednej dużej bibliotece, ale niektóre bardziej wyspecjalizowane typy (kolorowe plamy) zależą od pewnych zewnętrznych bibliotek, więc teraz Twoja biblioteka musi wyciągnąć wszystkie te zależności. To jest niepotrzebne, powinieneś zamiast tego złamać z tych typów do dalszych specjalistycznych bibliotek, które przyjmują potrzebne zależności.

Typy w pakiecie A i B mogą należeć do tej samej przestrzeni nazw . Odwoływanie się do A przynosi jeden zestaw typów, a następnie opcjonalnie odwoływanie się do B uzupełnia przestrzeń nazw o kilka więcej.

To wszystko.

Łukasz

 8
Author: Luke Puplett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-02 13:32:12

Patrzę na System.Data.dll (4.0) w przeglądarce obiektów i widzę, że jest autonomiczny sam w sobie z nie tylko interfejsami, ale wszystkimi klasami instrumentalnymi, takimi jak DataSet, DataTable, DataRow, DataColumn itp. Co więcej, przeglądanie listy przestrzeni nazw, które posiada Jak System.Dane, System.Data.Common, System.Konfiguracja I System.Xml, sugeruje po pierwsze, aby interfejsy zawarte we własnych złożeniach ze wszystkimi odpowiednimi i wymaganymi kodami trzymanymi razem, a po drugie i więcej co ważne, aby ponownie używać tych samych przestrzeni nazw w całej aplikacji (lub frameworku) do Wirtualnego segregowania klas.

 1
Author: Talha Yousuf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-09 17:18:45

Wiem, że ten wątek jest naprawdę, bardzo stary, ale mam pomysł na ten temat, który chcę tam umieścić.

Dostaję montaż do "ponownego użycia". Ale czy nie możemy pójść o krok dalej dla naszego konkretnego rozwiązania?

Jeśli mamy w naszym rozwiązaniu zespół, który ma odpowiednie interfejsy, możemy zbudować ten zespół i używać go wszędzie tam, gdzie ma to sens, w tym ponowne użycie.

Ale dla innych projektów w ramach naszego rozwiązania, dlaczego nie po prostu dodać plik interfejsu przez LINK do inne projekty, które wymagają zdefiniowania interfejsu?

Dzięki temu wdrożenie dla danego projektu zostanie uproszczone(nie trzeba wdrażać zestawu interfejsu). Z drugiej strony, jeśli chcesz ponownie użyć interfejsu w innym rozwiązaniu, masz do wyboru skopiowanie pliku interfejsu lub po prostu odwołanie się do zespołu interfejsu.

Wydaje się najlepszy z obu światów. Mamy wybór, jak uzyskać interfejs, i to jest nadal wersja kontrolowane.

Frank

 0
Author: Frank B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-06 16:56:09

Ostatnio zawsze opowiadałem się za oddzieleniem interfejsów od implementacji.

Nawet jeśli ktoś z zespołu powie" 99% implementacji tych interfejsów nigdy się nie zmieni", nigdy nie mów nigdy.

Dzielenie bibliotek zapisało naszą refaktoryzację podczas przenoszenia dużego projektu z EntityFramework do EntityFrameworkCore. Po prostu zmieniliśmy realizację w 10 projektach z EntityFramework i poszliśmy napić się kawy.

 0
Author: serge375,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-02-03 20:24:04