No mundo RAD (Rapid Application Development) de hoje pouco se discuti sobre a qualidade do código fonte dos nossos softwares.
E se você fizer uma pesquisa no Google encontrará um conteúdo abordando aspectos da ergonomia do código de forma isolada. Nosso intuito com esse artigo é iniciar uma discussão no intuito de identificar e organizar as qualidades qualidades desejadas e como chegar lá...
Após algumas reflexões e analogias com a ergonomia de interfaces chegamos em 6 qualidades que devemos trabalhar:
- Acoplamento: diz respeito a qualidade de baixa dependência;
- Coesão: diz respeito a qualidade de responsabilidade única;
- Flexibilidade: diz respeito a qualidade de extensão e personalização;
- Legibilidade: diz respeito ao esforço necessário para compreensão do código;
- Mensagens de Erros: diz respeito a qualidade das mensagens de erro;
- Presteza: diz respeito a qualidade das informações disponiveis sobre o código para informar e conduzir a utilização do código;
Acoplamento
O acoplamento entre classes ou subsistemas é uma medida da interconexão entre as classes ou subsistemas.
Exemplo de recomendações:
- Evitar duplicação de código;
- Evitar classes muito grandes;
- Evitar métodos muito longos;
- Evitar lista de parâmetro muito longas;
O exemplo abaixo ilustra um módulo de processamento de negócios com acoplamento forte. O primeiro problema é que não podemos reaproveitar a regra de negócio independentemente da estrutura do banco de dados, e segundo, que qualquer alteração na estratégia de acesso a dados , na estrutura de dados ou configuração afetará a lógica de negócio.
public class BusinessLogicClass
{
public void DoSomething()
{
int threshold = int.Parse(ConfigurationManager.AppSettings["threshold"]);
string connectionString = ConfigurationManager.AppSettings["connectionString"];
string sql = @"select * from things size > " + threshold;
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
SqlCommand command = new SqlCommand(sql, connection);
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
string name = reader["Name"].ToString();
string destination = reader["destination"].ToString();
// do some business logic in here
doSomeBusinessLogic(name, destination, connection);
}
}
}
}
}
Coesão
A coesão é a medida que indica se uma classe tem uma função bem definida no sistema.
Um sistema composto por classes e subsistemas coesos é semelhante a um grupo de discussão online bem projetado. Cada área do grupo online é estritamente concentrada em um tópico específico, para que seja fácil acompanhar a discussão e, se você estiver procurando um diálogo sobre determinado assunto, precise visitar apenas uma sala.
Exemplos de recomendações:
- Utilize o principio de separação de conceitos dividindo as funcionalidades da aplicação e minimizando pontos de interação;
- Utilize o principio Least Knowledge (LoD) para evitar que um objeto não conheça os detalhes internos de implementação de outro;
- Utilize o principio Tell, Don’t Ask e evite que seus objetos façam perguntas a um objeto sobre seu estado interno, tome algumas decisões sobre esse estado, e então, diga ao objeto o que fazer;
- Pratique a Lei de Deméter e faça com que a classe fale somente com seus “amigos” mais próximos;
public class Purchase
{
private readonly double _subTotal;
public Purchase(double subTotal)
{
_subTotal = subTotal;
}
public double Total
{
get
{
double discount = _subTotal > 10000 ? .10 : 0;
return _subTotal * (1 - discount);
}
}
}
public class Account
{
private double _balance;
public void Deduct(Purchase purchase, PurchaseMessenger messenger)
{
if (purchase.Total < _balance)
{
_balance -= purchase.Total;
}
else
{
messenger.RejectPurchase(purchase, this);
}
}
}
public class ClassThatObeysTellDontAsk
{
public void MakePurchase(Purchase purchase, Account account)
{
PurchaseMessenger messenger = new PurchaseMessenger();
account.Deduct(purchase, messenger);
}
}
Saiba mais através do artigo Padrões na prática: coesão e acomplamento
Flexibilidade
A flexibilidade do código é a qualidade que promove a reusabilidade e a facilidade de manutenção (reparo de erro e evolução) para responder de forma rápida e confiável as mudanças exigidas pelo dinâmismo do business das empresas.
Exemplos de Recomendações:
- Padronizar a criação de objetos;
- Isolar a complexidade de criação de objetos compostos;
- Fornecer meios de extender o comportamento das classes;
- Fornecer meios de mudar o comportamento das classes;
No exemplo abaixo, temos uma API para importação de dados externos ao sistema através de um conector. Nela isolamos e padronizamos a criação de conectores e fornecemos um meio de extendê-los e ou mudar sua estratégia de leitura das informações externas sem impacto no código.
Você pode fazer o download do projeto clicando aqui.
Legibilidade
A performance e a qualidade na evolução do código leva em conta as características cognitivas e perceptivas dos usuários.
Uma boa legibilidade facilita a leitura do código, a identificação de defeitos, reduz a curva de aprendizado da equipe, promove o reuso e reduz o gap de idioma com a área de negócio.
Exemplos de recomendações:
- Usar nomes claros e explicativos para funções (verbos) e variáveis (substantivos);
- Fazer com que o código reflita suas intenções e decisões;
- Trabalhar a lêxia do código de forma a refletir uma frase natural;
- Utilizar o padrão Pascal Case para nomes de namespaces, classes, métodos, propriedades, eventos e delegates;
- Utilizar o padrão Camel Case para nomes de variáveis;
Um recurso muito útil introduzido na versão 3.0 do Microsoft .Net Framework é o Extension Method, o qual, podemos adicionar comportamento (método) a um determinado tipo sem a necessidade de alterar o código original ou criar tipos derivados.
Vejamos um exemplo:
if (sale.IsValid() && sale.SaleDetailsList.HasAllItemsValid())
{
if(Stock.EvaluateProductsAvailability(sale))
{
using (var trans = new DataTransaction())
{
sale.Insert();
Stock.UpdateStock(sale.GetProductsSold());
sale.SaleDetailsList.Insert();
Invoice.Generate(sale);
Carrier.PlanDeliver(sale);
trans.Complete();
}
}
}
Mensagens de Erros
A qualidade das mensagens favorece o aprendizado do sistema, indicando ao usuário a razão ou a natureza do erro cometido, o que ele fez de errado, o que ele deveria ter feito e o que ele deve fazer.
Exemplos de recomendações:
- Construir mensagem no idioma do usuário;
- Utilizar mensagens de erro tão breves quanto possível;
- Fornecer mensagens de erro orientadas a tarefas;
- Utilizar termos tão específicos quanto possível para as mensagens de erros;
- Adotar um vocabulário neutro, não personalizado, não repreensivo nas mensagens de erro;
- Evitar o humor;
Vamos avaliar isso em um exemplo.
Ao invés de frases com estrutura negativo-negativo, como “A idade mínima não pode ser menor que 18 anos”, procure utilizar linguagem positiva e indicar os valores esperados, “A idade deve ser maior ou igual a 18 anos”.
Forneça na mensagem de erro todas as informações necessárias à compreensão e solução do problema.
Ao invés de “Desconto superior ao máximo permitido (G527)”, procure alternativas como “O valor de desconto (R$50,00) é superior ao máximo permitido para essa transação (R$7,50). Por favor, tente novamente com valor adequado ou solicite aprovação da supervisão”.
Curiosidade: 10 principais, diferentes e engreçadas mensagens de erro da computação
Presteza
Uma boa presteza facilita o aprendizado do usuário em como usar uma classe/método/serviço e diminui a ocorrência de erros.
Exemplos de Recomendações:
- Documentar a responsabilidade da classe;
- Documentar o que seus métodos fazem;
- Documentar para que serve o parâmetro de um método/serviço, o valor padrão e os valores esperados;
- Fornecer um cenário de uso da classe/método/serviço;
Vamos avaliar isso em um exemplo.
Você já usou uma classe de alguém que não se preocupou em escrever aquele cabeçalho do método, ou pior, que escreveu coisas que não ajudam em nada?
Vendo o exemplo acima você deve estar se perguntando: O que a classe XnMSteadyDetector faz? O que o parâmetro “CooldownFrames” controla e qual o range valores válidos? E o parametro “DetectionDuration”? Controla o tempo em segundos, millisegundos, ou outro? Qual é o valor padrão? E o “MaximumAllowedVelocity”?
Alguém pode argumentar “Isso está na documentação”. Perfeito! É o mínimo que tem que ser feito, senão, seria quase impossível usar a classe.
Mas no mundo .Net, onde há o Microsoft Visual Studio, você deveria ir além!
Deveria fornecer informações de forma integrada com o ambiente de desenvolvimento de forma a facilitar a vida da pessoa que está utilizando sua classe/método/serviço.
E isso é simples. Basta:
Para produzir esse resultado:
Fique a vontade para compartilhar suas experiências, visões, sugestões e críticas.
Até a próxima!
Artigos Recomendados:
>> Event-based Asynchronous Pattern (EAP). O futuro padrão para as interfaces das aplicações?
>> Software para Pessoas do Séc XXI
>> Prepare-se para o C# 5 – Parte 2
>> Prepare-se para o C# 5 – Parte 1
>> Reflection de Alta de Performance
>> Extension Methods = Manutenibilidade