Para quem
Este artigo é direcionado para aqueles que não encontraram o idioma padrão de modelo curiosamente recorrente (CRTP), mas têm uma ideia do que os modelos estão em C ++. Você não precisará de conhecimento específico ou conhecimento firme de programação em modelos para entender o artigo.
Deixe-nos resolver este problema:
Um arquivo vem da rede em um dos formatos: json ou xml e queremos analisá-los e obter algumas informações. A solução se sugere - usar o padrão de ponte para separar a interface do analisador e suas duas implementações, uma por formato de arquivo. Assim, após determinar o formato do arquivo, podemos passar a implementação de que precisamos na forma de um ponteiro para a função de análise.
Exemplo esquemático
// , Parser
// ,
ParsedDataType parseData(Parser* parser, FileType file);
int main() {
FileType file = readFile();
Parser* impl = nullptr;
if (file.type() == JsonFile)
impl = new ParserJsonImpl();
else
impl = new ParserXmlImpl();
ParsedDataType parsedData = parserData(impl, file);
}
Essa abordagem clássica tem várias desvantagens :
A interface do Parser deve ter funções virtuais e, como sabemos, é caro ir para a tabela de métodos virtuais.
A interface de função não é tão descritiva quanto gostaríamos que fosse quando comparada, por exemplo, a linguagens funcionais com sistemas de tipo rico.
( , ).
C++
CRTP - , , , .
- -, , , , .
template <typename Implementation>
struct ParserInterface {
ParsedData getData() {
return impl()->getDataImpl();
}
ParsedID getID() {
return impl()->getIDImpl();
}
private:
Implementation* impl() {
return static_cast<Implementation*>(this);
}
};
, , Implementation* impl()
.
-, . , -, .
struct ParserJsonImpl : public ParserInterface<ParserJsonImpl> {
friend class ParserInterface;
private:
ParsedData getDataImpl() {
std::cout << "ParserJsonImpl::getData()\n";
return ParsedData();
}
ParsedID getIDImpl() {
std::cout << "ParserJsonImpl::getID()\n";
return ParsedID;
}
};
struct ParserXmlImpl : public ParserInterface<ParserXmlImpl> {
friend class ParserInterface;
private:
ParsedData getDataImpl() {
std::cout << "ParserXmlImpl::getData()\n";
return ParsedData();
}
ParsedID getIDImpl() {
std::cout << "ParserXmlImpl::getID()\n";
return ParsedID();
}
};
, . , , ParserInterface<A>
ParserInterface<B>
. . , , - , static_cast<>()
Implementation* impl()
. , . .
:
, - .
, - , private.
, -, friend.
, , .
template <typename Impl>
std::pair<ParsedData, parsedID> parseFile(ParserInterface<Impl> parser) {
return std::make_pair(parser.getData(), parser.getID());
}
, . ParserInterface parser
. , static_cast
, , .
:
int main() {
ParserJsonImpl jsonParser;
parseFile(jsonParser);
ParserXmlImpl xmlParser;
parseFile(xmlParser);
return 0;
}
.
ParserJsonImpl::getData() ParserJsonImpl::getID() ParserXmlImpl::getData() ParserXmlImpl::getID()
, , , . , static_cast
. . , :
. , , .
: , , , .
Essa abordagem também é usada para o idioma de classes MixIn, que "mistura" seu comportamento com classes herdadas. Uma dessas classes - std::enable_shared_from_this
- combina funcionalidades para obter um ponteiro shared_ptr
para si mesma.
Este artigo fornece o exemplo mais simples para se familiarizar com o tópico, ainda mais - mais.
Lista completa de código de trabalho
#include <iostream>
template <typename Implementation>
struct ParserInterface {
int getData() {
return impl()->getDataImpl();
}
int getID() {
return impl()->getIDImpl();
}
private:
Implementation* impl() {
return static_cast<Implementation*>(this);
}
};
struct ParserJsonImpl : public ParserInterface<ParserJsonImpl> {
friend class ParserInterface<ParserJsonImpl>;
private:
int getDataImpl() {
std::cout << "ParserJsonImpl::getData()\n";
return 0;
}
int getIDImpl() {
std::cout << "ParserJsonImpl::getID()\n";
return 0;
}
};
struct ParserXmlImpl : public ParserInterface<ParserXmlImpl> {
int getDataImpl() {
std::cout << "ParserXmlImpl::getData()\n";
return 0;
}
int getIDImpl() {
std::cout << "ParserXmlImpl::getID()\n";
return 0;
}
};
template <typename Impl>
std::pair<int, int> parseFile(ParserInterface<Impl> parser) {
auto result = std::make_pair(parser.getData(), parser.getID());
return result;
}
int main() {
ParserJsonImpl jsonParser;
parseFile(jsonParser);
ParserXmlImpl xmlParser;
parseFile(xmlParser);
return 0;
}