如何优雅地声明变量集的子集

时间:2014-02-16 00:09:10

标签: c++

让我们说要求是这样的: 作为班级用户,我想收集有关某个主题的信息,当班级有足够的信息时,我希望班级能够将收集的数据列表返回给我。足够的信息被定义为 - 当收集来自所有可能信息的子集的所有信息时。该子集不是固定的,而是提供给班级的。

例如,这是所有可能信息的列表:

{
   string name; 
   int age;
   char sex;
   string location;
}

我想让我的用户有机会告诉我从一些数据源(我的班级解析数据)收听,直到我得到年龄和性别。

问题是我不知道如何在没有枚举的情况下传达它。 基本上我的枚举解决方案是监听数据源,直到我确定在我收集了所有数据的2组枚举(收集,必需)上使用std :: includes。

没有枚举可以做到吗?

5 个答案:

答案 0 :(得分:3)

每当我想要将一段逻辑的实现从它需要的地方解耦时 - 在这种情况下,知道“有多少数据就足够了” - 我想到了一个回调函数。

据推测,您的班级可以收集的所有可能数据集都是已知的(在您的示例中为nameagesexlocation。这意味着您的班级的所有客户也可以在不增加耦合量的情况下了解它。依赖

我的解决方案是创建一个封装此逻辑的“赋值器”类。此类的子类的实例由客户端创建,并在初始数据请求时传递给数据收集器;该对象负责在收集到足够的数据时决定(并告诉“收集器”)。

#include <string>

// The class that decides when enough data has been collected
// (Provided to class "collector" by its clients)
class evaluator
{
public:
  virtual ~evaluator() {};

  // Notification callbacks; Returning *this aids in chaining
  virtual evaluator& name_collected()     { return *this; }
  virtual evaluator& age_collected()      { return *this; }
  virtual evaluator& sex_collected()      { return *this; }
  virtual evaluator& location_collected() { return *this; }

  // Returns true when sufficient data has been collected
  virtual bool enough() = 0;
};

// The class that collects all the data
class collector
{
public:
  void collect_data( evaluator& e )
  {
    bool enough = false;
    while ( !enough )
    {
      // Listen to data source...

      // When data comes in...
      if ( /* data is name */ )
      {
        name = /* store data */
        enough = e.name_collected().enough();
      }
      else if ( /* data is age */ )
      {
        age = /* store data */
        enough = e.age_collected().enough();
      }
      /* etc. */
    }
  }

  // Data to collect
  std::string name;
  int age;
  char sex;
  std::string location;
};

在您的示例中,您希望特定客户能够指定agesex的组合就足够了。所以你像这样继承evaluator

class age_and_sex_required : public evaluator
{
public:
  age_and_sex_required()
    : got_age( false )
    , got_sex( false )
  {
  }

  virtual age_and_sex_required& age_collected() override
  {
    got_age = true;
    return *this;
  }

  virtual age_and_sex_required& sex_collected() override
  {
    got_sex = true;
    return *this;
  }

  virtual bool enough() override
  {
    return got_age && got_sex;
  }

private:
  bool got_age;
  bool got_sex;
};

客户端在请求数据时传递此类的实例:

collector c;
c.collect_data( age_and_sex_required() );

collect_data实例报告收集的数据量“足够”并且您没有构建任何逻辑,知识,枚举等任何内容时,age_and_sex_required方法退出并返回collector课程。此外,构成“足够”的逻辑是无限可配置,而collector类没有进一步的更改。

-----编辑-----

备用版本使用具有..._collected()方法的类,而只是使用单个(typedef'd)函数接受collector作为参数并返回{ {1}}:

boolean

#include <functional> typedef std::function< bool( collector const& ) > evaluator_t; 中的代码只会调用

collector::collect_data(...)

每次收集一条数据。

这将消除单独的enough = e( *this ); 抽象接口的必要性,但会增加对evaluator类本身的依赖,因为作为collector函数传递的对象将负责检查evaluator_t对象的状态,用于评估是否已收集到足够的数据(并且需要collector具有足够的公共接口来查询其状态):

collector

答案 1 :(得分:2)

不确定这是否适合您,但因为每个项目可能存在,也可能不存在,boost::optional会出现问题。

{
   boost::optional<string> name; 
   boost::optional<int> age;
   boost::optional<char> sex;
   boost::optional<string> location;
}

然后,您的班级可以使用bool validate()方法检查所需项目集的存在。这可以是类方法,也可以作为回调传入。

答案 2 :(得分:2)

默认情况下,您可以为每个成员说“我需要”。

static const string required_name = /* your default name */;
// ...

您还可以使用整数作为位掩码,其行为类似于一组枚举。

typedef int mask_type;
static const mask_type name_flag = 0x01;
static const mask_type age_flag = 0x02;
static const mask_type sex_flag = 0x04;
static const mask_type location_flag = 0x08;
//...

mask_type required = name_flag | age_flag; // need to collect name & age
collect(&my_instance, required) // collect and set required values

易于使用且没有单个int

的开销
  1. 不再需要值:required &= ~xx_flag
  2. 不再需要值:bool(required)
  3. 值是必需的:bool(required & xx_flag)
  4. ...

答案 3 :(得分:1)

枚举似乎是最干净的方法,但我想如果您愿意,可以使用与每种数据类型对应的不同字符的短字符串。这不是那么干净,但可能更容易调试。

答案 4 :(得分:1)

难道你不能通过使用模板和抽象类来完成这样的行为,做这样的事情?

class SomeAbstract
{
public:
    virtual bool getRequired()  = 0;
    virtual void setRequired(bool req) = 0;
};

template <class T>
class SomeTemplate
{
    T value;
    bool required;

public:
    TemplateName(T t)
    {
        value = t;
        required = false;
    }
    void setRequired(bool req)
    {
        required = req;
    }
    bool getRequired()
    {
        return required;
    }
    void setValue(T newValue)
    {
        value = newValue;
    }
    T getValue()
    {
        return value;
    }
};

然后,您可以将属性列表声明为相同的类型。

SomeTemplate<string> name; 
SomeTemplate<int> age;
SomeTemplate<char> sex;
SomeTemplate<string> location;

由于模板继承了相同的类型,您可以将它们存储在std::vector<SomeAbstract>中并对它们进行相同的处理。

这不是经过测试的代码,这个想法可能有一些改进,但我希望你明白我的意思。