O código está vivo e morto. Parte Dois Ações e propriedades

Na última vez, escrevi que os nomes dos objetos são de grande importância e que precisam ser selecionados com cuidado e atenção aos detalhes. O mau nome assusta e não permite entender a essência do que está acontecendo. Mas qual é a essência disso?


É difícil avaliar um herói sem entender suas "estatísticas" e "habilidades" . O que ele pode e o que é capaz é o próximo nível de dificuldade em que temos que mergulhar. Não basta refletir o santuário interno do objeto com a ajuda de um nome exato, você também precisa garantir que ele ainda seja um santuário , e não estábulos de quem recebe.


Sobre isso - no artigo.


Sumário do ciclo


  1. Os objetos
  2. Ações e propriedades
  3. Código como texto

Acções


O personagem ataca, defende, esquiva, atira em um arco, usa feitiços, acena uma lâmina. O nome reflete o objeto, mas o próprio objeto está em movimento, em reação, em ação. Caso contrário, falaríamos sobre tabelas no Excel.


Em C #, ações são métodos e funções. E para nós: verbos, átomos de movimento verbal. Os verbos movem o tempo, por causa deles os objetos existem e interagem. Onde há uma mudança - deve haver um verbo.


Setters


De todas as alterações, a atribuição é, no mínimo, móvel. Descreve estritamente e matematicamente o que são quantidades e o que são iguais, mas nunca comunica vida e vigor a um texto, como os verbos.


Por exemplo, há IPullRequest com a propriedade Status , que pode ser Approved , Declined ou Merged . Você pode escrever pullRequest.Status = Status.Declined , mas é o mesmo que dizer "Definir a solicitação de pool para o status cancelado" é imperativo. Muito mais forte é pullRequest.Decline() e, consequentemente, pullRequest.Approve() , pullRequest.Merge() .


Um verbo ativo é preferível a um levantador, mas nem todos os verbos são.


Voz passiva


PerformPurchase , DoDelete , MakeCall .


Como no HeroManager um substantivo importante é obscurecido por um Manager sem sentido, assim como em PerformMigration - Perform . Afinal, mais vivo - basta Migrate !


Os verbos ativos atualizam o texto: não "hit" , mas "hit" ; não "fez um balanço" , mas "acenou" ; não "tomou uma decisão" , mas "decidiu" . Assim, no código: PerformApplicationApply ; DoDeleteDelete ; PerformPurchasePurchase , Buy . (Mas DealDamage se acalmou, embora em casos raros o Attack possa ser considerado .)


Evitando a voz passiva, desenvolvemos a história, movemos os personagens, mas também precisamos garantir que o filme não saia em preto e branco.


Verbos Fortes


Algumas palavras transmitem tons de significado melhor que outras. Se você escrever "ele bebeu um copo de água" , será simples e claro. Mas "bebeu um copo de água" - figurativamente, mais forte.


Portanto, a mudança de saúde do jogador pode ser expressa por player.Health = X ou player.SetHealth , mas o mais pitoresco é player.RestoreHealth .


Ou, por exemplo, conhecemos Stack não por Add/Remove , mas por Push/Pop .


Verbos fortes e ativos saturam o objeto com comportamento, se não forem muito específicos.


Peças redundantes


Assim como no ManualResetEvent , quanto mais nos aproximamos dos internos técnicos do .NET, que são complexos e seria bom expressá-los, mais ricos são os detalhes e excessos da API.


Acontece que você precisa fazer algum trabalho em outro encadeamento, mas para não se preocupar em criar e pará-lo. Em C # existe o ThreadPool para isso. Somente aqui está um simples "fazendo o trabalho" aqui - QueueUserWorkItem ! Que tipo de item de trabalho ( WorkItem ) é e o que pode ser, se não for usuário ( User ), não está claro. Muito mais fácil seria - ThreadPool.Run ou ThreadPool.Execute .


Outro exemplo Lembrar e saber que existe uma instrução de comparação e troca atômica (CAS) é bom, mas transportá-la para o código não é a melhor solução. Interlocked.CompareExchange(ref x, newX, oldX) é inferior às Atomically.Change(ref x, from: oldX, to: newX) (usando parâmetros nomeados).


O código não é um doutorado em trabalhar com um computador quântico, não é um aplicativo para cálculos matemáticos, mas o leitor às vezes é completamente indiferente ao que as instruções de baixo nível são chamadas. O uso diário é importante.


Repetições


UsersRepository.AddUser , Benchmark.ExecuteBenchmark , AppInitializer.Initialize , UniversalMarshaller.Marshal , Logger.LogError .


Como eu disse na última parte, a repetição corrói o significado, comprime o espaço.


Não UsersRepository.AddUser , mas UsersRepository.Add ; não Directory.CreateDirectory , mas Directory.Create ; não HttpWebResponse.GetResponseStream , mas HttpWebResponse.Stream ; não Logger.LogError , mas Log.Error .


Ninhada fina


Check é uma palavra multifacetada. CheckHasLongName pode retornar um bool ou lançar uma exceção se o usuário tiver um nome muito longo. Melhor é o bool HasLongName ou void EnsureHasShortName . Eu até conheci o CheckRebootCounter , que ... Em algum lugar por dentro, reiniciei o IIS!


