RESPOSTAS
O ideal é fazer o reuso da coleção de dados, assim o tipo da resposta deve ser a interface negócio-dados (RepositorioClasseBasica), onde deve ser criada uma nova instância da coleção com os elementos que satisfazem o critério da busca.
Uma maneira é utilizar o padrão de projetos Iterator, e definir uma coleção de dados iterável, a qual permite varrer os objetos incluídos na mesma. Assim os repositórios devem ter o método getIterator() que retornam uma coleção iterável com os elementos contidos nos mesmos. Por exemplo, este método deve ser chamado da coleção de dados retornada como resposta de uma consulta, como dito no item anterior, para listar na GUI os objetos retornados como resposta.
Deve-se definir uma classe que armazena objetos do tipo da classe básica em memória (array ou lista), por exemplo IteratorClasseBasicaArray, com métodos para inserir, retornar o próximo objeto, e verificar se ainda tem objetos a retornar! Note que o método inserir deve ser usado pelo repositório para povoar a coleção iterável antes de retorná-la. Já os métodos para retornar o próximo objeto, e verificar se ainda tem objetos a retornar são usados pela GUI para mostrará o resultado de uma consulta. Os nomes dos métodos de uma coleção iterável devem ser:void add(ClasseBasica object) boolean hasNext() ClasseBasica next()Deve-se ainda definir uma interface, IteratorClasseBasica, implementada pela coleção iterável, a qual declara apenas os métodos hasNext e next. Essa interface deve ser o tipo retornado pelo método getIterator do repositório, de modo que na GUI apenas os mesmos possam ser executados.Observação: No caso de o tópico interfaces ainda não ter sido ministrado, a solução poder ser simplemente implementar o método getIterator retornando ClasseBassica[], ao invés da interface, por enquanto. Quem preferir pode implementar apenas a coleção iterável e retorná-la.
Como devo implementar as coleções de dados para classes com herança? Quantas tabelas devo criar no banco relacional?
Vamos imaginar uma hierarquia de classes com três classe: Paciente e Medico que herdam de Pessoa. Neste caso devem ser definidas três tabelas, uma para cada classe (Pessoa, Paciente e Medico). Além disso deve ser criada uma classe repositório em banco de dados para cada classe. Para efeitos de exemplo vamos considerar que a classe Pessoa tem como atributo codigo e nome e a classe Paciente tem o atributo convenio. Os repositórios podem ser implementados de duas formas:
Situação 1Você tem definido um cadastro de pacientes e outro de médicos, e portanto já tem definidos as interfaces negócio-dados para os mesmos. Neste caso RepositorioPessoasBDR é uma classe auxiliar das coleções de dados RepositorioPacientesBDR e RepositorioMedicosBDR.
//trabalha apenas com a tabela pessoa class RepositorioPessoasBDR { void inserir(Pessoa p) ... { // sql para inserir os dados da pessoa na tabela de pessoa } void remover(codigo) ... void atualizar(Pessoa) ... Pessoa procurar(codigo) ... boolean existe(codigo) ... } //trabalha apenas com a tabela paciente class RepositorioPacientesBDR implements RepositorioPacientes { RepositorioPessoaBDR pessoas; void inserir(Paciente p) ... { pessoas.inserir(p); // sql para inserir os dados específicos do paciente na tabela de paciente } Paciente procurar(codigo) ... { Pessoa p = pessoas.procurar(codigo); // sql que consulta os dados especificos do paciente na tabela de paciente return new Paciente(p.getCodigo(), p.getNome(),convenio); } void remover(codigo) ... void atualizar(Paciente) ... boolean existe(codigo) ... } class RepositorioMedicosBDR implements RepositorioMedicos { // similar a classe RepositorioPacientesBRD }Situação 2
Você tem apenas o cadastro de pessoas e portanto apenas a interface negócio-dados para pessoa. Neste caso RepositorioPacientesBDR e RepositorioMedicosBDR são classes auxiliares da coleção de dados RepositorioPessoasBDR.
class RepositorioPessoasBDR implements RepositorioPessoas { RepositorioMedicosBDR medicos; RepositorioPacientesBDR pacientes; void inserir(Pessoa p) ... { // sql para inserir os dados na tabela de pessoa if (p instanceof Paciente) pacientes.inserir(p); else if (p instanceof Medico) medicos.inserir(p); } Pessoa procurar(codigo) ... { // recupera os dados da tabela de pessoa com esse código Pessoa p = new Pessoa(codigo, nome); if (medicos.existe(codigo)) resposta = medicos.procurar(p); else if (pacientes.existe(codigo)) resposta = pacientes.procurar(p); return p; } void remover(codigo) ... void atualizar(Pessoa) ... boolean existe(codigo) ... } //trabalha apenas com a tabela paciente class RepositorioPacientesBDR { RepositorioPessoaBDR pessoas; void inserir(Paciente) ... void remover(codigo) ... void atualizar(Paciente) ... Paciente procurar(Pessoa p) ... { // sql para recuperar os dados do paciente com a // mesma identificação da pessoa passada por parametro return new Paciente(p.getCodigo(), p.getNome(), convenio) } boolean existe(codigo) ... } class RepositorioMedicosBDR { // similar a classe RepositorioPacientesBRD }
Mas e se no item anterior a classe Pessoa for abstrata?
Neste caso não poderemos instanciar um objeto de Pessoa para auxiliar na busca pelo Medico ou Paciente. Logo usaremos uma variação da solução proposta no item anterior, onde objetos da classe pessoa não são criados, podendo até ser visto como uma otimização sa solução anterior.
Situação 1
//trabalha apenas com a tabela pessoa class RepositorioPessoasBDR { void inserir(Pessoa p) ... { // sql para inserir os dados da pessoa na tabela de pessoa } void remover(codigo) ... void atualizar(Pessoa) ... Pessoa procurar(Pessoa p) ... { // sql para recuperar os dados da pessoa recebida na // tabela de pessoa e chamadas aos métodos set de pessoa // para preencher os atributos de Pessoa p.setNome(nome); return p; } boolean existe(codigo) ... } //trabalha apenas com a tabela paciente class RepositorioPacientesBDR implements RepositorioPacientes { RepositorioPessoaBDR pessoas; void inserir(Paciente p) ... { pessoas.inserir(p); // sql para inserir os dados específicos do paciente na tabela de paciente } Paciente procurar(codigo) ... { // sql que consulta os dados especificos do paciente na tabela de paciente Paciente p = new Paciente(codigo, null, convenio); return (Paciente) pessoas.procurar(p); } void remover(codigo) ... void atualizar(Paciente) ... boolean existe(codigo) ... } class RepositorioMedicosBDR implements RepositorioMedicos { // similar a classe RepositorioPacientesBRD }Situação 2
class RepositorioPessoasBDR implements RepositorioPessoas { RepositorioMedicosBDR medicos; RepositorioPacientesBDR pacientes; void inserir(Pessoa p) ... { // sql para inserir os dados na tabela de pessoa if (p instanceof Paciente) pacientes.inserir(p); else if (p instanceof Medico) medicos.inserir(p); } Pessoa procurar(codigo) ... { // recupera os dados da tabela de pessoa com esse código Pessoa resposta; if (medicos.existe(codigo)) { Medico m = new Medico(codigo, nome, null); resposta = medicos.procurar(m); } else if (pacientes.existe(codigo)) { Paciente p = new Paciente(codigo, nome, null); resposta = pacientes.procurar(p); } return resposta; } void remover(codigo) ... void atualizar(Pessoa) ... boolean existe(codigo) ... } //trabalha apenas com a tabela paciente class RepositorioPacientesBDR { RepositorioPessoaBDR pessoas; void inserir(Paciente) ... void remover(codigo) ... void atualizar(Paciente) ... Paciente procurar(Paciente p) ... { // sql para recuperar os dados do paciente com a // mesma identificação do passado por parametro // e chamadas aos métodos set para atualizar os // atributos específicos de Paciente p.setConvenio(convenio); return p; } boolean existe(codigo) ... } class RepositorioMedicosBDR { // similar a classe RepositorioPacientesBRD }
De modo a manter a transparencia do meio de armazenamento para as Coleções de Negócio, a Interface Negócio-Dados deve usar a exceção RepositorioException para indicar quaisquer erros específicos do meio de armazenamento. Por exemplo, erros de bancos de dados, como queda da conexão, coluna não encontrada, chave primeria violada (SQLException em geral), ou erros de arquivo não encontrado, caso a implementação da interface use arquivos para armazenar os objetos, etc.Note que a exceção RepositorioException encapsula a exceção específica, a qual pode eventualmente ser recuperada para um tratamento específico, se for o caso. A classe RepositorioException dará como mensagem a mensagem da exceção que foi emcapsulada. O mesmo acontece caso outros os métodos, como printStackTrace(), seja chamado.