Um Ihren Code hier lose zu koppeln, sind einige einfache Dinge zu beachten:
Teil 1:
Technisch bekannt als "Trennung von Bedenken". Jede Klasse hat eine bestimmte Rolle. Sie sollte sich mit Geschäftslogik oder Anwendungslogik befassen. Versuchen Sie, sich von Klassen fernzuhalten, die beide Verantwortlichkeiten verbinden. Das heißt, eine Klasse, die (allgemein) Daten verwaltet, ist Anwendungslogik, während eine Klasse, die Daten verwendet, Geschäftslogik ist.
Persönlich bezeichne ich dies (in meiner eigenen kleinen Welt) als create it or use it
. Eine Klasse sollte ein Objekt erstellen oder ein Objekt verwenden, das niemals beides tun sollte.
Teil 2:
So implementieren Sie die Trennung von Bedenken.
Als Ausgangspunkt gibt es zwei einfache Techniken:
Hinweis: Entwurfsmuster sind nicht absolut.
Sie sollen an die jeweilige Situation angepasst werden, haben jedoch ein zugrunde liegendes Thema, das allen Anwendungen ähnlich ist. Schauen Sie sich also die folgenden Beispiele nicht an und sagen Sie, dass ich dies genau befolgen muss. Dies sind nur Beispiele (und dabei etwas erfunden).
Abhängigkeitsinjektion :
Hier übergeben Sie ein Objekt, das eine Klasse verwendet. Das Objekt, das Sie basierend auf einer Schnittstelle übergeben, damit Ihre Klasse weiß, was damit zu tun ist, muss jedoch nicht die tatsächliche Implementierung kennen.
class Tokenizer
{
public:
Tokenizer(std::istream& s)
: stream(s)
{}
std::string nextToken() { std::string token; stream >> token;return token;}
private:
std::istream& stream;
};
Hier injizieren wir den Stream in einen Tokenizer. Der Tokenizer weiß nicht, um welchen Typ es sich bei dem Stream handelt, solange er die Schnittstelle von std :: istream implementiert.
Service Locator-Muster :
Das Service Locator-Muster ist eine geringfügige Variation der Abhängigkeitsinjektion. Anstatt ein Objekt anzugeben, das es verwenden kann, übergeben Sie ihm ein Objekt, das weiß, wie das zu verwendende Objekt gefunden (erstellt) wird.
class Application
{
public:
Application(Persister& p)
: persistor(p)
{}
void save()
{
std::auto_ptr<SaveDialog> saveDialog = persistor.getSaveDialog();
saveDialog.DoSaveAction();
}
void load()
{
std::auto_ptr<LoadDialog> loadDialog = persistor.getLoadDialog();
loadDialog.DoLoadAction();
}
private:
Persister& persistor;
};
Hier übergeben wir dem Anwendungsobjekt ein Persistorobjekt. Wenn Sie eine Aktion zum Speichern / Laden ausführen, wird mithilfe des Persistors ein Objekt erstellt, das tatsächlich weiß, wie die Aktion ausgeführt wird. Hinweis: Auch hier ist der Persistor eine Schnittstelle, und Sie können je nach Situation unterschiedliche Implementierungen bereitstellen.
Dies ist nützlich, wenn potentially
jedes Mal, wenn Sie eine Aktion instanziieren, ein eindeutiges Objekt erforderlich ist.
Persönlich finde ich dies besonders nützlich beim Schreiben von Unit-Tests.
Hinweis zu Mustern:
Designmuster sind ein großes Thema für sich. Dies ist keineswegs eine exklusive Liste von Mustern, die Sie zur Unterstützung der losen Kopplung verwenden können. Dies ist nur ein gemeinsamer Ausgangspunkt.
Mit der Erfahrung werden Sie feststellen, dass Sie diese Muster bereits verwenden, nur dass Sie ihre formalen Namen nicht verwendet haben. Indem wir ihre Namen standardisieren (und alle dazu bringen, sie zu lernen), stellen wir fest, dass es einfach und schneller ist, Ideen zu kommunizieren.
managing the data
spreche, beziehe ich mich auf die Variablen (nicht auf die tatsächlichen Daten). Dinge wie Zeiger müssen also so verwaltet werden, dass sie nicht auslaufen. Es können jedoch Daten eingefügt oder die Art und Weise, wie die Daten abgerufen werden, abstrahiert werden (sodass Ihre Klasse mit verschiedenen Datenabrufmethoden wiederverwendet werden kann). Es tut mir leid, ich kann nicht genauer sein.minutae of loose coupling
(liebe dieses Wort minutae)). Das Geheimnis der Programmierung besteht darin, zu lernen, wann die Techniken anzuwenden sind. Überbeanspruchung kann zu einem Codegewirr führen.Ich bin ein ASP.NET-Entwickler, weiß also nicht viel über die WinForms-Kopplung, weiß aber ein wenig über N-Tier-Webanwendungen Bescheid, vorausgesetzt, es handelt sich um eine dreistufige Anwendungsarchitektur mit Benutzeroberfläche, Domäne und Datenzugriffsschicht (DAL).
Bei der losen Kopplung geht es um Abstraktionen.
Wie @MKO angibt, besteht eine lose Kopplung, wenn Sie eine Assembly durch eine andere ersetzen können (z. B. ein neues UI-Projekt, das Ihr Domain-Projekt verwendet, eine neue DAL, die in einer Tabelle anstatt in einer Datenbank gespeichert wird). Wenn Ihre Domain und DAL von Projekten weiter unten in der Kette abhängen, ist die Kopplung möglicherweise lockerer.
Ein Aspekt von etwas, das lose gekoppelt ist, ist, ob Sie ein Objekt durch ein anderes ersetzen können, das dieselbe Schnittstelle implementiert. Es hängt nicht vom tatsächlichen Objekt ab, sondern von der abstrakten Beschreibung dessen, was es tut (seiner Schnittstelle).
Lose Kopplung, Schnittstellen und Abhängigkeitsinjektoren (DI) und Inversion of Control (IoC) sind nützlich für den Isolationsaspekt des Entwurfs für Testbarkeit.
Beispiel: Ein Objekt im UI-Projekt ruft ein Repository-Objekt im Domänenprojekt auf.
Sie können ein gefälschtes Objekt erstellen , das dieselbe Schnittstelle wie das Repository implementiert, das der zu testende Code verwendet, und dann ein spezielles Verhalten für Tests schreiben ( Stubs, um zu verhindern, dass Produktionscode, der speichert / löscht / aufgerufen wird, aufgerufen wird, und Mocks , die als Stubs fungieren und den Überblick behalten des Zustands des gefälschten Objekts zu Testzwecken).
Das heißt, der einzige aufgerufene Produktionscode befindet sich jetzt nur noch in Ihrem UI-Objekt. Ihr Test wird nur gegen diese Methode durchgeführt, und alle Testfehler isolieren den Fehler auf diese Methode.
Im Menü "Analysieren" in VS (abhängig von Ihrer Version) finden Sie außerdem Tools zum Berechnen von Codemetriken für Ihr Projekt, darunter die Klassenkopplung. Weitere Informationen hierzu finden Sie in der MSDN-Dokumentation.
Lassen Sie sich nicht TOO verzetteln in der minutae der losen Kopplung aber, wenn es keine Chance , dass die Dinge bekommen wiederverwendet werden (zB eine Domäne Projekt mit mehr als einem UI) und die Lebensdauer des Produkts ist klein, dann lose Kopplung wird weniger von Priorität (es wird weiterhin berücksichtigt), aber es liegt weiterhin in der Verantwortung der Architekten / Techniker, die Ihren Code überprüfen.
quelle
quelle
Schauen Sie sich die 5 SOLID- Prinzipien an. Durch die Einhaltung des SRP werden der ISP und der DIP die Kopplung erheblich verringern, wobei der DIP bei weitem der stärkste ist. Es ist das Grundprinzip unter dem bereits erwähnten DI .
Auch GRASP ist einen Blick wert. Es ist eine seltsame Mischung zwischen abstrakten Konzepten (die zunächst schwer umzusetzen sind) und konkreten Mustern (die tatsächlich hilfreich sein könnten), aber Schönheit ist derzeit wahrscheinlich das geringste Ihrer Anliegen.
Und zuletzt finden Sie diesen Abschnitt über IoC als Einstiegspunkt für gängige Techniken sehr nützlich.
Tatsächlich habe ich eine Frage zum Stackoverflow gefunden , in der ich die Anwendung von SOLID auf ein konkretes Problem demonstriere. Könnte eine interessante Lektüre sein.
quelle
Laut Wikipedia:
Das Problem der engen Kupplung macht es schwierig, Änderungen vorzunehmen. (Viele Autoren scheinen zu vermuten, dass dies hauptsächlich zu Problemen bei der Wartung führt, aber meiner Erfahrung nach ist dies auch bei der anfänglichen Entwicklung relevant.) In eng gekoppelten Systemen kann es vorkommen, dass eine Änderung an einem Modul im System zusätzliche Änderungen erfordert in den Modulen, an die es gekoppelt ist. Meistens erfordert dies mehr Änderungen in anderen Modulen und so weiter.
Im Gegensatz dazu sind in einem lose gekoppelten System Änderungen relativ isoliert. Sie sind daher kostengünstiger und können mit größerem Vertrauen hergestellt werden.
In Ihrem speziellen Beispiel bietet das Ereignisbehandlungsmaterial eine gewisse Trennung zwischen der GUI und den zugrunde liegenden Daten. Es hört sich jedoch so an, als gäbe es andere Bereiche der Trennung, die Sie erkunden könnten. Ohne weitere Details Ihrer speziellen Situation ist es schwierig, genau zu sein. Sie können jedoch mit einer dreistufigen Architektur beginnen, die sich voneinander trennt:
Zu berücksichtigen ist, dass sich bei kleinen, einmaligen Anwendungen mit einem einzelnen Entwickler die Vorteile der Durchsetzung einer losen Kopplung auf allen Ebenen möglicherweise nicht lohnen. Auf der anderen Seite sind größere, komplexere Anwendungen mit mehreren Entwicklern ein Muss. Anfänglich fallen Kosten für die Einführung der Abstraktionen und die Schulung von Entwicklern an, die mit dem Code hinsichtlich seiner Architektur nicht vertraut sind. Auf lange Sicht bietet die lose Kupplung jedoch Vorteile, die die Kosten bei weitem überwiegen.
Wenn Sie es ernst meinen, lose gekoppelte Systeme zu entwerfen, informieren Sie sich über SOLID-Prinzipien und Entwurfsmuster.
Es ist jedoch wichtig zu erkennen, dass diese Muster und Prinzipien genau das sind - Muster und Prinzipien. Sie sind keine Regeln. Dies bedeutet, dass sie pragmatisch und intelligent angewendet werden müssen
Wenn es um Muster geht, ist es wichtig zu verstehen, dass es keine einzige "korrekte" Implementierung eines der Muster gibt. Sie sind auch keine Cookie-Cutter-Vorlagen zum Entwerfen Ihrer eigenen Implementierung. Sie sind da, um Ihnen zu sagen, welche Form eine gute Lösung haben könnte, und um Ihnen eine gemeinsame Sprache für die Kommunikation von Entwurfsentscheidungen mit anderen Entwicklern bereitzustellen.
Alles Gute.
quelle
Verwenden Sie Abhängigkeitsinjektion, Strategiemuster und Ereignisse. Im Allgemeinen: Informieren Sie sich über Entwurfsmuster, es geht um lose Kopplung und das Reduzieren von Abhängigkeiten. Ich würde sagen, Ereignisse sind ungefähr so lose miteinander verbunden, wie Sie es erwarten würden, während Abhängigkeitsinjektion und Strategiemuster einige Schnittstellen erfordern.
Ein guter Trick besteht darin, Klassen in verschiedenen Bibliotheken / Assemblys zu platzieren und sie von so wenigen anderen Bibliotheken wie möglich abhängig zu machen, sodass Sie gezwungen sind, den Refactor zu verwenden, um weniger Abhängigkeiten zu verwenden
quelle
Lassen Sie mich eine alternative Ansicht geben. Ich denke nur daran, dass jede Klasse eine gute API ist. Die Reihenfolge, in der die Methoden aufgerufen werden, ist offensichtlich. Was sie tun, ist offensichtlich. Sie haben die Anzahl der Methoden auf das erforderliche Minimum reduziert. Zum Beispiel,
init, öffnen, schließen
gegen
setTheFoo, setBar, initX, getConnection, close
Das erste ist offensichtlich und sieht aus wie eine schöne API. Der zweite kann Fehler verursachen, wenn er in der falschen Reihenfolge aufgerufen wird.
Ich mache mir keine Sorgen, dass ich Anrufer ändern und neu kompilieren muss. Ich pflege eine Menge Code, einige davon neu und einige 15 Jahre alt. Ich möchte normalerweise Compilerfehler, wenn ich Änderungen vornehme. Manchmal werde ich aus diesem Grund absichtlich eine API brechen. Es gibt mir die Möglichkeit, die Auswirkungen auf jeden Anrufer zu berücksichtigen. Ich bin kein großer Fan von Abhängigkeitsinjektion, weil ich meinen Code ohne Blackboxen visuell verfolgen möchte und der Compiler so viele Fehler wie möglich abfangen soll.
quelle