Este guia apresenta um conjunto de padrões recomendados para escrever código consistente e de fácil manutenção. Seguindo essas diretrizes, os desenvolvedores podem melhorar a legibilidade do código, reduzir erros e facilitar a colaboração. Um estilo de código padronizado é necessário para garantir que seja fácil de entender e manter pelos desenvolvedores atuais e futuros que trabalham em um projeto.
Este guia apresenta recomendações, mas, em última análise, a escolha é da sua equipe.
1. Padrões de nomenclatura comuns
A nomenclatura é essencial para um código legível e sustentável. Tente ser consistente no estilo de nomenclatura em todo o código.
1.1 O que fazer
IsX: frequentemente usada para nomear variáveis lógicas para fazer uma pergunta (por exemplo, IsEmpty).OnX: uma função sobrecarregável chamada pelo framework.SubscribeX: inscrição no evento de framework chamado "X", geralmente passando uma função "OnX" para ele.MakeC: crie uma instância da classe C sem sobrecarregar o construtor C.CreateC: crie uma instância da classe C, iniciando sua duração lógica.DestroyC: encerre a duração da lógica.C:c: se estiver trabalhando com uma única instância da classe C, não há problema em chamá-la de C.
1.2 O que não fazer
Decore nomes de tipos. Basta chamá-la de
thing, nãothing_typeouthing_class.Decore valores de enumeração. Em vez de
color := enum{COLOR_Red, COLOR_Green}, usecolor := enum{Red, Green}.
2. Nomes
2.1 Tipos usam "caixa_baixa"
Nomes de tipos devem ser sempre caixa_baixa. Isso inclui todos os tipos: structs, classes, typedefs, traits/interfaces, enums, etc.
my_new_type := class2.2 Interfaces são adjetivos
Interfaces devem ser adjetivos sempre que possível, como imprimível, enumerável. Quando adjetivos não parecerem corretos, acrescente _interface ao nome.
my_new_thing_interface := interface2.3 PascalCase em todo o resto
Todos os outros nomes devem ser usar o formato "PascalCase". Módulos, variáveis de membros, parâmetros, métodos e assim por diante.
MyNewVariable:my_new_type = …2.4 Tipos paramétricos
Nomeie tipos paramétricos "t" ou "thing", em que "thing" explica o que o tipo deve representar. Por exemplo:
Send(Payload:payload where payload:type)Você está enviando alguns dados parametrizados,Carga útil, de qualquer tipopayload.Se houver mais de um tipo paramétrico, evite usar letras únicas, como
t,u,gNunca use o sufixo
_t.
3. Formatação
É importante manter-se consistente com a formatação em toda a base do código. Assim, o código fica mais fácil de ler e entender para você e outros desenvolvedores. Escolha um estilo de formatação que funcione para o projeto.
Como exemplo de consistência, você pode escolher um dos seguintes formatos de espaçamento e usá-lo em toda a base do código:
MyVariable : int = 5
MyVariable:int = 53.1 Recuo
Use quatro espaços para o recuo, nunca tabulações.
Os blocos de código devem usar blocos recuados (espaçados) em vez de colchetes (chaves):
Versemy_class := class: Foo():void = Print("Hello World")Exceto ao escrever expressões de linha única como
option{a},my_class{A := b}, etc.
3.2 Espaços
Use espaços ao redor dos operadores, a menos que faça sentido manter o código compacto para o seu contexto. Adicione colchetes para definir explicitamente a ordem das operações.
VerseMyNumber := 4 + (2 * (a + b))Não adicione espaços nem antes nem depois dos colchetes. Várias expressões dentro de colchetes devem ser separadas por um único espaço.
VerseMyEnum := enum{Red, Blue, Green} MyObject:my_class = my_class{X := 1, Y := 2} Vector := vector3{Left := 1000.0, Up := -1000.0, Forward := 0.0} Foo(Num:int, Str:[]char)Mantenha o identificador e o tipo juntos; adicione um espaço ao redor do operador
=de atribuição. Adicione um espaço ao redor de definições de tipos e operadores de inicialização constante (:=).VerseMyVariable:int = 5 MyVariable := 5 my_type := classSiga as mesmas recomendações para colchetes, identificadores e espaçamento de tipos para assinaturas de função.
VerseFoo(X:t where t:subtype(class3)):tuple(t, int) = (X, X.Property) Foo(G(:t):void where t:type):void Const(X:t, :u where t:type, u:type):t = X
3.3 Quebras de linha
Use um formulário espaçado de várias linhas para inserir uma quebra de linha.
O que fazer
VerseMyTransform := transform: Translation := vector3: Left := 100.0 Up := 200.0 Forward := 300.0 Rotation := rotation: Pitch := 0.0 Yaw := 0.0 Roll := 0.0Mais legível e fácil de editar.
O que não fazer
VerseMyTransform := transform{Translation := vector3{Left := 100.0, Up := 200.0, Forward := 300.0}, Rotation := rotation{...}}Difícil de ler em uma única linha.
Defina enumerações em formato multilinha espaçado se elas precisarem de comentários por enumeração ou se você precisar inserir uma quebra de linha.
Verseenum: Red, # Desc1 Blue, # Desc2
3.4 Chaves
Não use colchetes para definições de classes não herdadas.
O que fazer | Verse |
O que não fazer | Verse |
3.5 Evite a notação de ponto-espaço
Evite usar ponto-espaço a notação ". " no lugar de chaves. Ela torna visualmente mais difícil analisar espaços em branco e é uma fonte potencial de confusão.
O que não fazer | Verse |
O que não fazer | Verse |
4. Funções
4.1 Retorno implícito por padrão
Funções retornam o valor de sua última expressão. Use isso como um retorno implícito.
Sqr(X:int):int =
X * X # Implicit returnSe estiver usando qualquer retorno explícito, todos os retornos na função deverão ser explícitos.
4.2 As funções GetX devem ser
Getters ou funções com semântica semelhante que podem não retornar valores válidos devem ser marcadas como <decides><transacts> e retornar um tipo sem opção. O chamador deve lidar com possíveis falhas.
GetX()<decides><transacts>:xUma exceção são as funções que precisam gravar incondicionalmente em um var. A falha reverteria a mutação, então é necessário usar logic ou option para seu tipo de retorno.
4.3 Prefira métodos de extensão a funções de parâmetros únicos
Use métodos de extensão em vez de uma função com um único parâmetro digitado.
Fazer isso ajuda o IntelliSense. Ao digitar MyVector.Normalize() em vez de Normalize(MyVector), pode-se sugerir nomes com cada personagem do nome do método que você digitar.
O que fazer | Verse |
O que não fazer | Verse |
5. Verificações de falha
5.1 Limite a contagem de expressões falíveis de linha única a três
Limite as verificações condicionais/expressões falíveis em uma única linha a um máximo de três.
Verseif (Damage > 10, Player := FindRandomPlayer[], Player.GetFortCharacter[].IsAlive[]): EliminatePlayer(Player)Use a forma de
ifcom parênteses()quando o número de condições for menor que três.
O que fazer | Verse | Mantém o código conciso, mas legível. |
O que não fazer | Verse | Divide desnecessariamente o código em várias linhas sem melhorias de legibilidade. |
Se estiver usando mais de duas palavras para cada expressão, um máximo de duas expressões em uma única linha geralmente é mais legível.
Verseif (Player := FindAlivePlayer[GetPlayspace().GetPlayers()], Team := FindEmptyTeam[GetPlayspace().GetTeamCollection().GetTeams()]): AddPlayerToTeam(Player, Team)Você também pode aplicar a regra como em um contexto de falha em uma única linha. Não use mais de nove palavras. Quando ultrapassar o limite, use o formulário multilinha espaçado.
O que fazer | Verse | A leitura do texto é melhor, e o contexto é compreensível em várias linhas. |
O que não fazer | Verse | O texto é difícil de analisar. |
Avalie se o agrupamento de várias condições falíveis em uma única função
<decides>tornaria o código mais fácil de ler e reutilizar. Observe que, se o código for usado apenas em um local, um comentário e "seção" sem uma função específica pode ser suficiente.Verseif: Player := FindRandomPlayer[] IsAlive[Player] not IsInvulnerable[Player] Character := Player.GetFortCharacter[] Character.GetHealth < 10 then: EliminatePlayer(Player)Pode ser reescrito como:
VerseGetRandomPlayerToEliminate()<decides><transacts>:player= Player := FindRandomPlayer[] IsAlive[Player] not IsInvulnerable[Player] Character := Player.GetFortCharacter[] Character.GetHealth < 10 Player if (Player := GetRandomPlayerToEliminate[]): Eliminate(Player)A mesma diretriz se aplica a expressões em loops
for. Por exemplo:Verseset Lights = for (ActorIndex -> TaggedActor : TaggedActors, LightDevice := customizable_light_device[TaggedActor], ShouldLightBeOn := LightsState[ActorIndex]): Logger.Print("Adding Light at index {ActorIndex} with State:{if (ShouldLightBeOn?) then "On" else "Off"}") if (ShouldLightBeOn?) then LightDevice.TurnOn() else LightDevice.TurnOff() LightDeviceMelhor assim:
Verseset Lights = for: ActorIndex -> TaggedActor : TaggedActors LightDevice := customizable_light_device[TaggedActor] ShouldLightBeOn := LightsState[ActorIndex] do: if (ShouldLightBeOn?) then LightDevice.TurnOn() else LightDevice.TurnOff() LightDevice
5.2 Agrupe expressões de falha dependentes
Quando uma condição em um contexto de falha depende do êxito de um contexto de falha anterior, mantenha as duas condições juntas no mesmo contexto de falha quando possível e siga a diretriz 5.1.
Assim, você melhora a localidade do código, o que simplifica a compreensão lógica e a depuração.
O que fazer | Verse | Condições dependentes ou relacionadas são agrupadas. |
O que fazer | Verse | Condições dependentes ou relacionadas são agrupadas. |
O que não fazer | Verse | A endentação desnecessária pode dificultar o acompanhamento do fluxo. |
O que não fazer | Verse | A endentação desnecessária pode dificultar o acompanhamento do fluxo. |
É aceitável dividir contextos de falha quando você lida com cada falha potencial (ou grupos de falha) separadamente.
if (Player := FindPlayer[]):
if (Player.IsVulnerable[]?):
EliminatePlayer(Player)
else:
Print("Player is invulnerable, can’t eliminate.")
else:
Print("Can’t find player. This is a setup error.")6. Encapsulamento
6.1 Prefira interfaces a classes
Use interfaces em vez de classes onde for razoável. Isso ajuda a reduzir as dependências de implementação e permite que os usuários forneçam implementações que podem ser usadas pela framework.
6.2 Prefira o acesso privado e restrinja o escopo
Os membros da classe devem ser "private" na maioria dos casos.
Os métodos de classe e módulo devem ter o escopo o mais restritivo possível; <internal> ou <private> quando apropriado.
7. Eventos
7.1 Eventos de pós-fixação com manipuladores de eventos e prefixos com "On"
Eventos assináveis ou nomes de listas de delegados devem ser sufixados com "Event", e nomes de manipuladores de eventos devem ser prefixados com "On".
MyDevice.JumpEvent.Subscribe(OnJump)8. Simultaneidade
8.1 Não decorar funções com Async
Evite decorar funções <suspends> com Async ou termos semelhantes.
O que fazer | Verse |
O que não fazer | Verse |
É aceitável adicionar o prefixo Await a uma função <suspends> que espera internamente que algo aconteça.
Isso pode esclarecer como uma API deve ser usada.
AwaitGameEnd()<suspends>:void=
# Setup other things before awaiting game end…
GameEndEvent.Await()
OnBegin()<suspends>:void =
race:
RunGameLoop()
AwaitGameEnd()9. Atributos
9.1 Separe atributos
Coloque os atributos em uma linha separada. O código fica mais legível, especialmente se vários atributos são adicionados ao mesmo identificador.
O que fazer | Verse |
O que não fazer | Verse |
10. Expressões de importação
10.1 Classifique expressões de importação em ordem alfabética
Por exemplo:
using { /EpicGames.com/Temporary/Diagnostics }
using { /EpicGames.com/Temporary/SpatialMath }
using { /EpicGames.com/Temporary/UI }
using { /Fortnite.com/UI }
using { /Verse.org/Simulation }