Hoje vamos recordar a importância do conceito do encapsulamento de código na linguagem C#. |
As boas práticas recomendam que você deve dar a cada tipo a menor visibilidade necessária para realizar seu objetivo. Isso geralmente é menos visibilidade do que você pensa.
Criar tipos públicos é muito fácil e é uma tarefa que fazemos automaticamente. Embora em muitas situações a visibilidade do tipo tem que ser mesmo pública, isso não é uma regra geral, e, muitas classes criadas por você podem ser internas.
As classes internas ou privadas podem implementar interfaces públicas e assim as demais partes do projeto podem acessar a funcionalidade definida nas interfaces públicas declaradas em um tipo privado. Além disso você pode limitar ainda mais a visibilidade criando classes protegidas(protected) ou privadas aninhadas dentro de sua classe original.
Quanto menos visibilidade houver, menos o sistema inteiro será alterado quando você fizer atualizações, e, quanto menos lugares puderem acessar um trecho de código, menos lugares você deve alterar quando for modificá-lo.
Dessa forma exponha apenas o que precisa ser exposto. Tente implementar interfaces públicas com classes menos visíveis. Na plataforma .NET temos vários exemplos desta abordagem.
Veja por exemplo a classe List<T> do namespace System.Collections.Generics e a classe Enumerator<T> que implementa a interface IEnumerator<T>.
Espiando um trecho de código desta classe temos:
// Exibido apenas para ilustração
public class List<T> : IEnumerable<T>
{
private class Enumerator<T> : IEnumerator<T>
{
// Contains specific implementation of
// MoveNext(), Reset(), and Current.
public Enumerator(List<T> storage)
{
// código...
}
}
public IEnumerator<T> GetEnumerator()
{
return new Enumerator<T>(this);
}
// codigo...
}
|
Quando você vai fazer uma iteração o seu código nunca precisa saber nada sobre a classe Enumerator<T>. Tudo que você precisa saber é que você obtém um objeto que implementa a interface IEnumerator<T> quando chama a função GetEnumerator em um objeto List<T>. O tipo específico é um detalhe de implementação.
Percebeu que a interface pública implementa a classe privada e a interface pública usa a instância da classe privada que esta encapsulada, e, isso é transparente para você. Este é um padrão que se repete em outras coleções deste namespace.
Mas qual a vantagem disso ?
A classe Enumerator<T> sendo
privada oferece muitas
vantagens:
Percebeu agora !!!
Criar classes internas é um método muitas vezes esquecido de limitar o escopo dos tipos. Por padrão, a maioria dos programadores cria classes públicas o tempo todo, sem pensar nas alternativas.
Em vez de aceitar o padrão sem pensar, você deve pensar cuidadosamente onde seu tipo será usado, se será útil para todos os clientes ou se será usado principalmente internamente apenas pelo assembly.
Expor sua funcionalidade usando interfaces permite que você crie classes internas mais facilmente, sem limitar sua utilidade fora do assembly, e, usar classes internas permite substituir a classe por uma versão diferente, desde que implemente as mesmas interfaces.
Vejamos um exemplo bem simples para ilustrar esse conceito.
Suponha que seu sistema usa a classe ValidarTelefone que faz a validação no número dos telefones locais com o código abaixo:
public class ValidarTelefone
{
public bool ValidaNumero(NumeroTelefone numeroTelefone)
{
// faz a validação
// verifica o código de área válido
return true;
}
}
|
Um belo dia você recebe um pedido para validar também números de telefones internacionais, pois o sistema agora aceita pedidos do exterior.
Do jeito que esta, essa classe vai falhar quando for validar um número de telefone internacional, e, agora ela precisa validar os números locais e ainda os números do exterior.
Assim, ao invés de cair na tentação de estender a funcionalidade desta classe incluindo métodos para validar os telefones internacionais, você sabiamente preferiu reduzir o acoplamento entre os módulos e criou uma interface pública para realizar a validação de qualquer número de telefone:
public interfac IValidarTelefone
{
public bool ValidaNumero(NumeroTelefone numeroTelefone)
}
|
A seguir você alterou a classe que fazia a validação local, alterando também o seu nome para ValidaTelefoneLocal, para implementar esta interface e definiu a sua visibilidade como internal:
internal class ValidaTelefoneLocal : IValidarTelefone
{
public bool ValidaNumero(NumeroTelefone numeroTelefone)
{
// faz a validação
// verifica o código de área válido
return true;
}
}
|
Depois você criou uma classe chamada ValidaTelefoneInternacional que também implementa a interface e valida os números do exterior, declarando o escopo da classe como internal:
internal class ValidaTelefoneInternacional : IValidarTelefone
{
public bool ValidaNumero(NumeroTelefone numeroTelefone)
{
// faz a validação
// verifica o código de área válido
return true;
}
}
|
Para concluir a implementação basta você criar a classe apropriada com base no tipo do número de telefone para cada região do mundo definindo o escopo de visibilidade como internal.
Percebeu que apenas a interface é pública e portanto apenas ela é visível fora do Assembly.
As classes específicas para os telefones internacionais serão visíveis apenas dentro do Assembly.
Você pode adicionar diferentes classes de validação para diferentes regiões sem perturbar outros assemblies do sistema. Limitando o escopo das classes, você limitou o código que precisa alterar para atualizar e estender o sistema inteiro.
É claro que existem outras opções como criar uma classe base abstrata ValidarTelefone que contém algoritmos de implementação comuns, e os clientes podem acessar a funcionalidade pública por meio da classe base acessível.
Usando menos tipos públicos
teremos uma área de
superfície pública menor que facilitará a cobertura dos
testes de unidade. Se houver menos tipos públicos, há
menos métodos acessíveis ao público para os quais você
precisa criar testes.
Além disso, se mais APIs públicas forem expostas por
meio de interfaces, você criou automaticamente um
sistema no qual poderá substituir esses tipos usando
algum tipo de stubs para fins de teste de unidade.
Pense sempre que essas classes e interfaces que você expõe publicamente
ao mundo exterior são o seu contrato : você deve
cumpri-las.
Quanto mais confusa essa interface, mais restrita é sua direção futura. Quanto menos tipos públicos você expuser, mais opções você terá para estender e modificar qualquer implementação no futuro.
E, estamos conversados...
"Sede unânimes
entre vós; não ambicioneis coisas altas, mas acomodai-vos às humildes; não
sejais sábios em vós mesmos;"
Romanos 12:16
Referências:
VB .NET - Lendo Arquivos Binários - BinaryReader - Macoratti ...
Tratamento de arquivos - Questões práticas - Macoratti.net
C# - Copiando Arquivos - Macoratti
C# - Localizando linhas em um arquivo texto - Macoratti.net
VB .NET - Armazenando um objeto serializado em um arquivo
C# - Programa para Loja de Instrumentos Musicais - Macoratti ...
C# - Lendo e gravando dados binários no SQL ... - Macoratti