Enumerate - da mesma série. Existe um método Directory.EnumerateDirectories(path) no .NET: por algum motivo, é especificado que as pastas serão listadas, embora path.Directories() Directories.Of(path) ou path.Directories() .


Calc - Calculate é tão frequentemente reduzido, embora pareça mais com depósitos de cálcio.


Proc é outra abreviação sofisticada para Process .


Base , Impl , Internal , Raw - palavras parasitas que indicam a complexidade dos objetos.


Total


Mais uma vez, um leitor atento perceberá que tudo se resume à simplificação, à comparação da fala natural, e as dicas em si se relacionam amplamente não apenas ao código, mas à escrita em geral. Utilizando-os, o desenvolvedor aprimora o código como texto e o próprio texto, buscando uma apresentação transparente e suave, para simplificar.


Agora que descobrimos o movimento e os "efeitos especiais", vamos ver como as relações entre os objetos são descritas.


As propriedades


O personagem tem saúde e mana; os itens estão no carrinho de compras; o sistema solar consiste em planetas. Os objetos não apenas agem desinteressadamente, mas também se relacionam: hierarquicamente (herdeiro do ancestral), composicionalmente (parte inteira), espacialmente (elemento de armazenamento) etc.


Em C #, propriedades e relacionamentos são métodos (geralmente começando com Get ), getters (propriedades com um corpo get específico) e campos. Mas para nós é: acréscimos de palavras que expressam a pertença de um objeto a outro. Por exemplo, um jogador tem saúde - Player.Health , que corresponde quase exatamente à "saúde do jogador" em inglês.


O que está mais confuso hoje são métodos de ação e métodos de propriedade.


Verbo em vez de um substantivo


GetDiscount , CalculateDamage , FetchResult , ComputeFov , CreateMap .


Os acordos são ouvidos em todos os lugares: os métodos devem começar com os verbos. Você raramente vê alguém duvidando: é mesmo assim? Afinal, não pode haver uma diferença significativa entre Player.Health e Player.Health() . Que os registros sejam sintaticamente diferentes, eles significam a mesma coisa.


Suponha que, em IUsersRepository , seja IUsersRepository algum GetUser(int id) . Por que, para representar o usuário, pense em algum tipo de recibo ( Get )? Será mais preciso - User(int id) !


E realmente: não FetchResult() , mas Result() ; não GetResponse() , mas Response() ; não CalculateDamage() , mas Damage() .


Uma conversa DDD fornece um exemplo de um código “bom”: DiscountCalculator com o método CalculateDiscountBy(int customerId) . Além de haver uma repetição simétrica na face - DiscountCalculator.CalculateDiscount , eles também especificaram que o desconto é calculado . E o que mais, pergunta-se, fazer com ela?


Seria mais forte ir da própria entidade - o Discount com o método static decimal Of(Customer customer, Order order) para chamar Discount.Of(customer, order) - é mais simples que _discountCalculator.CalculateDiscountBy(customerId) e corresponde a um único idioma.


Às vezes, omitindo o verbo, perdemos algo, como, digamos, em CreateMap() : uma substituição direta por Map() pode não ser suficiente. Então a melhor solução é NewMap() : novamente, o objeto está na cabeça, não na ação.


O uso de verbos vazios é característico de uma cultura obsoleta e imperativa, onde o algoritmo é primário e está à frente do conceito. Lá você encontrará frequentemente uma "lâmina temperada" do que uma "lâmina endurecida" . Mas o estilo dos livros sobre James Bond não é adequado para descrever a paisagem. Onde não há movimento, não há lugar para o verbo.


Outros


Propriedades e métodos que expressam as relações entre objetos também são objetos; portanto, o mencionado acima se aplica a eles em muitos aspectos.


Por exemplo, repetições nas propriedades: não Thread.CurrentThread , mas Thread.Current ; não Inventory.InventoryItems , mas Inventory.Items , etc.


Total


Palavras simples e compreensíveis não são confusas e, portanto, o código que as consiste também não é confuso. Por escrito, é igualmente importante escrever com facilidade: evite preposições passivas, abundância de advérbios e adjetivos, repetições, pois as ações preferem um verbo a um substantivo. Um exemplo bem conhecido: "Ele acenou com a cabeça, concordando", em vez de "Ele acenou com a cabeça", causa um sorriso, e eu me lembro de QueueUserWorkItem .


O texto do código também é diferente, pois no primeiro caso você será pago se a casa estiver em pé, se afogando nos raios do sol poente ; no segundo - se a casa estiver de ; mas vale a pena lembrar: uma casa deve ficar de pé, e não grudar dos ajudantes.


Nos dois primeiros artigos da série, eu queria mostrar como é importante trabalhar não apenas no algoritmo, mas também na palavra; como os nomes determinam o conteúdo do que é chamado; quão redundante e código complicado demais afasta o leitor.


Junto com isso, bons nomes são apenas notas. Para tocar , eles devem ser escritos e incorporados na música. Vou lhe contar mais no próximo artigo final.

Source: https://habr.com/ru/post/pt447674/


All Articles