Questa guida fornisce un insieme di standard raccomandati per scrivere codice coerente e facile da aggiornare. Seguendo queste linee guida, gli sviluppatori possono migliorare la leggibilità del codice, ridurre gli errori e facilitare la collaborazione con i membri del loro team. Uno stile di scrittura standardizzato è necessario per garantire che il codice sia facile da comprendere e aggiornare per gli sviluppatori attuali e futuri che lavorano a un progetto.
È altresì importante sottolineare che questa guida propone solamente dei consigli. La scelta finale rimane sempre a discrezione del tuo team.
1. Modelli di denominazione standard
L'assegnazione di nomi appropriati è fondamentale per avere un codice leggibile e facile da aggiornare. Cerca di essere sempre coerente con lo stile di denominazione del tuo codice.
1.1 Da fare
IsX: spesso utilizzato per nominare variabili logiche al fine di porre una domanda (ad esempio, IsEmpty).OnX: una funzione sovraccaricabile chiamata dal framework.SubscribeX: sottoscrive l'evento del framework denominato X, spesso passando una funzione OnX.MakeC: crea un'istanza della classe c senza sovraccaricare il costruttore c.CreateC: crea un'istanza della classe c, avviando il ciclo di vita logico.DestroyC: termina il ciclo di vita logico.C:c: se stai utilizzando una singola istanza della classe c, è corretto chiamarla C.
1.2 Non farlo
Sovraccaricare i nomi dei tipi. Chiamali semplicemente
thing, nonthing_typeothing_class.Sovraccaricare i valori di enumerazione. Non utilizzare
color := enum{COLOR_Red, COLOR_Green}, macolor := enum{Red, Green}.
2. Nomi
2.1 Tipi con lower_snake_case
I nomi dei tipi devono essere sempre espressi con la struttura lower_snake_case., ovvero in minuscolo con trattini bassi. Ciò vale per tutti i tipi: strutture, classi, definizioni di tipi, tratti/interfacce, enumerazioni ecc.
my_new_type := class2.2 Interfacce e aggettivi
Le interfacce dovrebbero essere definite con aggettivi, ove possibile, come ad esempio "visualizzabile" o "enumerabile". Quando gli aggettivi non risultano la soluzione più appropriata, puoi aggiungere _interface al sostantivo.
my_new_thing_interface := interface2.3 PascalCase tutto il resto
Tutti gli altri nomi devono presentare il formato PascalCase. Moduli, variabili membro, parametri, metodi e così via.
MyNewVariable:my_new_type = …2.4 Tipi parametrici
Assegna un nome ai tipi parametrici t o thing, dove "thing" spiega cosa deve rappresentare il tipo. Ad esempio:
Send(Payload:payload where payload:type)Stai inviando alcuni dati parametrizzati,Payload, di qualsiasi tipo dipayload.Se esiste più di un tipo parametrico, evita di usare lettere semplici come
t,u,gNon utilizzare mai il suffisso
_t.
3. Formattazione
È importante mantenere una formattazione coerente in tutto il codice. In questo modo sarà più facile per te e per gli altri sviluppatori leggere e comprendere il codice. Scegli uno stile di formattazione adatto al progetto.
Come esempio di coerenza, puoi scegliere uno dei seguenti formati di spaziatura e utilizzarlo in tutto il codice:
MyVariable : int = 5
MyVariable:int = 53.1 Indentazione
Utilizza quattro spazi per ottenere l'indentazione, mai le tabulazioni.
I blocchi di codice dovrebbero essere costituiti da blocchi indentati (spaziati) piuttosto che da parentesi graffe:
Versemy_class := class: Foo():void = Print("Hello World")Tranne per la scrittura di espressioni di una sola riga come
Opzione{a},my_class{A := b}, ecc.
3.2 Spazi
Inserisci spazi tra un operatore e l'altro, a meno che non sia opportuno mantenere il codice compatto per il suo contesto. Aggiungi parentesi per definire esplicitamente l'ordine delle operazioni.
VerseMyNumber := 4 + (2 * (a + b))Non aggiungere spazi all'inizio e alla fine delle parentesi. Più espressioni all'interno delle parentesi devono essere separate da un singolo spazio.
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)Mantieni l'identificatore e il tipo insieme; aggiungi uno spazio intorno all'operatore di assegnazione
=. Aggiungi uno spazio intorno alle definizioni dei tipi e agli operatori di inizializzazione delle costanti (:=).VerseMyVariable:int = 5 MyVariable := 5 my_type := classSegui le stesse raccomandazioni per le parentesi, gli identificatori e la spaziatura dei tipi per le firme delle funzioni.
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 Interruzioni di riga
Utilizza un modulo spaziato e multilinea per inserire un'interruzione di riga.
Da fare
VerseMyTransform := transform: Translation := vector3: Left := 100.0 Up := 200.0 Forward := 300.0 Rotation := rotation: Pitch := 0.0 Yaw := 0.0 Roll := 0.0Più facile da leggere e modificare.
Non fare
VerseMyTransform := transform{Translation := vector3{Left := 100.0, Up := 200.0, Forward := 300.0}, Rotation := rotation{...}}Difficile da leggere su una singola riga.
Definisci le enumerazioni in un formato spaziato e su più righe, se richiedono commenti specifici per ogni enumerazione o se hai bisogno di inserire un'interruzione di riga.
Verseenum: Red, # Desc1 Blue, # Desc2
3.4 Parentesi
Non utilizzare parentesi per definizioni di classe non ereditarie.
Da fare | Verse |
Non fare | Verse |
3.5 Evitare l'uso della notazione a punti e spazi
Evita di utilizzare la notazione a punti ". " al posto delle parentesi graffe. Tale tecnica rende l'analisi della spaziatura visivamente più difficile e può causare confusione.
Non fare | Verse |
Non fare | Verse |
4. Funzioni
4.1 Ritorno implicito predefinito
Le funzioni restituiscono il valore dell'ultima espressione. Utilizzala come ritorno implicito.
Sqr(X:int):int =
X * X # Implicit returnSe si decide di utilizzare i ritorni espliciti, tutti i ritorni della funzione devono essere espliciti.
4.2 Le funzioni GetX devono essere
I getter o le funzioni con una semantica simile che potrebbero non restituire valori validi devono essere contrassegnati con <decides><transacts> e restituire un tipo non opzionale. Il chiamante deve gestire possibili errori.
GetX()<decides><transacts>:xUn'eccezione riguarda le funzioni che devono scrivere incondizionatamente su una var. Dal momento che un errore potrebbe annullare la mutazione, risulta necessario utilizzare logic o option come tipo di ritorno.
4.3 Preferire metodi di estensione a funzioni a parametro singolo
Utilizza metodi di estensione invece di una funzione con un solo parametro tipizzato.
Questa scelta facilita l'uso di Intellisense. Digitando MyVector.Normalize() invece di Normalize(MyVector), può suggerire nomi con ogni carattere del nome del metodo indicato.
Da fare | Verse |
Non fare | Verse |
5. Verifiche di errore
5.1 Limitare a tre il numero di espressioni fallibili su una sola riga
Limita i controlli condizionali e le espressioni fallibili su una singola riga a un massimo di tre.
Verseif (Damage > 10, Player := FindRandomPlayer[], Player.GetFortCharacter[].IsAlive[]): EliminatePlayer(Player)Utilizza la forma
ifcon le parentesi()quando il numero di condizioni è inferiore a tre.
Da fare | Verse | Mantiene il codice conciso ma leggibile. |
Non fare | Verse | Divide inutilmente il codice in più righe senza migliorarne la leggibilità. |
Se ogni espressione richiede più di due parole, spesso è preferibile avere un massimo di due espressioni su una singola riga per garantire una migliore leggibilità.
Verseif (Player := FindAlivePlayer[GetPlayspace().GetPlayers()], Team := FindEmptyTeam[GetPlayspace().GetTeamCollection().GetTeams()]): AddPlayerToTeam(Player, Team)Puoi applicare questa regola anche in un contesto di errore su una sola riga, ma senza superare mai nove parole. Quando superi questo limite, utilizza la forma distanziata e su più righe.
Da fare | Verse | Il testo è più leggibile e il contesto può essere compreso su più righe. |
Non fare | Verse | Il testo è difficile da analizzare. |
Valuta se raggruppare diverse condizioni fallibili in un'unica funzione
<decides>possa rendere il codice più facile da leggere e da riutilizzare. Se il codice viene utilizzato solo in un punto, può essere sufficiente un commento "sezione" senza una funzione ad hoc.Verseif: Player := FindRandomPlayer[] IsAlive[Player] not IsInvulnerable[Player] Character := Player.GetFortCharacter[] Character.GetHealth < 10 then: EliminatePlayer(Player)Può essere riscritto come:
VerseGetRandomPlayerToEliminate()<decides><transacts>:player= Player := FindRandomPlayer[] IsAlive[Player] not IsInvulnerable[Player] Character := Player.GetFortCharacter[] Character.GetHealth < 10 Player if (Player := GetRandomPlayerToEliminate[]): Eliminate(Player)La stessa linea guida si applica alle espressioni nei loop
for. Ad esempio: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() LightDeviceMeglio come:
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 Raggruppare le espressioni di errore dipendenti
Quando una condizione in un contesto di errore dipende dall'esito positivo di un precedente contesto di errore, ove possibile, raggruppa le due condizioni nello stesso contesto di errore e segui le linee guida della sezione 5.1.
Questa soluzione migliora la localizzazione del codice, rendendolo più facile da comprendere logicamente e per il debugging.
Da fare | Verse | Le condizioni dipendenti o collegate sono raggruppate insieme. |
Da fare | Verse | Le condizioni dipendenti o collegate sono raggruppate insieme. |
Non fare | Verse | Un'indentazione non necessaria può rendere il flusso più difficile da seguire. |
Non fare | Verse | Un'indentazione non necessaria può rendere il flusso più difficile da seguire. |
Puoi anche dividere i contesti di errore, a patto di trattare ogni potenziale errore (o gruppo di errore) separatamente.
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. Incapsulamento
6.1 Preferire interfacce a classi
Utilizza le interfacce al posto delle classi, ove possibile. Questa scelta consente di ridurre le dipendenze di implementazione e consente agli utenti di fornire implementazioni che possono essere utilizzate dal framework.
6.2 Preferire accesso privato e ambito delimitato
Nella maggior parte dei casi, i membri della classe devono essere "privati".
I metodi delle classi e dei moduli devono essere definiti nel modo più restrittivo possibile (con <internal> o <private>, ove appropriato).
7. Eventi
7.1 Eventi con il suffisso Event e gestori con il prefisso On
I nomi degli eventi sottoscrivibili o degli elenchi di delegati devono avere il suffisso 'Event' mentre i nomi dei gestori di eventi devono avere il prefisso 'On'.
MyDevice.JumpEvent.Subscribe(OnJump)8. Concorrenza
8.1 Non aggiungere a funzioni l'elemento async
Evita di sovraccaricare le funzioni <suspends> con Async o termini simili.
Da fare | Verse |
Non fare | Verse |
Puoi aggiungere il prefisso Await a una funzione <suspends> che internamente attende che accada qualcosa.
Ciò può aiutare a chiarire come deve essere utilizzata un'API.
AwaitGameEnd()<suspends>:void=
# Setup other things before awaiting game end…
GameEndEvent.Await()
OnBegin()<suspends>:void =
race:
RunGameLoop()
AwaitGameEnd()9. Attributi
9.1 Separare gli attributi
Inserisci gli attributi in una riga separata. Questa scelta rende il codice più leggibile, specialmente se più attributi vengono aggiunti a uno stesso identificatore.
Da fare | Verse |
Non fare | Verse |
10. Importa espressioni
10.1 Classificare le espressioni di importazione in ordine alfabetico
Ad esempio:
using { /EpicGames.com/Temporary/Diagnostics }
using { /EpicGames.com/Temporary/SpatialMath }
using { /EpicGames.com/Temporary/UI }
using { /Fortnite.com/UI }
using { /Verse.org/Simulation }