Você se pergunta: "Como escrever de forma limpa e clara?" Como descobrir rapidamente o código de outra pessoa? "
Lembre-se das regras abaixo e aplique-as!
O artigo não discute as regras básicas para nomear variáveis e funções, indentação sintática e um tópico de refatoração em larga escala. Consideramos 5 regras simples para simplificar o código e reduzir a carga no cérebro durante o processo de desenvolvimento.
Considere o processo de percepção de dados para correlacionar as regras descritas com o processo de percepção e determine os critérios para um código simples.
O processo simplificado de percepção consiste nas seguintes etapas:
- Os dados recebidos através dos receptores são consistentes com a experiência anterior.
- Se não houver correlação, isso é ruído. O barulho é rapidamente esquecido. Se há algo a que se relacionar, o fato é reconhecido.
- Se o fato for importante - lembramos, generalizamos ou agimos, por exemplo, dizemos ou digitamos o código.
- Para reduzir a quantidade de informações memorizadas e analisadas, a generalização é usada.
- Após resumir, as informações são novamente correlacionadas e analisadas (etapa 1).
Os cientistas dividem a memória em curto e longo prazo. A memória de curto prazo é pequena em volume, mas as informações são extraídas e armazenadas instantaneamente. Memória de curto prazo é o cache do cérebro. Pode armazenar 7 + -2 palavras, números, objetos. A memória de longo prazo é maior em volume, mas requer mais energia (esforço) para salvar e recuperar informações do que a memória de curto prazo.
Conclusões:
- quanto menos elementos, menos energia é gasta,
- necessário reduzir o número de elementos percebidos para 9,
- use a memória de longo prazo o menos possível: generalize ou esqueça.
Prosseguimos com a descrição das regras.
Regra 1. Usamos a declaração nas condições, livramos-nos do "não".A remoção do operador “not” reduz o número de elementos que estão sendo analisados. Além disso, em muitas linguagens de programação, um "!" É usado para o operador de negação. É fácil pular esse caractere ao ler o código.
Compare:
if (!entity.IsImportAvaible) {
Depois:
if (entity.IsImportAvaible) {
Regra 2. Reduzindo o nível de aninhamento.Durante a análise de código, cada nível de aninhamento precisa ser mantido na memória. Reduza o número de níveis - reduza o custo do combustível.
Considere maneiras de reduzir o número de níveis de aninhamento.
1) Controle de retorno. Cortamos alguns dos casos e focamos nos demais.
if (conributor != null) {
Converter em
if(contributor == null) { return; }
ou
while( 1) { if( 2) { break; }
ou
for(; 1;) { if( 2) { continue; }
Um exemplo estendido é dado na regra 5. Observe o lançamento de uma exceção.
2) Isolamento do método. O nome da função é o resultado da generalização.
if( ) { foreach(var sender in senders) { sender.Send(message); } }
em
if( ) { SendAll(senders, message); } void SendAll(IEnumerable<Sender> senders, string message) { foreach(var sender in senders) { sender.Send(message); } }
3) condições de combinação.
if (contributor == null) { if (accessMngr == null) {
em
if (contributor == null && accessMngr == null) {
4) Removendo variáveis e dividindo em blocos semânticos. Como resultado, os blocos podem ser alterados independentemente e, o mais importante, é muito mais fácil ler e entender esse código. Um bloco - uma funcionalidade. Um caso comum é uma pesquisa com processamento. É necessário dividir o código em blocos semânticos separados: pesquisa de elementos, processamento.
for (int i=0;i<array.length;i++) { if (array[i] == findItem) {
em
for(int i=0;i<array.length;i++) { if(array[i] == findItem) { foundItem =array[i]; break; } } if (foundItem != null) {
Regra 3. Nos livramos de indexadores e chamadas através de propriedades.Indexador - a operação de acessar um elemento de uma matriz no índice arr [index].
No processo de percepção do código, o cérebro opera com os símbolos [] como delimitadores e o indexador como uma expressão.
function updateActiveColumnsSetting(fieldsGroups) { var result = {}; for (var i = 0; i < fieldsGroups.length; i++) { var fields = fieldsGroups[i].fields; for (var j = 0; j < fields.length; j++) { if (!result[fieldsGroups[i].groupName]) { result[fieldsGroups[j].groupName] = {}; } result[fieldsGroups[i].groupName][fields[j].field] = createColumnAttributes(j, fields[j].isActive); } } return JSON.stringify(result); }
Um indexador é um local de erros frequentes. Em virtude de nomes curtos de índice, é muito fácil misturar I, j ou k.
No exemplo acima, no resultado da linha [fieldsGroups [j] .groupName] = {}; erro cometido:
j é usado em vez de i.
Para descobrir onde exatamente o i-ésimo valor é usado, você deve:
1) destacar visualmente uma variável de matriz
function updateActiveColumnsSetting(fieldsGroups) { var result = {}; for (var i = 0; i < fieldsGroups.length; i++) { var fields = fieldsGroups[i].fields; for (var j = 0; j < fields.length; j++) { if (!result[fieldsGroups[i].groupName]) { result[fieldsGroups[j].groupName] = {}; } result[fieldsGroups[i].groupName][fields[j].field] = createColumnAttributes(j, fields[j].isActive); } } return JSON.stringify(result); }
2) analisar cada ocorrência para o uso do indexador desejado I, j, i-1, j-1, etc., tendo em vista os locais de uso dos indexadores e referências de percepção já identificadas.
Ao destacar o indexador em uma variável, reduzimos o número de locais perigosos e podemos usar facilmente o cérebro para perceber a variável, sem a necessidade de memorização.
Após o processamento:
function updateActiveColumnsSetting(fieldsGroups) { var columnsGroups = {}; for (var i = 0; i < fieldsGroups.length; i++) { var fieldsGroup = fieldsGroups[i]; var groupName = fieldsGroup.groupName; var columnsGroup = columnsGroups[groupName]; if (!columnsGroup) { columnsGroup = columnsGroups[groupName] = {}; } var fields = fieldsGroup.fields; for (var j = 0; j < fields.length; j++) { var fieldInfo = fields[j]; columnsGroup[fieldInfo.field] = createColumnAttributes(j, field.isActive); } } return columnsGroups; }
A seleção visual é muito mais simples, e os ambientes e editores de desenvolvimento modernos ajudam destacando substrings em diferentes partes do código.
function updateActiveColumnsSetting(fieldsGroups) { var columnsGroups = {}; for (var i = 0; i < fieldsGroups.length; i++) { var fieldsGroup = fieldsGroups[i]; var groupName = fieldsGroup.groupName; var columnsGroup = columnsGroups[groupName]; if (!columnsGroup) { columnsGroup = columnsGroups[groupName] = {}; } var fields = fieldsGroup.fields; for (var j = 0; j < fields.length; j++) { var fieldInfo = fields[j]; columnsGroup[fieldInfo.field] = createColumnAttributes(j, field.isActive); } } return columnsGroups; }
Regra 4. Agrupe os blocos de acordo com o significado.Usamos o efeito psicológico da percepção - "O efeito da proximidade": figuras estreitamente espaçadas na percepção são combinadas. Para preparar o código para análise e generalização no processo de percepção e reduzir a quantidade de informações armazenadas na memória, você pode organizar uma linha uma ao lado da outra, unidas por significado ou por funcionalidade, dividindo-as por uma linha vazia.
Para:
foreach(var abcFactInfo in abcFactInfos) { var currentFact = abcInfoManager.GetFact(abcFactInfo); var percentage = GetPercentage(summaryFact, currentFact); abcInfoManager.SetPercentage(abcFactInfo, percentage); accumPercentage += percentage; abcInfoManager.SetAccumulatedPercentage(abcFactInfo, accumPercentage); var category = GetAbcCategory(accumPercentage, categoryDictionary); abcInfoManager.SetCategory(abcFactInfo, category); }
Depois:
foreach (var abcFactInfo in abcFactInfos) { var currentFact = abcInfoManager.GetFact (abcFactInfo); var percentage = GetPercentage(summaryFact, currentFact); accumPercentage += percentage; var category = GetAbcCategory(accumPercentage, categoryDictionary); abcInfoManager.SetPercentage(abcFactInfo, percentage); abcInfoManager.SetAccumulatedPercentage(abcFactInfo, accumPercentage); abcInfoManager.SetCategory(abcFactInfo, category); }
No exemplo superior, existem 7 blocos, nos 3 inferiores: obtendo valores, acumulando em um loop, definindo propriedades do gerenciador.
O recuo é bom para identificar lugares que merecem atenção. Então, as linhas
accumPercentage += percentage; var category = GetAbcCategory(accumPercentage, categoryDictionary);
além de depender dos cálculos anteriores, eles acumulam valores na variável acumulatedPercentage. Para enfatizar as diferenças, o código é recuado.
Um caso de uso específico para uma regra é declarar variáveis locais o mais próximo possível do local de uso.
Regra 5. Seguindo o princípio da exclusividade de responsabilidade.Este é o primeiro dos princípios do SOLID no OOP, mas, além das classes, pode ser aplicado a projetos, módulos, funções, blocos de código, equipes de projeto ou desenvolvedores individuais. Quando perguntado quem ou o que é responsável por essa área, fica imediatamente claro quem ou o quê. Uma conexão é sempre simples. Cada classe é generalizada para um conceito, frase, metáfora. Como resultado, menos para lembrar, o processo de percepção é mais fácil e mais eficiente.
Em conclusão, um exemplo abrangente:
private PartnerState GetPartnerStateForUpdate( PartnerActivityInfo partner, Dictionary<int, PartnerState> currentStates, Dictionary<int, PartnerState> prevStates) { PartnerState updatingState; if (prevStates.ContainsKey(partner.id)) { if (currentStates.ContainsKey(partner.id)) { var prevState = prevStates[partner.id]; updatingState = currentStates[partner.id]; // 1 } else { // 2 } } else if (currentStates.ContainsKey(partner.id)) { updatingState = currentStates[partner.id]; } else { throw new Exception(string.Format(" {0} {1}", partner.id, month)); } return updatingState; }
Depois de substituir os indexadores ContainsKe, inverter a ramificação, selecionar o método e reduzir os níveis de aninhamento, descobriu-se:
private PartnerState GetPartnerStateForUpdate( PartnerActivityInfo partner, Dictionary<int, PartnerState> currentStates, Dictionary<int, PartnerState> prevStates) { PartnerState currentState = null; PartnerState prevState = null; prevStates.TryGetValue(partner.id, out prevState); currentStates.TryGetValue(partner.id, out currentState); currentState = CombineStates(currentState, prevState); return currentState; } private PartnerState CombineStates( PartnerState currentState, PartnerState prevState) { if (currentState == null && prevState == null) { throw new Exception(string.Format( " {0} {1}" , partner.id, month)); } if (currentState == null) { // 1 } else if (prevState != null) { // } return currentState; }
A primeira função é responsável por obter estados dos dicionários, a segunda por combiná-los em um novo.
As regras apresentadas são simples, mas combinadas, elas fornecem uma ferramenta poderosa para simplificar o código e reduzir a carga de memória durante o processo de desenvolvimento. Você nem sempre será capaz de segui-los, mas se perceber que não entende o código, é mais fácil começar com eles. Eles ajudaram repetidamente o autor nos casos mais difíceis.