测试功能的c ++类 [英] Testing a c++ class for features

查看:150
本文介绍了测试功能的c ++类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一组类,描述一组逻辑框,可以容纳事物和做事情。我有

  struct IBox //所有框都做这些
{
....
}

struct IBoxCanDoX //执行的权力X
{
void x();
}

struct IBoxCanDoY //执行的权力Y
{
void y();
}



我不知道什么是最好的这些类的客户端处理这些可选功能



a)

  if(typeid(box)== typeid(IBoxCanDoX))
{
IBoxCanDoX * ix = static_cast< IBoxCanDoX *>
ix-> x();
}

b)



p $ p> IBoxCanDoX * ix = dynamic_cast< IBoxCanDoX *>(框);
if(ix)
{
ix-> x();
}

c)

  if(box-> canDoX())
{
IBoxCanDoX * ix = static_cast< IBoxCanDoX *>
ix-> x();
}

d)现在不同的类结构

  struct IBox 
{
void x();
void y();
}
...
box-> x(); ///被不执行x的实现忽略

e)除了

  box-> x()//'未实现'异常抛出

f)显式测试函数

  if(box-> canDoX 
{
box-> x();
}

我相信还有其他人。



编辑:



只是为了使用例更清楚



最终用户通过交互式ui的东西。他们可以输入make box do X。我需要知道如果box可以做x。或者我需要禁用make current box do X命令



EDIT2:Thx给所有回答者



正如诺亚罗伯茨指出的(a)没有工作(解释我的一些问题!)。
我最后做了(b)和一个小的变体

  template< class T& 
T * GetCurrentBox()
{
if(!current_box)
throwcurrent box not set;
T * ret = dynamic_cast< T *>(current_box);
if(!ret)
throw当前框不支持请求操作;
return ret;
}
...
IBoxCanDoX * ix = GetCurrentBox< IBoxCanDoX>();
ix-> x();

,并让UI管道处理异常(我不是真的抛出裸字符串)。



我也打算探索游客

解决方案

'I'前缀意味着接口,因为它将意味着在Java中,这将使用C ++中的抽象基础,然后你的第一个选项将无法工作....所以一个人出来。我已经使用它的一些东西虽然。



不要'd',它会污染你的层次结构。保持你的界面干净,你会很高兴你做到了。因此,车辆类没有踏板()功能,因为只有一些车辆可以踏板。如果客户端需要使用pedal()函数,那么它真的需要知道这些类。



同样的原因, d'PLUS,它违反了利斯科夫替代原则。如果客户端需要检查一个类在调用它之前响应它,以便它不会爆炸,那么最好的方法是尝试投射到具有该函数的对象。 'f'与支票是一样的。



'c'是多余的。如果你的层次结构设置了它应该的方式,然后投射到ICanDoX足以检查x是否可以做X()。



因此'b'成为你的答案从给定的选项。但是,正如Gladfelter所证明的,在你的帖子中有没有考虑过的选项。



编辑注释:我没有注意到'c'使用了static_cast而不是动态。正如我在一个回答中提到的,dynamic_cast版本是更干净,应该是首选,除非特定情况另有规定。它类似于以下选项,它污染基本接口。



编辑2:我应该注意,关于'a',我已经使用它,不要像你在你的帖子中那样静态地使用类型。任何时候,我使用typeid根据类型拆分流,它总是基于在运行时注册的东西。例如,打开正确的对话框以编辑某个未知类型的对象:对话框调控器根据其编辑的类型向工厂注册。这使我不必在添加/删除/更改对象时更改任何流控制代码。我通常不会在不同的情况下使用此选项。


I have a set of classes that describe a set of logical boxes that can hold things and do things to them. I have

struct IBox // all boxes do these
{
    ....
}

struct IBoxCanDoX // the power to do X
{
    void x();
}

struct IBoxCanDoY // the power to do Y
{
    void y();
}

I wonder what is the 'best' or maybe its just 'favorite' idiom for a client of these classes to deal with these optional capabilities

a)

    if(typeid(box) == typeid(IBoxCanDoX))
    {
         IBoxCanDoX *ix = static_cast<IBoxCanDoX*>(box);
         ix->x();
    }

b)

   IBoxCanDoX *ix = dynamic_cast<IBoxCanDoX*>(box);
    if(ix)
    {
         ix->x();
    }

c)

if(box->canDoX())
{
    IBoxCanDoX *ix = static_cast<IBoxCanDoX*>(box);
    ix->x(); 
}

d) different class struct now

struct IBox
{
    void x();
    void y();
}
...
box->x(); /// ignored by implementations that dont do x

e) same except

box->x() // 'not implemented' exception thrown

f) explicit test function

if(box->canDoX())
{
   box->x();
} 

I am sure there are others too.

EDIT:

Just to make the use case clearer

I am exposing this stuff to end users via interactive ui. They can type 'make box do X'. I need to know if box can do x. Or I need to disable the 'make current box do X' command

EDIT2: Thx to all answerers

as Noah Roberts pointed out (a) doesnt work (explains some of my issues !). I ended up doing (b) and a slight variant

   template<class T>
    T* GetCurrentBox()
    {
       if (!current_box)
          throw "current box not set";
       T* ret = dynamic_cast<T*>(current_box);
       if(!ret)
          throw "current box doesnt support requested operation";
       return ret;
    }
    ...
    IBoxCanDoX *ix = GetCurrentBox<IBoxCanDoX>();
    ix->x();

and let the UI plumbing deal nicely with the exceptions (I am not really throwing naked strings).

I also intend to explore Visitor

解决方案

If you are using the 'I' prefix to mean "interface" as it would mean in Java, which would be done with abstract bases in C++, then your first option will fail to work....so that one's out. I have used it for some things though.

Don't do 'd', it will pollute your hierarchy. Keep your interfaces clean, you'll be glad you did. Thus a Vehicle class doesn't have a pedal() function because only some vehicles can pedal. If a client needs the pedal() function then it really does need to know about those classes that can.

Stay way clear of 'e' for the same reason as 'd' PLUS that it violates the Liskov Substitution Principle. If a client needs to check that a class responds to pedal() before calling it so that it doesn't explode then the best way to do that is to attempt casting to an object that has that function. 'f' is just the same thing with the check.

'c' is superfluous. If you have your hierarchy set up the way it should be then casting to ICanDoX is sufficient to check if x can do X().

Thus 'b' becomes your answer from the options given. However, as Gladfelter demonstrates, there are options you haven't considered in your post.

Edit note: I did not notice that 'c' used a static_cast rather than dynamic. As I mention in an answer about that, the dynamic_cast version is cleaner and should be preferred unless specific situations dictate otherwise. It's similar to the following options in that it pollutes the base interface.

Edit 2: I should note that in regard to 'a', I have used it but I don't use types statically like you have in your post. Any time I've used typeid to split flow based on type it has always been based on something that is registered during runtime. For example, opening the correct dialog to edit some object of unknown type: the dialog governors are registered with a factory based on the type they edit. This keeps me from having to change any of the flow control code when I add/remove/change objects. I generally wouldn't use this option under different circumstances.

这篇关于测试功能的c ++类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