在此示例中,如何避免代码重复?

时间:2019-01-07 22:45:45

标签: c++

我有一个简单的库(我们叫它Library #1),它可以处理某些数据向量(例如时间序列)。我想创建一个新的库(Library #2),它实际上具有大多数(但不是全部)相同的功能,但仅作用于该向量中的单个数据点。我想象一种解决方案,它是现有库的一个瘦包装,可以最大程度地减少代码重复。

这是简单的Library #1

class Foo {
private: 
   std::vector<double> data;

public:
   // Some constructors

   double get_data_at_timepoint(int timepoint) const;

   // Some other methods
}

get_data_at_timepoint仅返回数据向量的适当元素(假设它存在)。库Bar中的另一个类有一个Foo容器,并以某种方式对其进行操作-特别是,它可以do_something,并且还可以得到一个Foo

class Bar {
private:
    std::vector<Foo> foos;

public:
    // Some constructors

    Foo get_this_foo(int idx) const;

    void do_something();

    // Some other methods
}

其中(重要do_something以某种方式调用get_data_at_timepoint

void Bar::do_something() {
   // ...
   double x = foos[some_idx].get_data_at_timepoint(some_timepoint);
   // ...
};

我还想在{strong>单个时间点使用Library #2。像这样:

Library #1

现在在哪里:

class Foo2 {
private:
    double data;

public:
    double get_data() const;

    // All those other methods of Foo
}

class Bar2 {
private:
    std::vector<Foo2> foos;

public:
    Foo2 get_this_foo_2(int idx) const;
    void do_something();
    // All those other methods of Bar
}

很显然,void Bar2::do_something() { // ... double x = foos[some_idx].get_data(); // ... }; 基本上只是Foo2,但只有一个数据条目。我可以重写Foo的所有内容,但随后必须重复所有方法。我想改为定义长度为1(单个数据点)的Foo的精简包装。

对于Foo,有两个选择:(1)子类Foo2,或(2)使Foo成为Foo2唯一ptr的包装。我认为(2)更好,因为用户不应访问例如基类Foo中的时间点。

我还想避免为Foo编写额外的代码。当然,Bar中的功能do_something需要稍作调整,但总体而言,这两个看起来是如此并行。 Bar2中的许多其他方法也相同。

如何避免BarFoo2的代码重复?

2 个答案:

答案 0 :(得分:1)

执行以下操作:

template <typename FooType>
class BarBase {
private:
    std::vector<FooType> foos;
protected:
    virtual double get_data_from_foo(unsigned int, void*) = 0;
public:
    void do_something();
    // all other methods that used to be in Bar
};

class Bar : public BarBase<Foo> {
protected:
    virtual void get_data_from_foo(unsigned int id, void* time_ptr) {
        return foos[id].get_data_at_timepoint(*(timepoint_t*)time_ptr);
    }
};

class Bar2 : public BarBase<Foo2> {
protected:
    virtual void get_data_from_foo(unsigned int id, void* dummy) {
        return foos[id].get_data();
    }
};

您必须在BarBase :: do_something()中调用get_data_from_foo()。无论是否需要,您还必须计算时间点并将其传递给该函数。

或者,如果您不介意do_something()内部的代码重复,则删除get_data_from_foo()并向Bar和Bar2中的每个添加do_something()成员函数,分别定义它们。

答案 1 :(得分:0)

这就是为什么在许多c ++库中,类本身仅具有少量成员函数的原因,这些成员函数仅限于描述类型所真正需要的成员函数。使用免费功能可以解决所有其他问题,这使您可以对代码进行模块化并以更好的方式重用功能。

因此,仅使用成员函数来隐藏类的数据结构的实现详细信息,或者如果需要使用virtual,则对于大多数其他情况,您大多数时候都希望使用自由函数。

实现可能看起来像这样(这只是概念证明代码,以说明自由函数的用法):

//INSERT: This occurs when the user clicks enter and a random code is sent to the database and the user's email and/or phone
$insert = mysqli_query($con, "INSERT INTO activity (id, usr, code, status) VALUES ('{$UNIQUEID}', '$userid', '0314f5s4f14', 0)") ? null : die();

//UPDATE: This occurs when the user clicks the link in their text messages or email which sets the code to used status.
$update = mysqli_query($con, "UPDATE verification SET status='1' WHERE 
id='".$_SESSION['id']."'") ? null : die();

//HINT: use json for client to server applications, research progressive web apps.
$data = array('_POST' => $_POST, '_SESSION' => $_SESSION, 'sql' => $sql, 'dlt' => $dltq);
print json_encode($data); unset($data); mysqli_close($con);

现在,您可以将#include <iostream> #include <vector> class Foo { private: std::vector<double> data = {1,2,3}; public: std::vector<double>::const_iterator begin() const { return data.begin(); } std::vector<double>::const_iterator end() const { return data.end(); } double first() const { return *begin(); } }; class Foo2 { private: double data = 42; public: const double * begin() const { return &data; } const double * end() const { return &data + 1; } double first() const { return *begin(); } }; template<typename T> double get_data_at_timepoint(const T &obj, size_t index) { auto it = obj.begin()+index; return *it; } template<typename T> double get_data(const T &obj) { return obj.first(); } int main() { Foo f; Foo2 f2; double d = get_data_at_timepoint(f, 2); double d2 = get_data_at_timepoint(f2, 0); double d3 = get_data(f); double d4 = get_data(f2); // double d2 = get_data_at_timepoint(f2, 0); std::cout << "get_data_at_timepoint " << d << " " << d2 << std::endl; std::cout << "get_data " << d3 << " " << d4 << std::endl; } 与支持get_data_at_timepoint类型的迭代器的任何数据类型一起使用,而不仅限于您的double类。

如果Foo对于这些类之一来说是特殊的,则可以为该类创建专门的版本:

get_data_at_timepoint