Comment structurer les interfaces lorsque les objets n'utilisent qu'une partie de l'interface?

9

J'ai un projet où j'ai deux classes qui nécessitent toutes deux un objet d'accès à la base de données qui met à jour la même table. Les contraintes du framework et du projet font que je ne peux pas combiner ces deux classes. J'ai créé un cas ci-dessous qui montre comment est la configuration. La classe A doit pouvoir mettre à jour et lire l'enregistrement, tandis que la classe B doit pouvoir mettre à jour et supprimer l'enregistrement.

Si j'utilise les classes telles qu'elles sont, cela fonctionne très bien, mais j'ai un problème avec le fait que chacune des classes nécessite des fonctionnalités qu'elle n'utilise pas pour être implémentées. Par exemple, pour utiliser la classe A, je dois lui passer un dao qui implémente la fonction delete, même si elle ne sera jamais appelée. De même, je dois passer en classe B un dao qui implémente la fonction de lecture mais elle ne sera jamais appelée.

J'ai pensé à l'aborder en ayant des interfaces qui héritent des autres (IReadDao, IUpdateDao, IDeleteDao étant les daos dont on hériterait), mais cette approche nécessiterait essentiellement une interface différente pour chaque combinaison de fonctions (IUpdateAndRead, IReadAndDelete, IReadAndUpdate ... )

Je veux utiliser une interface pour le dao parce que je ne veux pas coupler l'application avec la base de données. Existe-t-il un modèle ou une méthode pour accomplir ce que je veux que quiconque sache? Merci d'avance.

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
la source
2
Pourquoi avez-vous besoin d'interfaces distinctes pour chaque combinaison plutôt que de simplement avoir chaque classe qui les utilise implémente celles dont ils ont besoin?
yitzih
Dans le cas ci-dessus, plutôt que de passer IDao au constructeur de A, je devrais passer un objet qui implémente IUpdate et IRead, alors quel serait le type de la variable d'instance "dao"? Ne faudrait-il pas que ce soit quelque chose comme IUpdateAndReadDao? Cela doit encore être une interface car si je lui dis de prendre une implémentation spécifique à la base de données, j'ai couplé la classe à la base de données. C'est bien ce que vous demandiez?
jteezy14
3
Je pense que c'est un parfait exemple du principe de ségrégation d'interface (le Ide SOLID). Pourrait vouloir lire un peu là-dessus.
Christopher Francisco

Réponses:

10

Selon le commentaire de Christopher, il est probablement légèrement préférable de séparer les interfaces . Donc , vous auriez besoin au moins IReadDao, IDeleteDaoet IUpdateDao. Notez que vous n'avez pas nécessairement besoin de trois classes; vous pouvez avoir une grande classe DAO qui implémente les trois interfaces, s'il est logique que la base de code soit combinée de cette manière.

Pour éviter une explosion combinatoire (par exemple pour éviter la nécessité d'une IReadUpdate, IDeleteUpdateetc. interface) vous pouvez fournir les interfaces séparément dans l' injection de constructeur (vous pouvez passer le même objet deux fois différents paramètres), ou de fournir un seul objet supportant deux ou plusieurs interfaces dans un appel de méthode générique utilisant extends.

Injection constructeur:

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 injection, utilisant une méthode générique:

<T extends IUpdateDao & IDeleteDao> void InitializeDao(T dao)  //Pass a single object that implements both IUpdateDao and IDeleteDao
John Wu
la source
Lorsque j'utilise l'injection de setter, comment déclarer la variable d'instance que je définis dans la fonction InitializeDao?
jteezy14
Vous auriez besoin de deux variables d'instance (une pour les suppressions, une pour les mises à jour) ... affectées daoaux deux.
John Wu
Oh oui, cela a du sens. Merci beaucoup pour la bonne réponse!
jteezy14