Abstract DAL - Schnittstelle mit interner Klasse verwenden?

8

Wir haben eine Business Logic Layer (BLL), die eng mit unserer Datenzugriffsschicht (DAL) verbunden ist. Wir telefonieren wie folgt:

using (FooData data = new FooData())
{
  data.DoSomething();
}

Es ist wichtig zu beachten, dass sich alle unsere Datenklassen internalin derselben Assembly wie die Logikklassen befinden, sodass nur die BLL auf die DAL zugreifen kann.

Um diese zu entkoppeln (um das Testen von Einheiten zu erleichtern), besteht eine Idee darin, IDataClass-Schnittstellen wie IFooData zu erstellen. Zuerst dachte ich, dass es ein Problem sein könnte, weil wir unsere Datenklassen öffentlich machen müssten, um die Schnittstellen zu implementieren. Aber wir sollten in der Lage sein, die Datenklassen intern zu halten, obwohl wir immer noch die Methoden benötigen, um öffentlich zu sein:

public interface IFooData
{
  void DoSomething();
}

internal class FooData : IFooData  // Notice the class is internal
{
  public void DoSomething();  // Method is public, but that's ok because class is internal
}

Obwohl die Methode öffentlich ist, sollten wir, da die Klasse selbst intern ist, nur unseren BLL-Zugriff zulassen.

Stimmt etwas an diesem Ansatz nicht? Gibt es eine bessere Möglichkeit, die DAL für Unit-Tests zu abstrahieren, ohne die DAL für die Welt zu öffnen, indem sie veröffentlicht wird?

Bob Horn
quelle
1
Was versuchen Sie zu erreichen, indem Sie Ihren DAL so wegschließen? Warum es geheim halten? Es enthüllt eine Reihe von Methoden, es sollte nicht genau wichtig sein, wer sie aufruft. Wenn Sie ein Problem damit haben, dass es von der falschen Stelle aufgerufen wird, liegt ein Problem mit der Codeüberprüfung vor.
Slugster
Wenn Sie die Methoden intern machen, kann nur die BLL darauf zugreifen, wodurch alle Entwickler gezwungen werden, die BLL zu durchlaufen. Wenn Sie ein großes Entwicklerteam haben, ist alles, was Sie tun können, um Fehler zu minimieren, eine gute Sache.
Bob Horn
Mir ist klar, dass Sie versuchen, den Entwicklern, die den Code verwenden, ein gewisses Maß an Handarbeit zu leisten, aber damit haben Sie sich ein weiteres Problem geschaffen. Aus diesem Grund habe ich meinen Kommentar abgegeben - es ist nichts Falsches daran, die Methoden öffentlich zu machen (dies ist eine normale Praxis). Wenn die Benutzer sie falsch verwenden, liegt ein Problem mit der Codeüberprüfung vor.
Slugster
Ich stimme dir nicht zu. Warum dann überhaupt interne und private Modifikatoren? Warum nicht einfach alles öffentlich machen?
Bob Horn
Bob, du hast den Punkt total verpasst. Der DAL macht öffentliche Inhalte über einen VERTRAG verfügbar, der Ihre Schnittstelle ist. Standard-OO-Praxis bedeutet, dass nicht bekannt ist, wer es anruft. Sagen wir es noch einmal: Sie können die Klasse intern machen, aber Sie nicht brauchen , um. Der Rest der Welt kommt mit diesem Ansatz zurecht, warum nicht Sie? Sie haben sich auch in den Fuß geschossen, sobald Sie eine andere BL oder einen anderen Dienst haben, der dieselbe DAL aufrufen muss. Werden Sie einfach weiterhin Freund-Baugruppen hinzufügen?
Slugster

Antworten:

13

Das Extrahieren einer Schnittstelle und das Sicherstellen, dass die BLL nur Schnittstellentypen verwendet, ist ein guter Weg, um die BLL ohne die Datenbank testbar zu machen.

Was Sie tun, ist absolut in Ordnung.

