A escrita de código passa inevitavelmente por criar nomes. Nomes de classes, de métodos e de variáveis. Praticamente todo o código que escrevemos — que não é uma palavra reservada da linguagem — é um nome que tivemos que criar em algum momento, ou o nome que alguém já criou. Porque uma grande parte do código são nomes, é útil e conveniente ter uma convenção de nomenclatura.
Nomes cuidadosamente escolhidos tornam o código mais legível não apenas do ponto de vista da língua, mas também do propósito. Ao ser mais claro o propósito apenas pela leitura do código diminui a necessidade de comentários que expliquem o que o código faz. Pela diminuição de comentários, e clareza do código, o código se torna a sua própria melhor documentação.
O objetivo de uma convenção de nomenclatura é escolher certas regras que, se seguidas, produzem um nome legível, com significado e com uma mesma estrutura. Para escolher entre as regras temos que saber quais princípios elas devem obedecer, pois se criarmos uma regra de nomenclatura contrária a estes princípios, estaremos complicando o processo de criar nomes.
A primeira coisa que um nome deve fazer é identificar. A identificação deve trazer à mente de quem lê uma imagem em particular. No código, o nome deve revelar a intenção por detrás daquele artefato (classe, método, variável, etc). [[1]] [[2]]
O nome deve ser claro, simples e único. Para ser claro não pode haver ambiguidade no significado do nome com um, e apenas um, conceito. Para ser simples o nome deve ser composto do mínimo de palavras possível sem que obscureça o significado. Idealmente deve ser composto de apenas uma palavra. Para ser único o nome deve ser usado apenas para se referir a um único conceito no sistema.
É como usar nomes diferentes para a mesma operação, por exemplo retrieve
, get
, fetch
e find
. Escolha um nome apenas para o conceito. Use sempre o mesmo. Desta forma ao ver um nome parecido, saberá que existe alguma diferença no comportamento. Assim as palavras têm significado e peso.
Para que a leitura seja clara a todos, não é recomendado usar abreviações. Nem sempre será claro a quem lê o significado da abreviação.
Acrónimos podem ser usados e é bem provável que não tenha como evitar os acrónimos mais técnicos como XML ou HTML. Os acrónimos menos conhecidos, ou não conhecidos universalmente, melhor evitar.
Se nomeamos uma coisa é porque gostaríamos de nos referir a ela no futuro apenas usando o seu nome. Gostaríamos também que as outras pessoas soubessem ao que estamos nos referindo, apenas dizendo o nome. Para isto o nome tem que ser pronunciável. Nomes que são conjuntos de letras e nomes como 'a1" e "bc3to" onde somos obrigados a pronunciar as letras e os números um por um, são exemplo de maus nomes.
Outro fator que dificulta a pronuncia é a utilização de prefixos sem significado como começar todas as variáveis internas com underline ou o uso da Notação Hungara, muito famosa nos anos 70 em linguagens com modelos de tipos pouco definidos ou voláteis demais.
Outro fator que pode atrapalhar a comunicação é o uso de nomes com apenas uma letra como a ou b . Esta regra pode ser quebrada se estamos usando as letras i
, j
ou k
como contador, pois pela tradição todos acabam entendendo que se trata de uma variável muda de iteração. Outro ponto onde usar uma só letra é quando estamos resolvendo um problema matemático onde o uso de x
, y
e z
é facilmente reconhecível.
Para facilitar a comunicação os nomes não devem ter segundos significados, ou significados engraçados para a equipa e não devem ser usados sinônimos espertinhos em vez da palavra que seria esperada. Por exemplo um método que faz a aplicação terminar deve se chamar "terminar" e não "daOFora".
Em caso de duvida, ambiguidade ou dificuldade em encontrar um nome, nomes que estão relacionados aos conceitos técnicos usados no design devem ser preferidos. Em outras palavras nomes do domínio da solução devem ser preferidos a nomes do domínio do problema [[1]]. A razão para isto é que quem irá ler o código é um outro programador. Como programador ele terá que entender o propósito da classe apenas conhecendo o seu nome. Se usamos nomes do domínio do problema, o programador pode não estar familiarizado com o problema e portanto não entender o que está sendo feito pelo código.
Esta regra não é aplicável, obviamente, a classes que apenas existem como representações do domínio do problema; especialmente entidades.
Esta regra é útil para poder reaproveitar nomes facilmente usando nomes de padrões de projeto. Por exemplo um ServiceRegistry
é uma classe que segue o padrão Registry e é usada para guardar e recuperar serviços.
Caixa ( em inglês "case") é a forma como letras maiúsculas e minusculas são usadas para construir uma palavra e como as palavras são separadas.
Caixa Alta - Todas as letras são maiúsculas. As palavras são separadas por underline (_
). Exemplo: ACCOUNT_LIMIT
.
Caixa Baixa - Toda as letras são minúsculas. As palavras são separadas por underline (_
). Exemplo: account_limit
. Em inglês esta caixa é chamada de Lower Case ou Snake Case
Pascal - Toda as letras são minúsculas. As iniciais das palavras são maiúsculas. As palavras não são separada. Exemplo: AccountLimit
. Em inglês esta caixa é chamada de Pascal Case em homenagem à linguagem Pascal onde se tornou padrão.
Camel (Camelo) - Toda as letras são minúsculas. As iniciais das palavras são maiúsculas, excepto a primeira. As palavras não são separada. Exemplo: accountLimit
. Em inglês esta caixa é chamada de Camel Case sendo que a palavra parece um camelo com várias bossas.
Um conceito semelhante mas diferente é o de Caixa Elevada ou Caixa Rebaixada. Este é um conceito relativo e se refere a uma transformação. A linguagens têm normalmente métodos no SDK para este tipo de operação: upperCase
e lowerCase
. Repare que não é "upCase" ou "lowCase". Uma operação de upperCase
sempre transforma uma letra minúscula em uma maiúscula. A operação de lowerCase
smpre transforma uma letra maiúscula em um minúscula. Contudo é preciso atenção que esta transformação pode depender da cultura. O que se considera "maiúscula" e "minúscula" depende da cultura. Algumas letras nem têm as duas formas. Algarismos e símbolos não são normalmente transformados por estas operações.
Cada linguagem tem a sua escolha tipográfica de como os nomes de classes, variáveis, atributos e métodos deve ser escrita. Siga sempre a tipografia padrão da linguagem que está usando. O quadro abaixo mostra as caixas preferidas de algumas linguagens.
Linguagem |
Tipos |
Métodos |
Propriedades |
Campos |
Variáveis |
Constantes |
C# |
Pascal Case |
Pascal Case |
Pascal Case |
Camel Case |
Camel Case |
Pascal Case |
Java |
Pascal Case |
Camel Case |
n/a |
Camel Case |
Camel Case |
Caixa Alta |
TypeScript |
Pascal Case |
Camel Case |
n/a |
Camel Case |
Camel Case |
Caixa Alta |
Dart |
Pascal Case |
Camel Case |
n/a |
Camel Case |
Camel Case |
Camel Case |
Algumas linguagens têm limitações de design que obrigam ou sugerem o uso de símbolos agregados. Por exemplo, Dart exige que membros privados da classe tenham nomes começados com underline (_
), mas não é apenas uma tipografia, o underline funciona também como um modificador. Este tipo de decisão da linguagem é infeliz, mas acaba permeando e poluindo a escrita de nomes.
Sempre que possível, prefira o padrão em que as palavras são aglutinadas sem espaços e sem sinais não alfabético. Utilize a caixa das primeira letra das palavras componentes para marcar as palavras como é feito em Pascal Case ou Camel Case
Abreviações não devem ser utilizadas na formação de nenhum nome. Existem algumas exceções como a abreviação Id
que é usada tradicionalmente para abreviar Identification, mas como regra abreviações não devem ser usadas. Nomes devem ser completos e facilmente legíveis, abreviações não cooperam com estes princípios [[5]].
Se quiser mesmo usar algumas abreviações crie um documento e documente quais abreviações são aceitáveis e porquê.
Siglas como HTTP, URL e AJAX (entre outros) não devem, por padrão, ser escritas em maiúsculas. Apenas a primeira letra pode ser maiúscula caso esteja sendo usado Pascal Case, mas por padrão prefira Camel Case. Por exemplo um método assessor que recupere um URL seria getUrl()
[[5]] e não getURL
. A API Java padrão não adota uma politica especifica para estes casos. Por um lado termos a classe HttpServlet
e por outro as classes URL
e URLConnection
demonstrando uma falta ou falha nas regras de nomenclatura. Embora essas diferenças tenham razões históricas, com certeza é um exemplo a não seguir. Código desenhados mais recentemente têm mais tendência a seguir esta regra.
Se a linguagem/plataforma que está usando define regras para como deve ser formatados os nomes de pacotes e os namespaces siga essas regras.
Para os nomes em si, a plataforma de desenvolvimento não força regras especiais, embora seja frequentemente recomendado que os nomes sejam escolhidos tal que o nome completamente qualificado de uma classe seja universalmente único [[4]]. Para alcançar este objetivo é normalmente usado um caminho de domínio invertido como por exemplo world.codedesign
como pacote principal. O pacote principal é seguido do nome do projeto/aplicação. Dentro de pacote da aplicação são criados os pacotes para as partes da aplicação conforme o design utilizado.
Nomes dos pacotes internos da aplicação devem expressar o propósito das classes lá dentro da forma mais simples possível. Por exemplo, um pacote chamado org.enterprise.app.validation
deverá conter classes que auxiliam a validação. Nomes de pacotes não devem ser usados para separar entres camadas ou andares da sua aplicação. Podem ser usados em casos especiais para separar entre nodos como org.enterprise.app.server
e org.enterprise.app.client
A menos que a plataforma exija, nomes de pacotes ou namespaces não devem ser no plural.
Os nomes das classes devem ser substantivos que designam o conceito ou a responsabilidade que a classe ocupa no sistema. Os nomes devem ser específicos o suficiente para estarem relacionados ao problema ou à solução. Nomes genéricos como coleção, dados, informações, controlador ou processador, devem ser evitados por serem ambíguos.
Nomes de classes podem ter prefixo ou sufixos que tentam orientar o programador sobre o nível que a classe ocupa em uma hierarquia ou no design do sistema. Contudo prefixo ou sufixo que não adicionem informação devem ser evitados. Por exemplo, chamar um classe de NotafiscalPOJO
ou NotaFiscalBean
não faz sentido pois já é sabido que essas classes serão beans e POJOs. Especialmente classes que representam entidades do domínio como Cliente, Produto, Usuário, não precisam de qualquer afixo.
Outro prefixo que é inútil é um que identifique a aplicação [[1]]. Isso deve ser feito pelo nome de pacote da classe e não pelo nome da classe em si.
As regras de nomenclatura de interfaces devem seguir os mesmos princípios das regras para classes. A única diferença é que podemos escolher não apenas substantivos, mas também adjetivos e advérbios.
Muitos gostam de prefixar suas interfaces com um I maiúsculo. Embora algumas linguagens (como C#) e projetos (como Eclipse Commons) utilizem esta prática, ela não é recomendada [[1]] por violar explicitamente o encapsulamento fornecendo informação demais. Além disso, se nos arrependemos depois e quisermos mudar para uma classe essa mudança irá provocar uma refatoramento desnecessário do código inteiro e até de código cliente já escrito por outras pessoas.
A nomenclatura para classes abstratas é a mesma que para classes normais e interfaces normalmente adicionada do prefixo Abstract. Este recurso é especialmente importante no desenvolvimento de API de uso geral onde os elementos principais são definidos como interfaces e classes abstratas são implementadas para fornecer implementações padrão para a maior parte dos métodos da interface. O prefixo Abstract não é usado para significar que a classe precisa ser implementada, mas sim para posicionar a classe num patamar superior da hierarquia de classes (logo abaixo de interfaces, e acima de classes comuns). O prefixo Abstract no nome não força que a classe seja abstrata de fato ( marcada com abtract
), contudo, o fato da classe ser marcada com abtract
força que o nome seja prefixado com Abstract.
A nomenclatura para implementações de classes abstratas ou interfaces segue a regra de usar o mesmo nome da classe/interface pai com um prefixo ou sufixo que a distinga como sendo a implementação. É comum utilizar o sufixo "impl" para designar a implementação. Contudo esta não é uma boa prática, pois no momento que queremos uma implementação diferente não mais podemos usar esse prefixo e muito menos podemos usar prefixos como "impl2". Além disso o uso do prefixo "impl" viola a diretiva de não usar abreviações e o principio da Facilidade de Comunicação ao forçar a pronuncia de um afixo abreviado e sem dar informação sobre no que aquela implementação é diferente das outras.
Prefixos e sufixos de implementações devem explicitar a forma em que aquela implementação é especifica e diferente das outras. Normalmente este afixo está relacionado ao algoritmo ou à tecnologia usada internamente naquela implementação. Exemplos clássicos são ArrayList
que é a implementação de List
usando um array, ou TreeSet
que é a implementação de Set
usando um algoritmo de arvore binária.
Quando estamos implementando uma interface ou classe abstrata mas não conseguimos ver à partida várias opções de implementação afixos como Simple , Standard, Default ou Actual são comuns; como em SimpleDateFormat
e DefaultTableModel
.
Nomes de métodos devem ser verbos ( enviar, pagar, …) com a possibilidade de serem compostos com advérbios ou substantivos relativos ao papel dos argumentos ( enviarProposta, pagarImpostos ) Métodos que representem assessores ou modificadores devem seguir a nomenclatura utilizada pelo SDK da linguagem. Em Java, o padrão JavaBeans [[3]] é usado, começando com o prefixo o nome do método com get
(assessor) ou com set
(modificador).
A nomenclatura de construtores normalmente é restricta ao mesmo nome da classe (como em Java e C#) o que significa que se o construtor tiver parâmetros não temos como oferecer um auxiliar semântico para o significado de cada um. Neste caso é conveniente declarar métodos estáticos corretamente nomeados que informem o propósito dos argumentos e deixar os construtores com parâmetros privados seguindo o padrão Static Method Factory.
O nome de uma exceção deve revelar a causa da exceção e deve terminar com o sufixo que representa a sua hierarquia de exceção: Exception
ou Error
. Por exemplo, em ArithmeticException
sabemos que se trata de uma classe derivada de Exception
, e não de Error
, que foi causada por algum problema em uma operação aritmética.
Algumas classes com utilidade especifica têm formas especificas de serem nomeadas. A seguir são listados alguns modelos de nomenclatura para estes conjuntos de classes, baseados nas regras anteriores.
O nome de uma entidade é uma exceção ao principio de Preferência de Domínio Técnico. Nomes de entidades não devem conter sufixo ou prefixos de nenhum tipo, nem mesmo o popular Entity. Nomes de entidades, são exatamente isso, nomes. Substantivos que são recolhidos do domínio do problema e que normalmente guiam o objetivo ou a operação do software.
A nomenclatura do padrão Repository segue o modelo: [Agregado]Repository
Esta nomenclatura segue o principio de Preferência de Domínio Técnico deixando o nome do padrão no fim do nome. O nome é precedido pelo nome do agregado a que este repositório se refere. O agregado é a entidade principal que o repositório trabalha. Ele pode trabalhar também com outras entidades filhas do agregado.
Por exemplo, um OrderRepository
trabalha com o agregado Order
. O Order
contém OrderItem
. O OrderItem
pode estar contido no Order
ou ser procurado diretamente pelo repositório. Não é necessário um OrderItemRepository
.
O padrão Service tem duas peças: a interface do serviço e a implementação do serviço. A nomenclatura para a interface do padrão Service segue o modelo: [Proposito]Service
. Esta nomenclatura segue o principio de Preferência de Domínio Técnico deixando o nome do padrão no fim do nome. O nome é precedido pela descrição do propósito em forma resumida, por exemplo: EmailSendingService
.
O nome da implementação do serviço segue o modelo: [Tecnologia][Proposito]Service
, em que ao nome da interface do serviço é adicionado um prefixo que denota o tipo de tecnologia ou algoritmo que o serviço utiliza para satisfazer a interface, por exemplo: JavaMailEmailSendingService
O padrão DAO , semelhante ao padrão Service tem duas peças: a interface do serviço/estratégia e a implementação. Para DAO associados a entidades, o nome da entidade é adicionado como prefixo para distinguir entre os contratos de cada entidade. O nome da interface segue o modelo [Entidade]DAO
e o da implementação o modelo: [Tecnologia][Entidade]DAO
. Esta nomenclatura segue o principio de Preferência de Domínio Técnico deixando o nome do padrão no fim do nome.
O nome da implementação começa com o nome da tecnologia de persistência que a implementação do DAO usa, seguindo pelo nome da interface que está sendo implementada, por exemplo JdbcCustomerDAO
Muito do conceito de linguagem de programação é baseado na ideia de escrever o mais próximo da linguagem natural possível. Acontece que esta língua é normalmente o inglês. Por isso que as palavras reservadas são em inglês. A ideia é que se misturem bem com o resto dos nomes para prover uma leitura o mais fluida possível. É esta leitura fluida que promove a melhor documentação e o código mais limpo [[2]].
O problema acontece quando a língua natural do programador não é o inglês. Um programador cuja língua natural não é o inglês terá o entendimento do código dificultado à partida pois a leitura não mais é fluida. Mesmos assim, é possível adaptar regras de nomenclatura especificas à língua natural e ter os mesmos benefícios que antes. Todos excepto um: quem não souber ler aquele língua não entenderá o código. O problema não é apenas para quem tem o Português como língua natural, mas para todos aqueles que têm uma língua natural diferente do inglês. Por exemplo, o japonês. Embora as linguagens modernas aceitem código fonte em UTF-16 (permitindo assim a escrita de caracteres japoneses) apenas programadores que saibam ler e dominem o idioma podem entender o código.
Existem ocasiões em que obrigatoriamente teremos que usar o inglês. Afinal, o inglês é a língua franca de fato em programação. Isto acontece sempre que precisamos partilhar o código com outras pessoas em outros países ou culturas. Projetos Open Source são um exemplo. Projetos em outsource são outro. Na realidade existem poucos exemplos de quando o seu código não seria partilhado com pessoas de outros países ou culturas.
Deve estar pensando que a sua aplicação é muito simples, que você vai vendê-la para um só cliente e nunca sairá das fronteiras do seu pais ou do seu controle. Isso pode ser verdade durante a vida inicial do software, mas se ele durar, se houver sucesso, haverá necessidade de dar manutenção, se você quiser ganhar mais dinheiro comercializando o fonte ou se simplesmente quiser fazer outsource , ou open source, do desenvolvimento porque os custos estão muito caros… você não poderá. Escrever código em uma língua natural que não o inglês limita automaticamente a evolução, expansão e vida útil do seu software.
Um detalhe que leva muita gente a não escrever em inglês é achar que certos conceitos são muito particulares da cultura e não podem ser expressos em inglês (como por exemplo o CPF ou o CNPJ). Então, mesmo quando escrevendo o resto do aplicativo em inglês é comum ver classes do domínio com nomes na língua natural do programador. Isto é um problema porque esses nomes não traduzem o conceito, violando diretamente o principio de Expressar Intensão. Ele é o nome dado no domínio do problema a um certo conceito. Porque o programador, não sabe ou não consegue abstrair esse conceito ele não consegue atribuir outro nome além daquele que conhece na sua língua natural. Ao abstrair o conceito, é mais fácil encontrar um equivalente em inglês. Por exemplo, o CPF e o CNPF representam apenas um conceito: o numero de identificação tributário. Que podemos facilmente nomear em inglês como TributaryIdentificationNumber
ou TaxIdentificationNumber
( numero de identificação para pagamento de impostos).
Não é necessário que o nome em inglês seja a tradução direta porque isso pode causar outros problemas relativamente ao domínio. Por exemplo, nos EUA, os impostos são identificados usando o Social Security Number, que é o mesmo numero que a pessoa usa para se identificar em outras agencias do governo, não apenas a que recolhe os impostos e faz, para essa entidade o mesmo papel que o RG. Portanto, tentar a tradução direta é muitas vezes um erro em si mesmo porque não ha uma contraparte na cultura americana/inglesa. Por isso temos que nos concentrar em criar um nome, e não em traduzir.
Por exemplo, boletos bancários são uma prática que não existe fora do Brasil assim como não existem no Brasil a prática de emitir Cashier’s Checks . Boleto bancário é, assim, intraduzível diretamente. Contudo, atendendo ao conceito facilmente podemos criar o nome de PrintableBankCreditTitle
Analisar o nome do conceito na sua língua natural e tentar passá-lo para o inglês o ajudará a criar maior poder de abstração e um maior poder de abstração o poderá ajudar a modelar orientado a objetos de uma forma mais simples e natural. Por outro lado, ao abstrair o conceito você pode ver novas hierarquias de objetos e classes
Ninguém o vai impedir de criar nomes em línguas naturais diferentes do inglês, mas tenha em mente que ao fazer essa escolha estará inevitavelmente restringindo a evolução do software que está escrevendo.
[1] Robert C. Martin Clean Code: A Handbook of Agile Software Craftsmanship: Pearson Education (2009)
[2] Steve McConnell Code Complete: DV-Professional (2004)
[3] JavaBeans Tecnology (http://java.sun.com/products/javabeans/docs/spec.html)
[4] Code Conventions for the JavaTM Programming Language: Naming Conventions (http://java.sun.com/docs/codeconv/html/CodeConventions.doc8.html)
[5] Joshua Bloch Effective Java, 2nd Edition: Fonenix inc (2008)