Namespaces C++ aninhados, ao contrário da crença popular, não são maus.

Boas notícias todos! ATUALIZAÇÃO

A proposta foi apresentada para C ++ 17 (ou C ++ 1z) para uma declaração namespaces melhor aninhada! Leia mais

Eu estava navegando ao longo StackOverflow e encontrei uma grande questão sobre namespaces aninhados em C++. Eu sempre fui um grande usuário de namespaces aninhados, tive projetos que cresceram para mais de 5 namespaces aninhados.

O que realmente me chamou a atenção foi Jimmy J resposta, no qual ele diz:

Você é o excesso de usá-los (e você vai ter nada em troca).

Desculpe, o quê? Nada em troca? Sério? Bem, eu não acho que isso é ainda perto da verdade.

Namespaces podem ser um instrumento de documentação

Vamos dizer que você quer fazer algumas consultas no banco de dados. Você poderia simplesmente digitar MyProject::Database:: e conclusão de código já iria dar-lhe sugestões como:

  • MyProject::Database::DatabaseService
  • MyProject::Database::Query
  • MyProject::Database::ResultSet

Agora, você sabe exatamente ** ** o que as classes pertencem ao domínio de banco de dados e você sabe todos eles.

Alternativamente, você pode prefixar aulas com uma etiqueta de "banco de dados", como:

  • MyProject :: DatabaseService
  • MyProject :: DatabaseQuery
  • MyProject :: DatabaseResultSet

Não há problema em fazer isso (e eu, eu mesmo, fazer isso algumas vezes), mas há problemas com isso. Os seus nomes de classe tornam-se maiores (namespaces faz somente seus qualificadores maior) e using e using namespace se tornar complicado de usar. Digamos que você queria apelido MyProject::DatabaseResultSet como ResultSet e MyProject::DatabaseQuery como Query. Você sabe que tem que declarar duas definições:

using ResultSet = MyProject::DatabaseResultSet;
using Query     = MyProject::DatabaseQuery;

em vez de usar namespaces aninhados

using namespace MyProject::Database;

Mais tarde, você pode até mesmo implementar drivers banco de dados dentro namespaces MyProject::Database::MySQL, MyProject::Database::SQLite, em vez de prefixo nomes de classe como MyProject::DatabaseMySQLQuery você namespaces ninho como MyProject::Database::MySQL::Query e você pode alias-los como você quer, e mais importante, com uma única linha.

Ainda mais, se você tem enums você pode usar mais simples e mais curto (ainda significativo!) Nomes. C++11 de enum class *resolve alguns destes problemas, vale dizer, com um namespace * aninhada para o enum.

enum class Color {
    RED, GREEN, BLUE
}
myObject.setColor(Color::RED);

em oposição ao antigo

enum Color {
    COLOR_RED, COLOR_GREEN, COLOR_BLUE
}
myObject.setColor(COLOR_RED);

Mais uma vez, o compilador a IDE sabe que cor deve ser e pode autocomplete (ou fornecer mensagens de erro) que ajudá-lo muito.

As coisas só pioram quando o projeto cresce. Eu não vejo isso "dar nada em troca".

Namespaces mantém os módulos do projeto bem definido

Antes de eu começar aqui, eu sei que você pode construir uma aplicação bem projetada e modular sem namespaces aninhados, o argumento aqui é: é melhor usá-los do que não.

Olhe para o Boost bibliotecas, eles usam namespaces em toda parte! Em primeiro lugar, há uma namespace boost que envolve todas as bibliotecas. Em seguida, há um espaço para nome da biblioteca como boost::asio facilmente chegar tão profundo como boost::asio::ip::tcp. Isso é apenas bonito. Você sabe imediatamente que você está usando TCP / IP. Com um pouco de pensamento você pode até mesmo deduzir boost::asio::ip::udp deve ser UDP / IP. Bonito, bonito.

Agora, pense em um servidor de jogo, em que você tem basicamente 6 grandes módulos: Modelo, Mundo, a rede, banco de dados e eventos. (Nota: Eu tenho usado uma arquitetura semelhante para o meu servidor l2jserver2, em que o mundo, a rede, o banco de dados e os eventos são construidos em cima de um arquitetura orientada a serviços).

O Event módulo envia eventos que são criados por WorldService. Os eventos são capturados NetworkService e envia os eventos relevantes de volta para ao cliente do jogo.

Namespaces aninhados pode ajudá-lo a manter esta distinção em vigor: você não deve usar o GameServer::WorldObject dentro de seu código de rede. Nem você deve usar GameServer::Database dentro da sua rede ou evento módulos. Você sabe que se você tem qualquer dessas declarações dentro do seu código, você quebrou a regra.

O mal

O comitê de padronização do C++ fez realmente difícil de usar namespaces aninhados

namespace Namespace1 {
    namespace Namespace2 {
        namespace Namespace3 {
            // ...
        }
    }
}

Isso pode ser difícil de configurar em cada arquivo de cabeçalho que você precisa namespaces aninhados. Eu só desejo que a norma permitiria coisas como Namespace1::Namespace2::Namespace3 namespaces dentro declarações.

namespace Namespace1::Namespace2::Namespace3 {
    // ...
}

Existem várias opiniões sobre a forma de resolver isto:

  1. Pular o recuo
  2. Use o proprocessador para criar macros, como NAMESPACE1_NAMESPACE2_NAMESPACE3
  3. Não fazer nada sobre isso

Solução #1 é ok, mas o Xcode (que é o que eu uso IDE) tem problemas com identação, se eu copiar e colar um código, ele irá colar com identação adequada, o que não é o que eu quero. Solução #2 é o pior, ele pode criar problemas sutis e você ainda tem que declarar os namespaces dentro de um #define, que é um trabalho tedioso. Isso deixa solução #3 como a que eu escolher - não é rápido, mas é sólido. Cada programador C++ vai saber o que foi feito.

Essa dificuldade vem de seu único propósito inicial: evitar conflitos. Eu não acredito que isso deve ser ignorado por mais tempo - namespaces aninhados são extremamente úteis e pode ajudar a evitar complexidades ou complicações no design do software.


Afora essas pequenas desvantagens para o aninhamento de namespaces em C++, eu não vejo nenhuma razão forte para não usá-los: eles mantêm o seu código organizado, modularizado, e mais importante, simples. Lembre-se que namespaces aninhados ter um efeito colateral surpreendente: eles melhoram a documentação do código, é tipo como de alguns "auto-documenter", no qual as pessoas não têm de ler páginas e páginas de documentação para encontrar o que classes e métodos que eles devem usar - mas hey, não se atreva a parar de escrever documentação!