基于抽象类快速确定子类 我有C++基类C抽象指令和大量直接子类: class CAbstrInstruction { /* ... */ }; class CSleepInstruction: public CAbstrInstruction { /* ... */ }; class CSetInstruction: public CAbstrInstruction { /* ... */ }; class CIfInstruction: public CAbstrInstruction { /* ... */ }; class CWhileInstruction: public CAbstrInstruction { /* ... */ }; // ...

基于抽象类快速确定子类 我有C++基类C抽象指令和大量直接子类: class CAbstrInstruction { /* ... */ }; class CSleepInstruction: public CAbstrInstruction { /* ... */ }; class CSetInstruction: public CAbstrInstruction { /* ... */ }; class CIfInstruction: public CAbstrInstruction { /* ... */ }; class CWhileInstruction: public CAbstrInstruction { /* ... */ }; // ...,c++,inheritance,design-patterns,C++,Inheritance,Design Patterns,还有一个CScriptWorker,它公开了一个公共方法execute: class CScriptWorker { public: void execute (const CAbstrInstruction *pI); private: void doSleep (const CSleepInstruction *pI); void doSet (const CSetInstruction *pI); void

还有一个CScriptWorker,它公开了一个公共方法execute:

class CScriptWorker
{
    public:
        void execute (const CAbstrInstruction *pI);

    private:
        void doSleep (const CSleepInstruction *pI);
        void doSet (const CSetInstruction *pI);
        void doIf (const CIfInstruction *pI);
        void doWhile (const CWhileInstruction *pI);
        // ...
};
execute方法的实现目前如下所示:

void CScriptWorker::execute (const CAbstrInstruction *pI)
{
    const CSleepInstruction *pSleep =
        dynamic_cast<const CSleepInstruction *>(pI);

    if (pSleep != NULL)
    {
        doSleep (*pSleep);
        return;
    }

    const CSetInstruction *pSet =
        dynamic_cast<const CSetInstruction *>(pI);

    if (pSet != NULL)
    {
        doSet (*pSet);
        return;
    }

    const CIfInstruction *pIf =
        dynamic_cast<const CIfInstruction *>(pI);

    if (pIf != NULL)
    {
        doIf (*pIf);
        return;
    }

    const CWhileInstruction *pWhile =
        dynamic_cast<const CWhileInstruction *>(pI);

    if (pWhile != NULL)
    {
        doWhile (*pWhile);
        return;
    }

    /* ... */
}

然而,这不是我想要的。为什么不呢关注点分离:CABSTRI指令的实例只是对所做工作的描述。它们构成了脚本的抽象语法树。这已经足够令人担忧了。CScriptWorker关心的是实际执行指令中描述的操作。CScriptWorker知道脚本运行的上下文。CAbstrInstruction不应该知道这一点。

CAbstrInstruction
应该定义一个纯虚拟方法(
execute()
,在您的示例中),您的子类应该覆盖并实现该方法

例如:

class CAbstrInstruction 
{
     /* ... */
     virtual void execute() const = 0;
}

class CSleepInstruction 
{
     /* ... */
     void execute() override const
     {
         /* your code here */
     }
}

/* ... */
void CScriptWorker::execute (const CAbstrInstruction *pI)
{
    pI->execute();
}

CAbstrInstruction
应该定义一个纯虚拟方法(
execute()
,在您的示例中),您的子类应该覆盖并实现该方法

例如:

class CAbstrInstruction 
{
     /* ... */
     virtual void execute() const = 0;
}

class CSleepInstruction 
{
     /* ... */
     void execute() override const
     {
         /* your code here */
     }
}

/* ... */
void CScriptWorker::execute (const CAbstrInstruction *pI)
{
    pI->execute();
}

如果事情简单的话,将
execute
方法的实现转移到
CAbstrInstruction
的子类中可能是答案。但是,OP明确规定,执行方法应在CScriptWorker中保持独立,以将了解要做什么(指令的工作)与如何做(CScriptWorker的工作)分开。这可以通过双重分派实现,有时也称为访客模式:

