Design pattern for a method returning different types / classes

This is a question for Object Design template specialists.

Suppose I have a class Parserthat is responsible for reading / parsing a data stream (which carry information of packets of different types ). Each of these packages has a different type of information, so ideally I'll have a class for each packet type ( PacketTypeA, PacketTypeB, ... each with its own interface).

class Parser {

public:

    /* ctor */
    /* dtor */


   void read_packet(/* arguments */);

   // methods...
private:
   // more methods...
}

Then the method Parser::read_packetwill go through the stream and return the class (or a pointer or a reference to the class) to the corresponding package type.

void ? (PacketBasicInterface), () ( )?

// Pure virtual (abstract) class to provide a common (and partial) interface
class PacketBasicInterface {
public:

    std::string whoAmI() const = 0;
    bool amIofType(const std::string& type) const = 0;

}

// Class to access data of type A packet
class PacketTypeA : public PacketBasicInterface {

public:
    // methodA_1()
    // methodA_2(), ...
}

// Class to access data of type A packet
class PacketTypeB : public PacketBasicInterface {

public:
    // methodB_1()
    // methodB_2(), ...
}

!

!

+4
3

, std:: variant .

, :

enum class packet_type {initialization_packet, confirmation_type, ... };

read_packet package_type :

typedef std::variant< ... > packet_info;

std::tuple<packet_type, packet_info> read_packet();

, , .

:

  • std::string, , .

  • std::any std::variant.

  • , std::string, , , .

, , std::variant ++ 17. , : .

+5

void ?

.

(PacketBasicInterface), () ( )?

.

. , . if-else. factory. , , , , .

"PacketTypeA" , , PacketTypeAFactory .

FWIW, . , .


, :


.

class PacketBasicInterface { };

class PacketTypeA : public PacketBasicInterface { };

class PacketTypeB : public PacketBasicInterface { };

factory.

// PacketFactory.h
class PacketFactory
{
   public:

      static PacketBasicInterface* makePacket(std::string const& packetData);

      static void registerFactory(std::string const& key, PacketFactory* factory);

      virtual PacketBasicInterface* make(std::string const& packetData) = 0;

      virtual ~PacketFactory() {}
};

, factory.

// PacketFactory.cpp

#include "PacketFactory.h"

namespace PacketBasicInterface_Impl
{
   using PacketFactoryMap = std::map<std::string, PacketFactory*>;

   PacketFactoryMap& getPacketFactoryMap()
   {
      static PacketFactoryMap theMap;
      return theMap;
   }
};

uisng namespace PacketBasicInterface_Impl;

PacketBasicInterface* PacketFactory::makePacket(std::string const& packetData)
{
   std::string key = extractKey(packetData);
   PacketFactoryMap& theMap = getPacketFactoryMap();
   PacketFactoryMap::iterator iter = theMap.find(key);
   if ( iter == theMap.end() )
   {
      return nullptr;
   }

   return iter->second->make(packetData);
}

void registerFactory(std::string const& key, PacketFactory* factory)
{
   getPacketFactoryMap()[key] = factory;
}

PacketTypeA factory.

// PacketTypeAFactory.cpp

#include "PacketFactory.h"
#include "PacketTypeA.h"

class PacketTypeAFactory : public PacketFactory
{
   public:

      virtual PacketBasicInterface* make(std::string const& packetData)
      {
         PacketTypeA* packet = new PacketTypeA();

         // Flesh out packet with data pulled from packetData
         // ...
         //

         return packet;
      }

      struct Initializer
      {
         Initializer() { PacketFactory::registerFactory("PacketTypeA", new PacketTypeAFactory); }
      };
};


// Constructing this object at static initialization time makes sure
// that PacketTypeAFactory is registered with PacketFactory when the
// stream data need to be parsed.
static PacketTypeAFactory::Initializer initializer;

PacketTypeB   PacketTypeA factory.

// PacketTypeBFactory.cpp


#include "PacketFactory.h"
#include "PacketTypeB.h"

class PacketTypeBFactory : public PacketFactory
{
   public:

      virtual PacketBasicInterface* make(std::string const& packetData)
      {
         PacketTypeA* packet = new PacketTypeA();

         // Flesh out packet with data pulled from packetData
         // ...
         //

         return packet;
      }

      struct Initializer
      {
         Initializer() { PacketFactory::registerFactory("PacketTypeB", new PacketTypeBFactory); }
      };
};


// Constructing this object at static initialization time makes sure
// that PacketTypeBFactory is registered with PacketFactory when the
// stream data need to be parsed.
static PacketTypeBFactory::Initializer initializer;

.

std::string packetData;
while ( getPacketData(packetData) )
{
   PacketBasicInterface* packet = PacketFactory::makePacket(packetData);
   if ( packet == nullptr )
   {
      // Deal with error.
   }
   else
   {
      // Use packet
   }
}
+1

, - .
:

#include<iostream>

struct Visitor;

struct PacketBasicInterface {
    virtual void accept(Visitor &) = 0;
};

struct PacketTypeA: PacketBasicInterface {
    void accept(Visitor &) override;
};

struct PacketTypeB: PacketBasicInterface {
    void accept(Visitor &) override;
};

struct Visitor {
    void visit(PacketTypeA) {
        std::cout << "PacketTypeA" << std::endl;
    }

    void visit(PacketTypeB) {
        std::cout << "PacketTypeB" << std::endl;
    }
};

void PacketTypeA::accept(Visitor &visitor) {
    visitor.visit(*this);
}

void PacketTypeB::accept(Visitor &visitor) {
    visitor.visit(*this);
}

struct Parser {
   PacketBasicInterface * read_packet() {
       return new PacketTypeB{};
   }
};

int main() {
    Visitor visitor;
    auto *packet = Parser{}.read_packet();
    packet->accept(visitor);
    delete packet;
}
+1

Source: https://habr.com/ru/post/1672491/


All Articles