Oded
quelle
3
Ich werde hinzufügen, dass das, was Sie tun, in Ordnung ist, aber es ist nur der erste Schritt. Wenn sich das, was Sie jetzt tun, using (IFooData data = new FooData())in Ihrer BLL befindet, haben Sie außer der Einführung der Benutzeroberfläche keine wirklichen Änderungen vorgenommen. Sie müssen jetzt einen Weg finden, um das IFooDataexterne zu instanziieren , sonst werden Ihre BLL-Tests weiterhin mit dem DAL gekoppelt.
Avner Shahar-Kashtan
Einverstanden. Ich mache jetzt tatsächlich so etwas:DataAccessFactory.GetDataInterface<IFooData>().DoSomething();
Bob Horn
1

Sie können die InternalsVisibleToAttributeverwenden, um Ihre DAL aus Ihrer Business-Schicht herauszubrechen, aber die DAL-Methoden weiterhin intern zu halten. Am Ende erstellen Sie Ihre DAL als Freund-Assembly .

Dies ist möglicherweise kein guter Ansatz, da Sie in der Assembly.csDatei Ihres DAL angeben müssen, welche Assemblys auf den DAL zugreifen können. Es handelt sich also immer noch um eine Kopplung (DAL weiß etwas über BLL).

Um Ihre Frage zu beantworten, ist an Ihrem Ansatz nichts auszusetzen. Die Nutzung von Friend-Assemblys kann Ihnen jedoch ein wenig mehr Abstraktion bieten und Ihnen wahrscheinlich dabei helfen, Ihren Code testbarer zu machen.

Ich hoffe das hilft.

David Hoerster
quelle
Ich muss den DAL nicht wirklich aus der BLL entfernen. Ich muss nur eine Schnittstelle implementieren.
Bob Horn
Übrigens, danke. Ich bin damit vertraut InternalsVisibleTo, und wenn ich den DAL auf eine andere Baugruppe verschieben muss, würde das in der Tat helfen.
Bob Horn
@ BobHorn, also verstehe ich es nicht. Was ist das Problem, um Ihre Dal-Klassen als Interna zu machen? Sie können Ihre DAL-Schnittstellen sogar als Interna erstellen. Ich sehe hier keine Probleme.
Marcelo De Zen
Die DAL-Klassen intern zu haben, ist das, was wir derzeit haben und was wir in Zukunft haben wollen. Die Schnittstellen müssen öffentlich sein, damit die BLL eine Fabrik um eine konkrete Implementierung bitten kann, und es ist ihnen egal, ob es sich um die echte DAL oder einen Schein handelt. Ich denke, meine ganze Frage ist wirklich eine Überprüfung der geistigen Gesundheit. Vermisse ich einen besseren Weg, dies zu tun?
Bob Horn
1

Sie scheinen unbeweglich in Bezug auf Ihre Ideen zu sein, die Sie in Ihrer Frage dargelegt haben, aber ich werde trotzdem eine Antwort geben - jemand könnte sie irgendwann in der Zukunft nützlich finden.

Wenn Sie darauf bestehen, Ihr DAL intern zu halten, es aber dennoch mit einigen Komponententests erreichen möchten und die Microsoft Unit Testing-Tools nicht verwenden möchten , versuchen Sie es mit dem PrivateObject- Ansatz. Dies ist ein Wrapper zum Nachdenken, den Sie jederzeit selbst codieren können, der Ihnen jedoch einige Zeilen beim Schreiben erspart.

Beachten Sie, dass InternalsVisibleTo Verwendung ist ein gangbarer Weg, aber es ist ungeschickt - wenn Sie es verwenden , dann können Sie bereits bergab auf einem schnellen Schlitten sein. internalbedeutet, dass etwas nur innerhalb der Grenzen seiner Assembly öffentlich ist. Dieser Modifikator wird normalerweise verwendet, weil Sie nicht möchten, dass Inhalte von außen verwendet werden könnenDiese Versammlung, dann drehen Sie sich sofort um und verletzen dieses Konzept, indem Sie ein InternalsVisibleTo ... deklarieren, das eine große schmutzige rote Fahne ist, die nach einem Refactoring schreit. Das Entkoppeln ist Zeitverschwendung, wenn nur eine Baugruppe die andere aufruft. Sie können sie auch zusammenkleben. Die Entkopplung lässt darauf schließen, dass mehrere Assemblys das Ziel aufrufen (die Einführung von Unit-Tests zählt nicht für das "Multiple", da es Möglichkeiten gibt, zur Ziel-Assembly zu gelangen, wie ich bereits erwähnt habe).

