Programmierung auf datenorientierte Schnittstellen

9

Es gibt einen Teil unserer Codebasis, der im folgenden Stil geschrieben ist:

// IScheduledTask.cs
public interface IScheduledTask
{
    string TaskName { get; set; }
    int TaskPriority { get; set; }
    List<IScheduledTask> Subtasks { get; set; }
    // ... several more properties in this vein
}

// ScheduledTaskImpl.cs
public class ScheduledTaskImpl : IScheduledTask
{
    public string TaskName { get; set; }
    public int TaskPriority { get; set; }
    public List<IScheduledTask> Subtasks { get; set; }
    // ... several more properties in this vein, 
    // perhaps a constructor or two for convenience.
}

Das heißt, es gibt eine große Anzahl von Schnittstellen, die nur eine Reihe von Eigenschaften ohne Verhalten angeben, wobei jeweils eine einzige entsprechende Implementierung diese mit automatischen Eigenschaften implementiert. Der Code wurde von jemandem geschrieben, der ziemlich hochrangig ist (viel mehr als ich) und abgesehen von dieser Verwendung von Schnittstellen vernünftigen Verfahrenscode. Ich habe mich gefragt, ob jemand auf diesen Stil gestoßen ist / ihn verwendet hat und ob er Vorteile gegenüber der Verwendung konkreter DTOs überall ohne Schnittstellen hat.

Walpen
quelle
1
Ein Grund, warum ich das getan habe, ist die COM-Kompatibilität, aber das scheint hier nicht Ihr Grund zu sein.
Mike unterstützt Monica
@ Mike Interessant, ich habe noch nie COM verwendet und es wird hier nicht verwendet. Ich werde den Autor dieses Abschnitts des Codes fragen, ob er vorhatte, die COM oder etwas anderes zu verwenden.
Walpen
Ich vermute, dass er davon ausgeht, dass in relativ naher Zukunft mindestens eine dieser Eigenschaften nicht automatisch sein wird, und dass es eine Gewohnheit ist, diese Boilerplate zu Beginn aufzuschreiben, um später Zeit zu sparen, obwohl ich es bin Kein C # -Typ, das ist alles, was ich sagen kann.
Ixrec
6
IMHO ist es die Überbesessenheit und falsche Anwendung von Code auf Schnittstellen, nicht auf die Implementierung . Eine wichtige Aussage ist die alleinige Umsetzung . Der Punkt von Schnittstellen ist Polymorphismus, aber eine Nur-Eigenschaften-Klasse ist in gewissem Sinne polymorph, indem sie einfach unterschiedliche Werte hat. Selbst mit Verhalten - Methoden - macht es bei einer einzigen Implementierung keinen Sinn. Dieser Unsinn ist überall in unserer Codebasis zu finden und behindert bestenfalls die Wartung. Ich verschwende viel zu viel Zeit damit zu fragen, warum, was und wo. Warum haben sie es getan, welches Design sehe ich nicht und wo sind die anderen Implementierungen?
Radarbob
2
Die meisten der vorhergehenden Kommentare beziehen sich auf den "Code-Geruch" der einzelnen Implementierung einer vorzeitigen Abstraktion. Es gibt jedoch auch ein anämisches Domain-Modell "Code-Geruch" hier, siehe: martinfowler.com/bliki/AnemicDomainModel.html
Erik Eidt

Antworten:

5

