Como estruturar interfaces quando objetos usam apenas parte da interface?

9

Eu tenho um projeto em que tenho duas classes que exigem um objeto de acesso ao banco de dados que atualiza a mesma tabela. As restrições da estrutura e do projeto fazem com que eu não possa combinar essas duas classes. Eu criei um caso abaixo que mostra como está a instalação. A classe A precisa ser capaz de atualizar e ler o registro, enquanto a classe B precisa ser capaz de atualizar e excluir o registro.

Se eu usar as classes como elas são, funciona muito bem, mas estou tendo um problema com o fato de que cada uma das classes exige uma funcionalidade que não está sendo usada para ser implementada. Por exemplo, para usar a classe A, preciso passar um dao que implemente a função de exclusão, mesmo que nunca seja chamada. Da mesma forma, eu tenho que passar para a classe B um dao que implementa a função de leitura, mas nunca será chamada.

Pensei em abordá-lo tendo interfaces que herdam outras pessoas (IReadDao, IUpdateDao, IDeleteDao sendo os daos que seriam herdados), mas essa abordagem basicamente requer uma interface diferente para cada combinação de funções (IUpdateAndRead, IReadAndDelete, IReadAndUpdate ... )

Eu quero usar uma interface para o dao porque não quero associar o aplicativo ao banco de dados. Existe um padrão ou método para realizar o que quero que alguém saiba? Desde já, obrigado.

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
fonte
2
Por que você precisa de interfaces separadas para cada combinação, em vez de apenas fazer com que cada classe que as use implemente as que precisam?
yitzih
No caso acima, em vez de passar o IDao para o construtor de A, eu teria que passar um objeto que implementa IUpdate e IRead, então qual seria o tipo da variável de instância "dao"? Não teria que ser algo como IUpdateAndReadDao? Ele ainda precisa ser uma interface, porque se eu pedir para executar uma implementação específica do banco de dados, acoplarei a classe ao banco de dados. Era isso que você estava perguntando?
jteezy14
3
Eu acho que este é um exemplo perfeito do Princípio de Segregação de Interface (a Ipartir de SOLID). Pode querer ler um pouco sobre isso.
Christopher Francisco

Respostas:

10

Pelo comentário de Christopher, provavelmente é um pouco melhor segregar as interfaces . Então você precisaria de pelo menos IReadDao, IDeleteDaoe IUpdateDao. Observe que você não precisa necessariamente de três classes; você pode ter uma grande classe DAO que implementa todas as três interfaces, se fizer sentido que a base de código seja combinada dessa maneira.

Para evitar explosão combinatória (por exemplo, para evitar a necessidade de um IReadUpdate, IDeleteUpdate, etc interface) pode proporcionar as interfaces separadamente em injecção construtor (pode passar o mesmo objecto duas vezes mais diferentes parâmetros), ou proporcionar um único objecto que suporte dois ou mais interfaces em uma chamada de método genérica usando extends.

Injeção de construtor:

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

Injeção de incubadora, usando o método genérico:

<T extends IUpdateDao & IDeleteDao> void InitializeDao(T dao)  //Pass a single object that implements both IUpdateDao and IDeleteDao
John Wu
fonte
Ao usar a injeção de setter, como declarar a variável de instância que estou configurando na função InitializeDao?
jteezy14
Você precisaria de duas variáveis ​​de instância (uma para exclusões e outra para atualizações) ... atribuídas daoa ambas.
John Wu
Ah, sim, isso faz sentido. Muito obrigado pela ótima resposta!
jteezy14