Usuário com melhor resposta
Como Eliminar os Ifs

Pergunta
-
Tenho um metodo para testar conexao com o banco de dados que recebe uma entidade burra com strings de nome, usuario senha e tipo de banco
public static bool TestarConexao(Banco banco){ IProvedor provedor; if banco.tipo == "mysql") provedor = new ProvedorMysql(); if banco.tipo == "SqlServer2000") provedor = new ProvedorSqlServer2000(); if banco.tipo == "SqlServer2008") provedor = new ProvedorSqlServer2008(); return provedor.testar(banco); }
o problema é que tenho estes ifs (são mais ou menos uns 10) e a tendencia é aumentar.Eu sei que com algum padrão de projeto (talvez strategy) eu posso resolver isso mas me falta conhecimento.
Se alguem puder me esclarecer.
Fernando Mondo
- Movido William John Adam Trindade terça-feira, 9 de agosto de 2011 14:29 Forum apropriado (De:.NET Development - Geral)
Respostas
-
Olá Fernando,
Excelente dúvida.
Nestes casos existem diversas formas de associar o banco aí, este tipo de arquitetura esta diretamente relacionada a Injeção de Dependência, no caso, o objetivo é injetar um objeto que implemente IProvedor a sua factory, o problema é que aí no TestarConexao você esta passando uma classe com uma string e fazendo um monte de IF, em questão de arquitetura esta errado, você precisa injetar a responsabilidade em 3 opções:
- passando o objeto que implemente IProvider (este seria o tipo Interface Injection),
- utilizando REFLECTION na raça
- utilizando algum container de inversão de controle como o SPRING que através de um arquivo de configuração XML voce diz qual a classe a ser instanciada e ele ja tem o reflection implementado).
Em primeiro momento, seria possível já passar o provedor instanciado para o TestaConexão ao invés do banco?
Caso contrário segue um exemplo de reflection na mão (opção 2):
using System.Reflection; public class LateBinder { /// <exception cref="System.TypeLoadException">The type couldn't be /// loaded.</exception> /// <exception cref="System.Exception">The assembly couldn't be loaded /// or other errors.</exception> /// <exception cref="System.ArgumentNullException">fullyQualifiedTypeName /// is a null reference.</exception> public static object CreateInstance(string assemblyName,string fullyQualifiedTypeName) { Type type = null; if (assemblyName != null) { // this method will also search the GAC and will use the latest // version if multiple versions are found. Assembly assembly = Assembly.LoadWithPartialName(assemblyName); if (assembly != null) { type = assembly.GetType(fullyQualifiedTypeName,true,true); } else { throw new Exception("Failed to load assembly \"" + assemblyName + "\"."); } } else { type = Type.GetType(fullyQualifiedTypeName,true,true); } // invoke the type's constructor to create an instance object instance = type.InvokeMember("", BindingFlags.CreateInstance | BindingFlags.Public | BindingFlags.Instance, null, null, new object[] { }); return instance; } public static object CreateInstance(string fullyQualifiedTypeName) { return CreateInstance(null,fullyQualifiedTypeName); } }
private static IProvider GetConnection(string assemblyName, string providerConnectionTypeName)
{
object o = LateBinder.CreateInstance(assemblyName,providerConnectionTypeName);
if (!(o is IProvider)){
throw new Exception("\"" + providerConnectionTypeName + "\" doesn't implement IProvider.");
}
return (IProvider) o;
}
Com isso basta fazer o load desta forma:public static bool TestarConexao(Banco banco){ IProvedor provedor = GetConnection(banco.AssemblyName,banco.TypeName) return provedor.testar(banco); }
Nesse caso você deverá não passar mais um nome qualquer e sim ter 2 propriedades a mais nessa classe banco, uma com o assembly do provider e outra com o nome completo da classe.
Peguei esse exemplo reflection em http://authors.aspalliance.com/thycotic/articles/view.aspx?id=5 fiz algumas modificações para o seu caso específico!
Qualquer dúvida avise!
Tornar o simples complicado é facil, tornar o complicado simples é criatividade, vontade e conhecimento
- Sugerido como Resposta Ricardo R Carneiro Gonçalves quinta-feira, 11 de agosto de 2011 01:38
- Marcado como Resposta Fernando Mondo quinta-feira, 11 de agosto de 2011 15:18
Todas as Respostas
-
Olá Fernando,
Se eu entendi bem, você podeia implementa o "Factory Pattern", acho isso resolveria seu problema.
da uma olhada nesse link.
Artur Araújo -
na verdade eu já uso uma Factory, só não coloquei aqui pra não ficar poluido mas no fim a minha Factory se parece com isso:
public IProvedor CreateProvedor(string tipo) { if (tipo == "mysql") return new ProvedorMysql(); if (tipo == "SqlServer2000") return new ProvedorSqlServer2000(); if (tipo == "SqlServer2008") return new ProvedorSqlServer2008(); }
ou seja, continuo com os Ifs -
Olá Fernando,
Excelente dúvida.
Nestes casos existem diversas formas de associar o banco aí, este tipo de arquitetura esta diretamente relacionada a Injeção de Dependência, no caso, o objetivo é injetar um objeto que implemente IProvedor a sua factory, o problema é que aí no TestarConexao você esta passando uma classe com uma string e fazendo um monte de IF, em questão de arquitetura esta errado, você precisa injetar a responsabilidade em 3 opções:
- passando o objeto que implemente IProvider (este seria o tipo Interface Injection),
- utilizando REFLECTION na raça
- utilizando algum container de inversão de controle como o SPRING que através de um arquivo de configuração XML voce diz qual a classe a ser instanciada e ele ja tem o reflection implementado).
Em primeiro momento, seria possível já passar o provedor instanciado para o TestaConexão ao invés do banco?
Caso contrário segue um exemplo de reflection na mão (opção 2):
using System.Reflection; public class LateBinder { /// <exception cref="System.TypeLoadException">The type couldn't be /// loaded.</exception> /// <exception cref="System.Exception">The assembly couldn't be loaded /// or other errors.</exception> /// <exception cref="System.ArgumentNullException">fullyQualifiedTypeName /// is a null reference.</exception> public static object CreateInstance(string assemblyName,string fullyQualifiedTypeName) { Type type = null; if (assemblyName != null) { // this method will also search the GAC and will use the latest // version if multiple versions are found. Assembly assembly = Assembly.LoadWithPartialName(assemblyName); if (assembly != null) { type = assembly.GetType(fullyQualifiedTypeName,true,true); } else { throw new Exception("Failed to load assembly \"" + assemblyName + "\"."); } } else { type = Type.GetType(fullyQualifiedTypeName,true,true); } // invoke the type's constructor to create an instance object instance = type.InvokeMember("", BindingFlags.CreateInstance | BindingFlags.Public | BindingFlags.Instance, null, null, new object[] { }); return instance; } public static object CreateInstance(string fullyQualifiedTypeName) { return CreateInstance(null,fullyQualifiedTypeName); } }
private static IProvider GetConnection(string assemblyName, string providerConnectionTypeName)
{
object o = LateBinder.CreateInstance(assemblyName,providerConnectionTypeName);
if (!(o is IProvider)){
throw new Exception("\"" + providerConnectionTypeName + "\" doesn't implement IProvider.");
}
return (IProvider) o;
}
Com isso basta fazer o load desta forma:public static bool TestarConexao(Banco banco){ IProvedor provedor = GetConnection(banco.AssemblyName,banco.TypeName) return provedor.testar(banco); }
Nesse caso você deverá não passar mais um nome qualquer e sim ter 2 propriedades a mais nessa classe banco, uma com o assembly do provider e outra com o nome completo da classe.
Peguei esse exemplo reflection em http://authors.aspalliance.com/thycotic/articles/view.aspx?id=5 fiz algumas modificações para o seu caso específico!
Qualquer dúvida avise!
Tornar o simples complicado é facil, tornar o complicado simples é criatividade, vontade e conhecimento
- Sugerido como Resposta Ricardo R Carneiro Gonçalves quinta-feira, 11 de agosto de 2011 01:38
- Marcado como Resposta Fernando Mondo quinta-feira, 11 de agosto de 2011 15:18
-
Amigo, obrigado por responder.
Reflection foi o que o pessoal aqui da empresa me aconselhou, e eu acabei usando o Activator.CreateInstance() do proprio framework.
A arquitetura do projeto possue classes burras que recebem os dados do database. portatnto a principio nao posso mudar a classe Banco.
Acabei por mudar os nomes dos provedores para "Provedor" + Banco.Tipo.
thanks!!