Home > Blockchain >  Is it bad to store the underlying type of an object?
Is it bad to store the underlying type of an object?

Time:07-21

If I have a class called Node, would it be bad, if objects of the Node class knew their NodeType, and the NodeType would be used to cast to a specific interface like this:

// NodeType

enum class NodeType : uint8_t
{
    None        = 0,
    Foo         = 1 << 0,
    Bar         = 1 << 1,
    FooBar      = 1 << 2,
    
    FooMask     = Foo | FooBar,
    BarMask     = Bar | FooBar,
};

inline constexpr uint8_t operator&(const NodeType& t_lhs, const NodeType& t_rhs)
{
    return static_cast<uint8_t>(t_lhs) & static_cast<uint8_t>(t_rhs);
}
// Base Node Class

class Node
{
public:
    virtual NodeType GetNodeType() const = 0;
};
// Interfaces

class IFoo
{
public:
    virtual ~IFoo() = default;

    virtual void FooSpecificMethod() const = 0;
};

class IBar
{
public:
    virtual ~IBar() = default;

    virtual void BarSpecificMethod() const = 0;
};
// Derived Node Classes

class FooNode : public Node, public IFoo
{
public:
    NodeType GetNodeType() const override { return NodeType::Foo; }

    void FooSpecificMethod() const override { std::cout << "Foo.\n"; }
};

class BarNode : public Node, public IBar
{
public:
    NodeType GetNodeType() const override { return NodeType::Bar; }

    void BarSpecificMethod() const override { std::cout << "Bar.\n"; }
};

class FooBarNode : public Node, public IFoo, public IBar
{
public:
    NodeType GetNodeType() const override { return NodeType::FooBar; }

    void FooSpecificMethod() const override { std::cout << "Foo.\n"; }

    void BarSpecificMethod() const override { std::cout << "Bar.\n"; }
};
// Use of NodeType and Interfaces

std::vector<std::unique_ptr<Node>> GetNodes()
{
    std::vector<std::unique_ptr<Node>> nodes{};

    nodes.push_back(std::make_unique<FooNode>());
    nodes.push_back(std::make_unique<FooNode>());

    nodes.push_back(std::make_unique<BarNode>());

    nodes.push_back(std::make_unique<FooBarNode>());
    nodes.push_back(std::make_unique<FooBarNode>());
    nodes.push_back(std::make_unique<FooBarNode>());

    return nodes;
}

int main()
{
    std::vector<std::unique_ptr<Node>> nodes{ GetNodes() };
    
    for (const auto& node : nodes)
    {
        if ((node->GetNodeType() & NodeType::FooMask) != 0)
            dynamic_cast<const IFoo*>(node.get())->FooSpecificMethod();
    }

    for (const auto& node : nodes)
    {
        if ((node->GetNodeType() & NodeType::BarMask) != 0)
            dynamic_cast<const IBar*>(node.get())->BarSpecificMethod();
    }
}

My goal is to do type specific things on objects in a polymorphic collection like in the last code snippet. Is this a bad approach? Is there any more OO approach to this?

CodePudding user response:

Is this a bad approach? Is there any more OO approach to this?

Yes. You can just dynamic_cast to the appropriate pointer type and check the result is not null.

int main()
{
    std::vector<std::unique_ptr<Node>> nodes{ GetNodes() };
    
    for (const auto& node : nodes)
    {
        if (auto foo = dynamic_cast<const IFoo*>(node.get()))
            foo->FooSpecificMethod();
    }

    for (const auto& node : nodes)
    {
        if (auto bar = dynamic_cast<const IBar*>(node.get()))
            bar->BarSpecificMethod();
    }
}

If for some reason you want to avoid dynamic_cast, you can add virtual functions to Node. This is perhaps the "most OO" way.

class Node
{
public:
    virtual ~Node() = default;
    virtual const IFoo * asFoo() const { return nullptr; }
    virtual const IBar * asBar() const { return nullptr; }
};

class FooNode : public Node, public IFoo
{
public:
    const IFoo* asFoo() const override { return this; }

    void FooSpecificMethod() const override { std::cout << "Foo.\n"; }
};

class BarNode : public Node, public IBar
{
public:
    const IBar* asBar() const override { return this; }

    void BarSpecificMethod() const override { std::cout << "Bar.\n"; }
};

class FooBarNode : public Node, public IFoo, public IBar
{
public:
    const IFoo* asFoo() const override { return this; }
    const IBar* asBar() const override { return this; }

    void FooSpecificMethod() const override { std::cout << "Foo.\n"; }

    void BarSpecificMethod() const override { std::cout << "Bar.\n"; }
};

int main()
{
    std::vector<std::unique_ptr<Node>> nodes{ GetNodes() };
    
    for (const auto& node : nodes)
    {
        if (auto foo = node->asFoo())
            foo->FooSpecificMethod();
    }

    for (const auto& node : nodes)
    {
        if (auto bar = node->asBar())
            bar->BarSpecificMethod();
    }
}
  •  Tags:  
  • c
  • Related