C++设计选择

C++设计选择,c++,oop,C++,Oop,有一个类ActionSelection,它具有以下方法: ActionBase*选择ActionTable*表,状态*状态 ActionBase是一个抽象类。在SelectAction方法中,如果表不是空的,则根据状态从表中提取一些操作 如果表为空,则应创建并返回一个随机操作。但是ActionBase是一个抽象类,因此无法实例化 对于不同的实验/环境,动作是不同的,但有一些共同的行为,这就是为什么有一个ActionBase类 问题是,如果表为空,那么这个函数SelectAction应该返回一个特

有一个类ActionSelection,它具有以下方法:

ActionBase*选择ActionTable*表,状态*状态

ActionBase是一个抽象类。在SelectAction方法中,如果表不是空的,则根据状态从表中提取一些操作

如果表为空,则应创建并返回一个随机操作。但是ActionBase是一个抽象类,因此无法实例化

对于不同的实验/环境,动作是不同的,但有一些共同的行为,这就是为什么有一个ActionBase类


问题是,如果表为空,那么这个函数SelectAction应该返回一个特定于实验的操作,但是它对特定实验一无所知。这有什么设计上的变通方法吗?

这取决于空表

预计在正常情况下会发生 可能在异常情况下发生 除非程序中有错误,否则不会发生 解决方案1: 将空表处理包括到控制流中。由于功能没有足够的信息来正确反应,因此:

传入第三个参数,其中包含要返回的默认操作:

ActionBase *SelectAction(Table *table, State *state, ActionBase *defaultAction);
如果不希望构造默认操作(除非需要),则可以改为通过模板参数传递其类型,也可以使用其他参数来构造它:

template <class DefaultAction, class... DefActArgs>
ActionBase *SelectAction(Table *table, State *state, DefActArgs &&... args);
解决方案2: 抛出异常。它将由谁来处理。这很少用作参数检查,因为它应该由首先应该生成非空表的对象抛出

ActionBase *SelectAction(Table *table, State *state) {
    if(table->empty())
        throw EmptyTableException();

    // ...
}
解决方案3: 设置断言。如果您的函数收到一个空表,则表示某些内容已损坏,最好停止程序并使用调试器查看它

ActionBase *SelectAction(Table *table, State *state) {
    assert(!table->empty());

    // ...
}

以下是我的想法:它不是经过测试的代码,但你明白了。 一,

当然,我们的想法是创建一些RandomAction的不同实现,甚至通过它们的构造函数将参数传递给它们,使它们完全不同。对于您创建的每个实例,它们将显示在静态列表中。 用一个新的实现扩展列表仅仅意味着从RandomActionBase派生、实现它并确保创建一个实例,基类永远不会受到这一点的影响,这使得它甚至成为一个符合OCP的设计。 开闭原理。代码是可扩展的,而不必更改已经存在的代码。OCP是SOLID的一部分

二,。 另一个可行的解决方案是返回空对象。它与上面的非常相似,但是当列表为空时,总是返回null对象。请注意,空对象不仅仅是空的。看见
它只是一个类的虚拟实现,以避免必须检查空指针,从而使设计更优雅,更不容易出现空指针解引用错误。

问题已得到纠正,定义了哪些是好的随机操作。从actionbase派生一个随机操作,并使其成为一个链表,以便它们都在链表中注册自己。那么你的随机操作就是从列表中取一个的过程,这个列表无论如何都是可以访问的,因为它是一个链表,其中第一个是类中静态的,RandomActionBasequestion被更正了。。。然后关闭。。。当我写一篇问答时:。无论哪种方式,这种情况下的正常操作都是编写错误消息/报告错误,以这样的方式,用户拥有最多的信息来解决问题,例如表XXX应该包含与特定实验匹配的记录-表是空的,而不是一群人正在结束他们不知道答案的问题。我完全得到了op想要的。不幸的是,我输入了一个答案,解释了如何从actionbase创建一个名为randomactionbase的派生类。然后从这个类中导出所有可行的随机动作。在randomactions中,构造函数将它们链接在一起。然后从静态列表中随机抽取一个。由于列表中有3个可能的选项,因此向上投票。我在回答中假设选项1-
ActionBase *SelectAction(Table *table, State *state) {
    assert(!table->empty());

    // ...
}
//header
class RandomActionBase : public ActionBase{
public
    RandomActionBase();
    static RandomAction* selectRandomAction();
protected:
    static RandomActionBase* _first;
    RandomActionBase* _next;
    void register(RandomActionBase* r);
};

//implementation
RandomActionBase::_first = NULL;

RandomActionBase::RandomActionBase():_next(NULL){
    if (_first==NULL) _first = this;
    else _first->register(this);
}

void RandomActionBase::register(RandomActionBase* r)
{
     if (_next==NULL) _next = r;
     else _next->register(r); 
}

RandomAction* RandomActionBase::selectRandomAction()
{
    //count the number of randomactionbases
    int count = 0;
    RandomActionBase* p = _first;
    while(p){
         ++count;
         p = p->_next;
    }
    //now that you know the count you can create a random number ranging from 0 to count, I 'll leave this up to you and assume the random number is simply 2, 
    unsigned int randomnbr = 2;
    RandomActionBase* p = _first;
    while(randomnbr>0){
        p= p->_next;
        --randomnbr;
    }
    return p;
}

//header
class SomeRandomAction : public RandomActionBase{
public:
   //implement the custom somerandomaction
}

//implementation
static SomeRandomAction SomeRandomAction_l;