Hier sind meine zwei Cent:

  • Wir sind uns nicht sicher, ob ein DTO jemals ein bisschen Verhalten zeigen wird. Für mich gibt es also Objekte, die sich in Zukunft möglicherweise verhalten oder nicht.
  • Eine mögliche Ursache für so viele sole-Implementierungsklassen aufweist , ist ein Mangel an Design , zum Beispiel in Ermangelung eines solchen sehen ScheduledTask, ScheduledJob, ScheduledEvent, ScheduledInspection, usw., sollte nur eine getrennt Schedulablejede Implementierer planbare Schnittstelle zu machen.
  • Das Erstellen der Schnittstellen ist eine sehr gute Vorgehensweise. Sie gibt Ihnen einen Einblick in das, was Sie benötigen. Viele Schnittstellen haben zunächst überhaupt keine Implementierung. Dann schreibt jemand eine Implementierung und sie haben diese. Der Zustand einer alleinigen Implementierung ist vorübergehend, wenn Sie vermieden haben, was ich im zweiten Punkt erwähne. Woher wissen Sie im Voraus, dass eine Schnittstelle niemals eine zweite oder dritte Implementierung haben wird?
  • Der Name, den wir für eine gut durchdachte Schnittstelle wählen, ändert sich in Zukunft tendenziell nicht, sodass die Abhängigkeiten von dieser Schnittstelle nicht beeinträchtigt werden. Andererseits kann eine konkrete Klasse umbenannt werden, wenn sich etwas Wichtiges in der Art und Weise, wie sie implementiert wird, ändert. Beispielsweise könnte sich eine benannte konkrete Klasse TaxSheetändern, SessionAwareTaxSheetweil eine wesentliche Überarbeitung vorgenommen wurde, die Schnittstelle ITaxSheetjedoch wahrscheinlich nicht so einfach umbenannt werden kann.

Endeffekt:

  • Gute Entwurfspraktiken gelten auch für DTOs.
  • DTOs können ein wenig später hinzugefügt werden.
  • Jede Schnittstelle beginnt mit nur einer Implementierung.
  • Wenn Sie jedoch zu viele Kombinationen aus einer Schnittstelle und einer Klasse haben, kann es an Design mangeln, auf das hingewiesen werden sollte. Ihre Beschäftigung kann gerechtfertigt sein .
Tulains Córdova
quelle
4
Ich habe Ihre Antwort positiv bewertet, aber Ihre zweite Aufzählung impliziert, dass alle Klassen automatisch eine entsprechende Schnittstelle haben sollten, eine Position, der ich nie zugestimmt habe. Das Schreiben von Schnittstellen mit der Möglichkeit, dass Sie möglicherweise eine zweite Implementierung haben, führt lediglich zu einer Proliferation der Schnittstelle und zu YAGNI.
Robert Harvey
1

Ein besonderes Problem, das ich bei DTOs gesehen habe, die Schnittstellen verwenden, ist, dass dies Folgendes ermöglicht:

public interface ISomeDTO { string SomeProperty { get; set; }}

public class SomeDTO : ISomeDTO
{
    public string SomeProperty { get; set; }
    string ISomeDTO.SomeProperty { get { /* different behavior */ } set { SomeProperty = value; } }
}

Ich habe gesehen, dass dieses Muster als schneller, schmutziger Hack angewendet wurde, um ein kritisches Verhalten zu implementieren. Dies führt zu Code, der sich sehr verwirrend verhalten kann:

SomeDTO a = GetSomeDTO();
ISomeDTO ia = a;
Assert.IsTrue(a.SomeProperty == ia.SomeProperty); // assert fails!

Dies ist schwer zu pflegen und verwirrend zu versuchen, zu entwirren oder umzugestalten. IMO: Das Hinzufügen von Verhalten zu DTOs verstößt gegen das Prinzip der Einzelverantwortung. Der Zweck eines DTO besteht darin, Daten in einem Format darzustellen, das von einem Persistenz-Framework beibehalten und ausgefüllt werden kann.

DTOs sind keine Domänenmodelle. Wir sollten uns keine Sorgen machen, wenn DTOs anämisch sind. Um Martin Fowlers Diskussion über das anämische Domänenmodell zu zitieren :

Hervorzuheben ist auch, dass das Einfügen von Verhalten in Domänenobjekte nicht dem soliden Ansatz widersprechen sollte, mithilfe von Layering die Domänenlogik von Dingen wie Persistenz und Präsentationsverantwortung zu trennen .

Erik
quelle