class IInstructionDispatchTarget
{
    public:
    virtual void onDispatch (const CSleepInstruction &instr) = 0;
    virtual void onDispatch (const CSetInstruction &instr) = 0;
};
class CAbstrInstruction
{
    public:
    virtual void dispatch (IInstructionDispatchTarget &t) const = 0;
};
class CSleepInstruction: public CAbstrInstruction
{
     public:
     virtual void dispatch (IInstructionDispatchTarget &t) const override
         { t.onDispatch (*this); }
};
class CSetInstruction: public CAbstrInstruction
{
     public:
     virtual void dispatch (IInstructionDispatchTarget &t) const override
         { t.onDispatch (*this); }
};
class CScriptWorker: public IInstructionDispatchTarget
{
    public:
    void execute (const CAbstrInstruction *pI)
        { pI->dispatch (*this); }
    virtual void onDispatch (const CSleepInstruction &instr) override
    {
        // do sleep
    }
    virtual void onDispatch (const CSetInstruction &instr) override
    {
        // do set
    }
};
CScriptWorker
上调用
execute
时,它调用指令的
dispatch
方法。作为回报,指令使用其特定的
this
指针调用调度目标上的
onDispatch
方法,从而调用正确的方法

接口
IInstructionDispatchTarget
有两个用途。一方面,它确保
CAbstrInstruction
的实例根本不需要知道
CScriptWorker
;他们只需要知道接口。另一方面,它允许其他调度目标使用相同的机制,例如在遍历指令以优化AST时


如果认为存在
IInstructionDispatchTarget
是不必要的,那么事情可以稍微简化,如ROX的回答所示

如果事情简单的话,将
execute
方法的实现移动到
CAbstrInstruction
的子类中可能是一个答案。但是,OP明确规定,执行方法应在CScriptWorker中保持独立,以将了解要做什么(指令的工作)与如何做(CScriptWorker的工作)分开。这可以通过双重分派实现,有时也称为访客模式:

class IInstructionDispatchTarget
{
    public:
    virtual void onDispatch (const CSleepInstruction &instr) = 0;
    virtual void onDispatch (const CSetInstruction &instr) = 0;
};
class CAbstrInstruction
{
    public:
    virtual void dispatch (IInstructionDispatchTarget &t) const = 0;
};
class CSleepInstruction: public CAbstrInstruction
{
     public:
     virtual void dispatch (IInstructionDispatchTarget &t) const override
         { t.onDispatch (*this); }
};
class CSetInstruction: public CAbstrInstruction
{
     public:
     virtual void dispatch (IInstructionDispatchTarget &t) const override
         { t.onDispatch (*this); }
};
class CScriptWorker: public IInstructionDispatchTarget
{
    public:
    void execute (const CAbstrInstruction *pI)
        { pI->dispatch (*this); }
    virtual void onDispatch (const CSleepInstruction &instr) override
    {
        // do sleep
    }
    virtual void onDispatch (const CSetInstruction &instr) override
    {
        // do set
    }
};
CScriptWorker
上调用
execute
时,它调用指令的
dispatch
方法。作为回报,指令使用其特定的
this
指针调用调度目标上的
onDispatch
方法,从而调用正确的方法

接口
IInstructionDispatchTarget
有两个用途。一方面,它确保
CAbstrInstruction
的实例根本不需要知道
CScriptWorker
;他们只需要知道接口。另一方面,它允许其他调度目标使用相同的机制,例如在遍历指令以优化AST时


如果认为存在
IInstructionDispatchTarget
是不必要的,那么事情可以稍微简化,如ROX的回答所示

当客户端不需要知道对象的具体类型时,最好使用继承。您希望使用,因为您有固定数量的已知指令,并且您的执行者需要知道它正在执行哪种指令。这是最容易使用的,或者如果您是C++17之前的版本

