Wie strukturiere ich Schnittstellen, wenn Objekte nur einen Teil der Schnittstelle verwenden?

9

Ich habe ein Projekt, in dem ich zwei Klassen habe, für die beide ein Datenbankzugriffsobjekt erforderlich ist, das dieselbe Tabelle aktualisiert. Die Einschränkungen des Frameworks und des Projekts machen es so, dass ich diese beiden Klassen nicht kombinieren kann. Ich habe unten einen Fall erstellt, der zeigt, wie das Setup ist. Klasse A muss in der Lage sein, den Datensatz zu aktualisieren und zu lesen, während Klasse B in der Lage sein muss, den Datensatz zu aktualisieren und zu löschen.

Wenn ich die Klassen so verwende, wie sie sind, funktioniert es einwandfrei, aber ich habe ein Problem damit, dass jede der Klassen Funktionen benötigt, deren Implementierung nicht verwendet wird. Um beispielsweise Klasse A zu verwenden, muss ich ein Dao übergeben, das die Löschfunktion implementiert, obwohl sie niemals aufgerufen wird. Ebenso muss ich Klasse B ein Dao übergeben, das die Lesefunktion implementiert, aber niemals aufgerufen wird.

Ich dachte darüber nach, es durch Schnittstellen zu erreichen, die andere erben (IReadDao, IUpdateDao, IDeleteDao sind die Daos, von denen geerbt werden würde), aber dieser Ansatz würde grundsätzlich eine andere Schnittstelle für jede Funktionskombination erfordern (IUpdateAndRead, IReadAndDelete, IReadAndUpdate ... )

Ich möchte eine Schnittstelle für das Dao verwenden, da ich die Anwendung nicht mit der Datenbank koppeln möchte. Gibt es ein Muster oder eine Methode, um das zu erreichen, was ich will, von dem jeder weiß? Danke im Voraus.

class IDao {

  void update(ModelDao model);
  void delete(String guid);
  ModelDao read(String guid);

}

Class A {

  private IDao dao;

  public A(IDao dao) {

    this.dao = dao;

  }

  public void doStuff() {

    ModelDao model = new ModelDao();

    ...

    dao.update(model);

  }

  public void readThenDoSomething(String id) {

    ModelDao model = dao.read(id);

    ...

  }

}

Class B {

  private IDao dao;

  public B(IDao dao) {

    this.dao = dao;

  }

  public void makeUpdate() {

    ModelDao model = new ModelDao();

    ...

    dao.update(model);

  }

  public void delete(String id) {

    dao.delete(id);

  }

}
jteezy14
quelle
2
Warum benötigen Sie separate Schnittstellen für jede Kombination, anstatt nur von jeder Klasse, die sie verwendet, die benötigten zu implementieren?
Yitzih
Im obigen Fall müsste ich, anstatt IDao an den Konstruktor von A zu übergeben, ein Objekt übergeben, das IUpdate und IRead implementiert. Wie lautet also der Typ der Instanzvariablen "dao"? Müsste es nicht so etwas wie IUpdateAndReadDao sein? Es muss immer noch eine Schnittstelle sein, denn wenn ich ihm sage, dass eine datenbankspezifische Implementierung verwendet werden soll, habe ich die Klasse an die Datenbank gekoppelt. Haben Sie das gefragt?
jteezy14
3
Ich denke, dies ist ein perfektes Beispiel für das Prinzip der Schnittstellentrennung (das Ivon SOLID). Vielleicht möchten Sie ein bisschen darüber lesen.
Christopher Francisco

Antworten:

10

Laut Christophers Kommentar ist es wahrscheinlich etwas besser, die Schnittstellen zu trennen . So benötigen Sie mindestens würden IReadDao, IDeleteDaound IUpdateDao. Beachten Sie, dass Sie nicht unbedingt drei Klassen benötigen. Sie können eine große DAO-Klasse haben, die alle drei Schnittstellen implementiert, wenn es sinnvoll ist, die Codebasis auf diese Weise zu kombinieren.

Um kombinatorische Explosion zu vermeiden (zB die Notwendigkeit , ein zu vermeiden IReadUpdate, IDeleteUpdateetc. Schnittstelle) Sie die Schnittstellen separat in Konstruktor Injektion zur Verfügung stellen können (können Sie das gleiche Objekt doppelt so unterschiedliche Parameter übergeben), oder bieten ein einzelnes Objekt Unterstützung von zwei oder mehr Schnittstellen in einem generischen Methodenaufruf mit extends.

Konstruktorinjektion:

class MyDaoLibrary : IUpdateDao, IInsertDao, IDeleteDao {
    //Etc....
}

class A
{
    //It is OK if the IoC container factory provides the same instance for both parameters.
    a(IUpdateDao dao1, IDeleteDao dao2) {
        this.updater = dao1;
        this.deleter = dao2;
    }
    //Etc....
}

Setter-Injektion mit generischer Methode:

<T extends IUpdateDao & IDeleteDao> void InitializeDao(T dao)  //Pass a single object that implements both IUpdateDao and IDeleteDao
John Wu
quelle
Wie würde ich bei Verwendung der Setter-Injection die Instanzvariable deklarieren, die ich in der InitializeDao-Funktion einstelle?
jteezy14
Sie benötigen zwei Instanzvariablen (eine zum Löschen, eine für Aktualisierungen) ... die daobeiden zugewiesen sind .
John Wu
Oh ja, das macht Sinn. Vielen Dank für die tolle Antwort!
jteezy14