jeudi 30 septembre 2021

How to call member functions of an IMPL from an injected strategy pattern

So I have been tasked with implementing a strategy pattern on an existing code base. The code below is a very simplified version of the code that I am using for demonstration purposes. I don't have a lot of leeway to re-architect the WorldTraversal class. So the issue I am having is that I am not that familiar with the PIMPL idiom, and it adds a layer of doubt into my mind as to how to approach this.

The apply method in the strategy will need to execute two methods that are defined in the impl. But I am unsure how to gain access to them without exposing them to the public interface. I have tried using std::function, and passing the method as a parameter, but I am unsure of the syntax for binding a method on an impl (and even if this should be attempted).

Any assistance would be appreciated.

// The main class/object that will need to execute different strategies based on the situation
class WorldTraversal
{
public:
    void setStrategy(ITraversalStrategy&& strategy) { m_impl->strategy = std::move(strategy); }
    void execute()
    {
        // Apply needs access to methods in the impl but they are not static
        m_impl->traversalStrategy->apply(std::bind(&m_impl->processPath, *m_impl), std::bind(&m_impl->executePath, *m_impl)); // Can I even do this given the impl is a unique_ptr??
    }

private:
    struct Impl;
    std::unique_ptr<Impl> m_impl;
}

// The IMPL which contains (at least) two methods I would need to call
struct WorldTraversal::Impl
{
    Impl() {}

    void processPath();
    void executePath();

    std::unique_ptr<ITraversalStrategy> traversalStrategy;
}

WorldTraversal::Impl::processPath() {}
WorldTraversal::Impl::executePath() {}

// The strategy (I left the interface out for brevity)
class GroundTraversal : public ITraversalStrategy
{
public:
    using ProcessPath = std::function<void()>;
    using ExecutePath = std::function<void()>;

    virtual void apply(ProcessPath processPath, ExecutePath executePath) override
    {
        // Implementation of the actual strategy goes here
        // I need to be able to call processPath and executePath in here
        // Different strategies may need to call these differently etc.
        processPath();
        executePath();
    }
}

int main()
{
    WorldTraversal worldTraversal;

    // Could be ground, air, water etc.
    worldTraversal.setStrategy(std::make_unique<GroundTraversal>());
    worldTraversal.execute();

    return 0;
}

Aucun commentaire:

Enregistrer un commentaire