c++设计选择

C++ design choice

本文关键字:选择 c++      更新时间:2023-10-16

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

ActionBase* SelectAction(Table* Table, State* State);

ActionBase是一个抽象类。在SelectAction方法内部,如果表不是空的,则考虑状态,从表中获取一些动作。

如果表为空,则应该创建并返回一个随机操作。但是ActionBase是一个抽象类,所以不能被实例化

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

问题是,这个函数(SelectAction)应该返回一个实验特定的动作,如果表是空的,但是它不知道任何关于特定的实验。有什么设计上的变通方法吗?

这取决于空表是否…

  1. 预计在正常情况下发生
  2. 可能在异常情况下发生
  3. 应该永远不会发生,除非程序中有错误

解决方案1:

在控制流中包含空表处理。函数没有足够的信息来正确响应,因此:

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

    ActionBase *SelectAction(Table *table, State *state, ActionBase *defaultAction);
    

    如果你不想构造默认的动作,除非它是必要的,你可以通过模板参数传递它的类型,可以选择使用附加的参数来构造它:

    template <class DefaultAction, class... DefActArgs>
    ActionBase *SelectAction(Table *table, State *state, DefActArgs &&... args);
    
  • 让调用者处理它,返回操作是否成功:

    bool SelectAction(Table *table, State *state, ActionBase *&selectedAction);
    

解决方案2:

抛出异常。谁能处理它,它就会冒出来。这很少用作参数检查,因为它应该由首先应该产生非空表的对象抛出。

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

解决方案3:

设置断言。如果你的函数收到一个空表,说明有问题,最好停止程序,用调试器检查一下。

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

这是我的想法:这不是经过测试的代码,但是你明白了。
1 .

//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;
当然,我们的想法是创建SomeRandomAction的不同实现,甚至通过构造函数将参数传递给它们,使它们完全不同。对于您创建的每个实例,它们都将出现在静态列表中。用新的实现扩展列表只是意味着从RandomActionBase派生,实现它并确保创建一个实例,基类永远不会受此影响,这使得它甚至是根据OCP设计的。开闭原则。代码是可扩展的,而不必更改已经存在的代码。OCP是SOLID的一部分。

2。另一个可行的解决方案是返回一个空对象。它与上面非常相似,但是当列表为空时,您总是返回null对象。请注意,空对象并不仅仅是空。参见https://en.wikipedia.org/wiki/Null_Object_pattern它只是一个类的虚拟实现,以避免必须检查空指针,从而使设计更优雅,更不容易受到空指针解引用错误的影响。