Come strutturare le interfacce quando gli oggetti usano solo parte dell'interfaccia?


9

Ho un progetto in cui ho due classi che richiedono entrambi un oggetto di accesso al database che aggiorna la stessa tabella. I vincoli del framework e del progetto lo rendono tale da non poter combinare queste due classi. Di seguito ho creato un caso che mostra come è l'installazione. La classe A deve essere in grado di aggiornare e leggere il record, mentre la classe B deve essere in grado di aggiornare ed eliminare il record.

Se uso le classi così come sono, funziona perfettamente, ma sto riscontrando un problema con il fatto che ciascuna delle classi richiede funzionalità che non utilizza per essere implementata. Ad esempio, per utilizzare la classe A, devo passare un dao che implementa la funzione di eliminazione, anche se non verrà mai chiamato. Allo stesso modo, devo passare la classe B a dao che implementa la funzione di lettura ma non verrà mai chiamata.

Ho pensato di approcciarlo avendo interfacce che ereditano gli altri (IReadDao, IUpdateDao, IDeleteDao essendo i daos che sarebbero ereditati da), ma questo approccio richiederebbe sostanzialmente un'interfaccia diversa per ogni combinazione di funzioni (IUpdateAndRead, IReadAndDelete, IReadAndUpdate ... )

Voglio usare un'interfaccia per il dao perché non voglio accoppiare l'applicazione con il database. Esiste un modello o un metodo per realizzare ciò che voglio che qualcuno conosce? Grazie in anticipo.

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);

  }

}

2
Perché hai bisogno di interfacce separate per ogni combinazione anziché avere semplicemente ogni classe che le utilizza implementa quelle di cui hanno bisogno?
yitzih,

Nel caso precedente, anziché passare IDao al costruttore di A, dovrei passare un oggetto che implementa IUpdate e IRead, quindi quale sarebbe il tipo della variabile di istanza "dao"? Non dovrebbe essere qualcosa come IUpdateAndReadDao? Deve ancora essere un'interfaccia perché se gli dico di prendere un'implementazione specifica per il database ho accoppiato la classe al db. È questo quello che stavi chiedendo?
jteezy14,

3
Penso che questo sia un perfetto esempio di Interface Segregation Principle (il Ida SOLID). Potrebbe voler leggere un po 'su di esso.
Christopher Francisco,

Risposte:


10

Secondo il commento di Christopher, probabilmente è leggermente meglio separare le interfacce . Così si avrebbe bisogno di almeno IReadDao, IDeleteDaoe IUpdateDao. Nota che non hai necessariamente bisogno di tre classi; puoi avere una grande classe DAO che implementa tutte e tre le interfacce, se ha senso combinare la base di codice in quel modo.

Per evitare l'esplosione combinatoria (ad es. Per evitare la necessità di un'interfaccia IReadUpdate, IDeleteUpdateecc.) È possibile fornire le interfacce separatamente nell'iniezione del costruttore (è possibile passare lo stesso oggetto due volte rispetto a parametri diversi) o fornire un singolo oggetto che supporti due o più interfacce in una chiamata di metodo generica utilizzando extends.

Iniezione costruttore:

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....
}

Iniezione setter, con metodo generico:

<T extends IUpdateDao & IDeleteDao> void InitializeDao(T dao)  //Pass a single object that implements both IUpdateDao and IDeleteDao

Quando si usa l'iniezione setter, come dovrei dichiarare la variabile di istanza che sto impostando nella funzione InitializeDao?
jteezy14,

Avresti bisogno di due variabili di istanza (una per le eliminazioni, una per gli aggiornamenti) ... da assegnare daoa entrambi.
John Wu,

Oh sì, ha senso. Grazie mille per l'ottima risposta!
jteezy14,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.