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
- Os objetos
- Ações e propriedades
- 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: PerformApplication
→ Apply
; DoDelete
→ Delete
; PerformPurchase
→ Purchase
, 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 pé ; 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.