¿Cómo estructuro las interfaces cuando los objetos solo usan parte de la interfaz?

9

Tengo un proyecto donde tengo dos clases que requieren un objeto de acceso a la base de datos que actualiza la misma tabla. Las restricciones del marco y el proyecto hacen que no pueda combinar estas dos clases. He creado un caso a continuación que muestra cómo es la configuración. La clase A necesita poder actualizar y leer el registro, mientras que la clase B debe poder actualizar y eliminar el registro.

Si uso las clases como están, funciona bien, pero tengo un problema con el hecho de que cada una de las clases requiere una funcionalidad que no está utilizando para implementarse. Por ejemplo, para usar la clase A, tengo que pasarle un dao que implemente la función de eliminación, aunque nunca se llamará. Del mismo modo, tengo que pasar la clase B a un dao que implementa la función de lectura, pero nunca se llamará.

Pensé en abordarlo teniendo interfaces que hereden otros (IReadDao, IUpdateDao, IDeleteDao son los daos que se heredarían), pero este enfoque básicamente requeriría una interfaz diferente para cada combinación de funciones (IUpdateAndRead, IReadAndDelete, IReadAndUpdate ... )

Quiero usar una interfaz para el dao porque no quiero acoplar la aplicación con la base de datos. ¿Hay algún patrón o método para lograr lo que quiero que alguien sepa? Gracias por adelantado.

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
fuente
2
¿Por qué necesita interfaces separadas para cada combinación en lugar de que cada clase que las usa implemente las que necesita?
yitzih
En el caso anterior, en lugar de pasar IDao al constructor de A, tendría que pasar un objeto que implemente IUpdate e IRead, entonces, ¿cuál sería el tipo de la variable de instancia "dao"? ¿No tendría que ser algo así como IUpdateAndReadDao? Todavía debe ser una interfaz porque si le digo que tome una implementación que sea específica de la base de datos, he acoplado la clase a la base de datos. ¿Es eso lo que preguntabas?
jteezy14
3
Creo que este es un ejemplo perfecto del Principio de segregación de interfaz (el Ide SOLID). Podría querer leer un poco sobre eso.
Christopher Francisco

Respuestas:

10

Según el comentario de Christopher, probablemente sea un poco mejor segregar las interfaces . Por lo que tendría por lo menos IReadDao, IDeleteDaoy IUpdateDao. Tenga en cuenta que no necesariamente necesita tres clases; puede tener una gran clase DAO que implemente las tres interfaces, si tiene sentido que la base de código se combine de esa manera.

Para evitar la explosión combinatoria (por ejemplo, para evitar la necesidad de un IReadUpdate, IDeleteUpdate, etc. interfaz) puede proporcionar las interfaces por separado en la inyección de constructor (que puede pasar el mismo objeto doble de diferentes parámetros), o proporcionar un único objeto de soportar dos o más interfaces en un método genérico llamar usando extends.

Inyección de constructor:

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

Inyección de Setter, utilizando un método genérico:

<T extends IUpdateDao & IDeleteDao> void InitializeDao(T dao)  //Pass a single object that implements both IUpdateDao and IDeleteDao
John Wu
fuente
Cuando uso la inyección de setter, ¿cómo declararía la variable de instancia que estoy configurando en la función InitializeDao?
jteezy14
Necesitaría dos variables de instancia (una para eliminar, una para actualizaciones) ... asignar daoa ambas.
John Wu
Oh si, eso tiene sentido. Muchas gracias por la gran respuesta!
jteezy14