tło
Mam projekt, który zależy od użycia określonego rodzaju urządzenia sprzętowego, podczas gdy tak naprawdę nie ma znaczenia, kto je tworzy, o ile robi to, czego potrzebuję. Biorąc to pod uwagę, nawet dwa urządzenia, które powinny robić to samo, będą się różnić, jeśli nie zostaną wyprodukowane przez tego samego producenta. Zastanawiam się więc, czy użyć interfejsu do oddzielenia aplikacji od konkretnej marki / modelu urządzenia, a zamiast tego interfejs powinien obejmować tylko funkcjonalność najwyższego poziomu. Oto, jak myślę, jak będzie wyglądać moja architektura:
- Zdefiniuj interfejs w jednym projekcie C #
IDevice
. - Posiadaj konkretny element w bibliotece zdefiniowanej w innym projekcie C #, który będzie używany do reprezentowania urządzenia.
- Niech konkretne urządzenie wdroży
IDevice
interfejs. IDevice
Interfejs może mieć metod, takich jakGetMeasurement
czySetRange
.- Spraw, aby aplikacja miała wiedzę na temat betonu, i przekaż ten beton do kodu aplikacji, który wykorzystuje (a nie implementuje )
IDevice
urządzenie.
Jestem całkiem pewien, że jest to właściwy sposób, aby to zrobić, ponieważ wtedy będę mógł zmienić, które urządzenie jest używane, bez wpływu na aplikację (co zdarza się czasami). Innymi słowy, nie będzie miało znaczenia, w jaki sposób implementacje GetMeasurement
lub SetRange
faktycznie działają przez beton (co może różnić się między producentami urządzenia).
Jedyne wątpliwości, jakie mam, to to, że teraz zarówno aplikacja, jak i konkretna klasa urządzenia zależą od biblioteki zawierającej IDevice
interfejs. Ale czy to zła rzecz?
Nie widzę też, w jaki sposób aplikacja nie musi wiedzieć o urządzeniu, chyba że urządzenie i IDevice
są w tej samej przestrzeni nazw.
Pytanie
Czy wydaje się to właściwym podejściem do implementacji interfejsu w celu oddzielenia zależności między moją aplikacją a urządzeniem, którego używa?
Odpowiedzi:
Myślę, że dobrze rozumiesz, jak działa oddzielone oprogramowanie :)
Nie musi tak być!
Możesz rozwiązać wszystkie te problemy za pomocą Struktury Projektu .
Sposób, w jaki zazwyczaj to robię:
Common
projekcie. Coś jakMyBiz.Project.Common
. Inne projekty mogą się do niego odwoływać, ale nie mogą odnosić się do innych projektów.MyBiz.Project.Devices.TemperatureSensors
. Ten projekt będzie odnosił się doCommon
projektu.Client
projekt, który jest punktem wejścia do mojej aplikacji (coś w rodzajuMyBiz.Project.Desktop
). Podczas uruchamiania aplikacja przechodzi proces ładowania początkowego, w którym konfiguruję odwzorowania abstrakcji / konkretnych implementacji. Mogę utworzyć instancję moich konkretnych elementów,IDevices
takich jakWaterTemperatureSensor
iIRCameraTemperatureSensor
tutaj, lub skonfigurować usługi, takie jak fabryki lub kontener IoC, aby później utworzyć dla mnie odpowiednie typy konkretnych elementów.Kluczową kwestią jest to, że tylko Twój
Client
projekt musi być świadomy zarównoCommon
projektu abstrakcyjnego , jak i wszystkich konkretnych projektów wdrożeniowych. Ograniczając abstrakcyjne> konkretne mapowanie do kodu Bootstrap, pozwalasz pozostałej części aplikacji na błogą nieświadomość konkretnych typów.Luźno powiązany kod ftw :)
źródło
new Foo(new DeviceA());
), zamiast posiadania prywatnego pola w samej instancji Foo urządzenia DeviceA (private IDevice idevice = new DeviceA();
) - nadal osiągasz DI, ponieważ Foo nie wie o DeviceA w pierwszym przypadkuTak, to wydaje się właściwe podejście. Nie, nie jest źle, że aplikacja i biblioteka urządzeń zależą od interfejsu, szczególnie jeśli kontrolujesz implementację.
Jeśli obawiasz się, że z jakiegoś powodu urządzenia nie zawsze mogą implementować interfejs, możesz użyć wzorca adaptera i dostosować konkretną implementację urządzeń do interfejsu.
EDYTOWAĆ
Odpowiadając na piąty problem, pomyśl o takiej strukturze (zakładam, że masz kontrolę nad definiowaniem swoich urządzeń):
Masz podstawową bibliotekę. Jest w nim interfejs o nazwie IDevice.
W bibliotece urządzeń masz odniesienie do biblioteki podstawowej i zdefiniowałeś serię urządzeń, które wszystkie implementują IDevice. Masz również fabrykę, która wie, jak tworzyć różnego rodzaju urządzenia ID.
W swojej aplikacji dołączasz odniesienia do biblioteki podstawowej i biblioteki urządzeń. Twoja aplikacja korzysta teraz z fabryki do tworzenia instancji obiektów zgodnych z interfejsem IDevice.
Jest to jeden z wielu możliwych sposobów rozwiązania problemów.
PRZYKŁADY:
źródło
Musisz pomyśleć na odwrót. Jeśli nie pójdziesz tą drogą, aplikacja będzie musiała wiedzieć o wszystkich różnych urządzeniach / implementacjach. Należy pamiętać, że zmiana tego interfejsu może spowodować uszkodzenie aplikacji, jeśli jest ona już dostępna w środowisku naturalnym, dlatego należy starannie zaprojektować ten interfejs.
Po prostu tak
Aplikacja może załadować konkretny zestaw (dll) w czasie wykonywania. Zobacz: /programming/465488/can-i-load-a-net-assembly-at-runtime-and-instantiate-a-type-knowing-only-the-na
Jeśli chcesz dynamicznie rozładować / załadować taki „sterownik”, musisz załadować zespół do osobnej AppDomain .
źródło
IDevice
interfejsu, aby jego urządzenie mogło być używane z Twoją aplikacją, wtedy nie byłoby innego sposobu IMO.