#include <variant>

struct Set {};
struct If {};
struct While {};

using Instruction = std::variant<
    Set,
    If,
    While
    >;

#include <iostream>

struct Executor {
    void operator()(Set const&) const { std::cout << "Set\n"; }
    void operator()(If const&) const { std::cout << "If\n"; }
    void operator()(While const&) const { std::cout << "While\n"; }
};

void execute(Instruction const& i) {
    std::visit(Executor(), i);
}

当客户端不需要知道对象的具体类型时,最好使用继承。您希望使用,因为您有固定数量的已知指令,并且您的执行者需要知道它正在执行哪种指令。这是最容易使用的,或者如果您是C++17之前的版本

#include <variant>

struct Set {};
struct If {};
struct While {};

using Instruction = std::variant<
    Set,
    If,
    While
    >;

#include <iostream>

struct Executor {
    void operator()(Set const&) const { std::cout << "Set\n"; }
    void operator()(If const&) const { std::cout << "If\n"; }
    void operator()(While const&) const { std::cout << "While\n"; }
};

void execute(Instruction const& i) {
    std::visit(Executor(), i);
}

如果有其他类将实现IInsttructionVisitor接口,那么visitor模式工作得很好。这确保了所有这些类都可以处理同一组指令类

如果没有从IInstuctorVisitor派生的其他类,则可以稍微简化它:-

class CScriptWorker
{
    public:
        void execute (const CAbstrInstruction* pI)
        {
           pI->ResolveInstructionType(*this);
        }

    // Can be made friends of appropriate instruction classes or left public as you see fit
       void doInstruction (const CSleepInstruction* pI);
       void doInstruction (const CSetInstruction* pI);
       void doInstruction (const CIfInstruction* pI);
       void doInstruction (const CWhileInstruction* pI);
    // note the name is now the same, name of the parameter should be enough to tell what's being done
    // also I'd probably make these references not pointers

};


class CAbstrInstruction
{
    public:
    virtual void ResolveInstructionType (CScriptWorker& v) = 0;
};


class CSleepInstruction: public CAbstrInstruction
{
     public:
     void ResolveInstructionType (CScriptWorker& w) override { w.doInstruction (this); }
};

简化的一点好处是,现在代码稍微少一些,要修改的代码稍微少一些。如果添加了新指令,则可以选择除visit、visitor等以外的名称。

如果有其他类将实现IInstructionVisitor接口,则visitor模式工作得很好。这确保了所有这些类都可以处理同一组指令类

如果没有从IInstuctorVisitor派生的其他类,则可以稍微简化它:-

class CScriptWorker
{
    public:
        void execute (const CAbstrInstruction* pI)
        {
           pI->ResolveInstructionType(*this);
        }

    // Can be made friends of appropriate instruction classes or left public as you see fit
       void doInstruction (const CSleepInstruction* pI);
       void doInstruction (const CSetInstruction* pI);
       void doInstruction (const CIfInstruction* pI);
       void doInstruction (const CWhileInstruction* pI);
    // note the name is now the same, name of the parameter should be enough to tell what's being done
    // also I'd probably make these references not pointers

};


class CAbstrInstruction
{
    public:
    virtual void ResolveInstructionType (CScriptWorker& v) = 0;
};


class CSleepInstruction: public CAbstrInstruction
{
     public:
     void ResolveInstructionType (CScriptWorker& w) override { w.doInstruction (this); }
};

简化的一点好处是,现在代码稍微少了一点,要修改的代码稍微少了一点。如果添加了新指令,您可以选择除visit、visitor等以外的名称。

查看visitor设计模式您是对的,我认为这可能会解决问题。您的
CAbstrInstruction
没有提供任何内容,你所做的相当于铸造
void*
。作为接口,这些说明应该提供什么?不确定你的意思。当然,我已经删除了类中的所有细节。我的意思是,您没有使用任何有意义的特性