Hintergrund
Ich habe ein Projekt, das von der Verwendung eines bestimmten Hardwaregerätetyps abhängt, während es nicht wirklich wichtig ist, wer dieses Hardwaregerät herstellt, solange es das tut, wofür ich es brauche. Abgesehen davon weisen sogar zwei Geräte, die dasselbe tun sollen, Unterschiede auf, wenn sie nicht vom selben Hersteller hergestellt werden. Daher denke ich daran, eine Schnittstelle zu verwenden, um die Anwendung von der jeweiligen Marke / dem jeweiligen Gerätemodell zu entkoppeln , und stattdessen die Schnittstelle nur die Funktionalität auf höchster Ebene abzudecken. Ich denke, meine Architektur wird folgendermaßen aussehen:
- Definieren Sie eine Schnittstelle in einem C # -Projekt
IDevice
. - Lassen Sie einen Beton in einer Bibliothek in einem anderen C # -Projekt definieren, der zur Darstellung des Geräts verwendet wird.
- Lassen Sie das konkrete Gerät die
IDevice
Schnittstelle implementieren . - Die
IDevice
Schnittstelle kann Methoden wieGetMeasurement
oder habenSetRange
. - Stellen Sie sicher, dass die Anwendung Kenntnisse über den Beton hat, und übergeben Sie den Beton an den Anwendungscode, der das Gerät verwendet ( nicht implementiert )
IDevice
.
Ich bin mir ziemlich sicher, dass dies der richtige Weg ist, denn dann kann ich ändern, welches Gerät verwendet wird, ohne die Anwendung zu beeinträchtigen (was manchmal zu passieren scheint). Mit anderen Worten, es spielt keine Rolle, wie die Implementierungen des Betons funktionieren GetMeasurement
oder SetRange
tatsächlich funktionieren (wie es bei den Herstellern des Geräts unterschiedlich sein kann).
Der einzige Zweifel in meinem Kopf ist, dass jetzt sowohl die Anwendung als auch die konkrete Klasse des Geräts von der Bibliothek abhängen, die die IDevice
Schnittstelle enthält . Aber ist das eine schlechte Sache?
Ich sehe auch nicht, wie die Anwendung nichts über das Gerät wissen muss, es sei denn, das Gerät IDevice
befindet sich im selben Namespace.
Frage
Scheint dies der richtige Ansatz für die Implementierung einer Schnittstelle zu sein, um die Abhängigkeit zwischen meiner Anwendung und dem verwendeten Gerät zu entkoppeln?
Antworten:
Ich denke, Sie haben ein ziemlich gutes Verständnis dafür, wie entkoppelte Software funktioniert :)
Es muss nicht sein!
Mit all diesen Problemen können Sie sich mit der Projektstruktur befassen .
So mache ich es normalerweise:
Common
Projekt ein. So etwas wieMyBiz.Project.Common
. Andere Projekte können darauf verweisen, aber möglicherweise nicht auf andere Projekte.MyBiz.Project.Devices.TemperatureSensors
. Dieses Projekt verweist auf dasCommon
Projekt.Client
Projekt, das der Einstiegspunkt für meine Anwendung ist (so etwas wieMyBiz.Project.Desktop
). Beim Start durchläuft die Anwendung einen Bootstrapping- Prozess, bei dem ich Abstraktions- / konkrete Implementierungszuordnungen konfiguriere. Ich kann meinen BetonIDevices
wieWaterTemperatureSensor
undIRCameraTemperatureSensor
hier instanziieren oder Dienste wie Fabriken oder einen IoC-Container konfigurieren, um später die richtigen Betontypen für mich zu instanziieren.Das Wichtigste dabei ist, dass nur Ihr
Client
Projekt sowohl das abstrakteCommon
Projekt als auch alle konkreten Implementierungsprojekte kennen muss. Indem Sie die abstrakte-> konkrete Zuordnung auf Ihren Bootstrap-Code beschränken, können Sie dafür sorgen, dass der Rest Ihrer Anwendung die konkreten Typen glücklicherweise nicht kennt.Locker gekoppelter Code ftw :)
quelle
new Foo(new DeviceA());
), anstatt ein privates Feld in Foo zu haben, um DeviceA selbst zu instanziieren (private IDevice idevice = new DeviceA();
) - Sie erreichen dennoch DI, as Foo ist DeviceA im ersten Fall nicht bekanntJa, das scheint der richtige Ansatz zu sein. Nein, es ist keine schlechte Sache für die Anwendung und die Gerätebibliothek, von der Schnittstelle abhängig zu sein, insbesondere wenn Sie die Kontrolle über die Implementierung haben.
Wenn Sie befürchten, dass die Geräte die Schnittstelle aus irgendeinem Grund nicht immer implementieren, können Sie das Adaptermuster verwenden und die konkrete Implementierung der Geräte an die Schnittstelle anpassen.
BEARBEITEN
Stellen Sie sich bei der Beantwortung Ihres fünften Problems eine Struktur wie diese vor (ich gehe davon aus, dass Sie die Kontrolle über die Definition Ihrer Geräte haben):
Sie haben eine Kernbibliothek. Darin befindet sich eine Schnittstelle namens IDevice.
In Ihrer Gerätebibliothek haben Sie einen Verweis auf Ihre Kernbibliothek und Sie haben eine Reihe von Geräten definiert, die alle IDevice implementieren. Sie haben auch eine Fabrik, die weiß, wie man verschiedene Arten von IDevices erstellt.
In Ihrer Anwendung enthalten Sie Verweise auf die Kernbibliothek und die Gerätebibliothek. Ihre Anwendung verwendet jetzt die Factory, um Instanzen von Objekten zu erstellen, die der IDevice-Schnittstelle entsprechen.
Dies ist eine von vielen Möglichkeiten, um Ihre Bedenken auszuräumen.
BEISPIELE:
quelle
Sie müssen umgekehrt denken. Wenn Sie diesen Weg nicht gehen, muss die Anwendung alle verschiedenen Geräte / Implementierungen kennen. Was Sie beachten sollten, ist, dass das Ändern dieser Schnittstelle Ihre Anwendung beschädigen kann, wenn sie bereits in freier Wildbahn ist. Daher sollten Sie diese Schnittstelle sorgfältig entwerfen.
Einfach ja
Die Anwendung kann die Betonbaugruppe (DLL) zur Laufzeit laden. Siehe: /programming/465488/can-i-load-a-net-assembly-at-runtime-and-instantiate-a-type-knowing-only-the-na
Wenn Sie einen solchen "Treiber" dynamisch entladen / laden müssen, müssen Sie die Assembly in eine separate AppDomain laden .
quelle
IDevice
Benutzeroberfläche, damit sein Gerät mit Ihrer App verwendet werden kann. Dann gibt es keinen anderen Weg, IMO.