Slugster
quelle
Warum scheine ich bei meinen Ideen unbeweglich zu sein? Ich bin bereit, Ihnen zuzustimmen. Ich brauche nur eine Diskussion, um mich zuerst zu überzeugen. Und sag mir nicht, dass ich mit dem Konzept verstoße InternalsVisibleTo. Ich benutze es nicht. Aus irgendeinem Grund nehmen Sie an, dass ich es benutze. Ich verstehe nicht warum.
Bob Horn
Übrigens, ich schätze Ihre Ansichten, weil ich meine überdenken muss. Denken Sie jedoch daran, ich darf auch meine Meinung haben. Du musst nicht snarky werden, weil ich dir nicht sofort zustimme.
Bob Horn
@Bob, tut mir leid, dass ich mich auf diesen bestimmten Winkel festgelegt habe. Aus Ihrem Kommentar "und wenn ich den DAL auf eine andere Baugruppe verschieben muss" geht hervor, dass Ihr DAL wahrscheinlich genau dort ist, wo ich vorgeschlagen hätte, dass es sich um ein internes Element handelt. Wenn Sie sich entschieden haben, was zu tun ist, stellen Sie sicher, dass Sie es hier aufschreiben (wenn keine Antworten passen), wird es interessant sein zu sehen, was Sie letztendlich einfallen lassen.
Slugster
1

Technisch ist an Ihrem Design nichts auszusetzen.

Ich würde jedoch einen alternativen Ansatz in Betracht ziehen: Anstatt Ihre BLL von Ihrer DAL zu entkoppeln, sollten Sie Ihre DAL besser von Ihrer Datenbank entkoppeln. Ermöglichen Sie die Erstellung von DAL-Objekten im Speicher. Wenn Ihre BLL DAL-Objekte von irgendwoher laden muss, verwenden Sie das Repository-Muster (siehe hier ), um einen direkten Kontakt der BLL mit der Datenbank zu vermeiden. Auf diese Weise können Sie

  • Erstellen Sie problemlos Unit-Tests für Ihre DAL-Objekte, ohne dass eine Datenbank erforderlich ist
  • Erstellen Sie Komponententests für Ihre BLL, indem Sie im Speicher erstellte DAL-Objekte über ein Mock-Repository als Testdaten bereitstellen (ok, Sie können jetzt darüber streiten, ob diese Tests wirklich als Komponententests bezeichnet werden sollten).

Was Sie verlieren, ist die Fähigkeit, die BLL mit DAL-Mocks zu testen. In der Realität sehen diese DAL-Scheinobjekte jedoch in der Regel Ihren echten DAL-Objekten sehr ähnlich, um eine Grundlage für nützliche Tests der BLL zu bieten. IMHO vermeidet es viel Code-Duplizierung und unnötigen Aufwand, wenn Sie Ihre realen DAL-Objekte für automatisierte Tests der BLL wiederverwenden.

Doc Brown
quelle
0

Der Schnittstellenansatz ist ein guter / allgemeiner Ansatz, um Ihre DAL beim Testen von Verbrauchern wie der BLL zu verspotten.

Beachten Sie jedoch, dass Sie durch die interne Erstellung Ihrer DAL-Betonklassen möglicherweise die direkte Prüfung Ihrer konkreten DAL erschweren, da dies jetzt impliziert, dass Ihre DAL-Komponententests auch in dieser Baugruppe durchgeführt werden müssen, ohne dass Hintertüren verwendet werden müssen wie Reflexion.

Außerdem können Sie zu einem späteren Zeitpunkt in Betracht ziehen, einen IoC-Container zu verwenden, um die Implementierung hinter einer Schnittstelle aufzubauen, die möglicherweise auch auf der internen Schnittstelle ausgelöst wird.

Sie können die Kapselung nachahmen, die Sie mit "nur intern" erreichen möchten, indem Sie eine Konvention / LINT-Regel hinzufügen, die Ihre BLL / andere Ebenen über die Schnittstelle mit der DAL koppeln müssen, nicht über die konkrete Implementierung.

StuartLC
quelle
Vielen Dank, aber die Visual Studio-Komponententests können tatsächlich interne und private Methoden in anderen Assemblys testen.
Bob Horn