如何在类之间共享昂贵的计算?

时间:2014-09-29 14:19:29

标签: c++ oop design-patterns

作为一个例子,我有这种情况,其中类AB执行相同的昂贵计算,函数expensiveFunction。这个函数是“纯粹的”,因为我可以保证它会在给定相同输入的情况下给出相同的结果。客户端可以使用具有相同输入的两个类(或更多类似的类),并且我希望耗费功能仅计算一次。但是,客户端也可能只对给定的输入使用一个类。

代码示例:

class A {
public:
    A(const InputData& input) {
        res = expensiveFunction(input);
    }
    void foo(); //Use the expensive result
private:
    ExpensiveResult res;
};

class B {
public:
    B(const InputData& input) {
        res = expensiveFunction(input); //Same function as in A
    }
    double bar(); //Use the expensive result
private:
    ExpensiveResult res;
};

int main() {
    //Get some input
    //...
    A a(input);
    B b(input);

    //Do stuff with a and b

    //More input

    A a2(otherInput);
    //...
}

在某些语言中,由于引用透明度和memoization,它只能为给定的输入安全地计算一次。

我所想到的是使用某种排序工厂方法/类,或者为存储结果的AB类提供函数对象/ functor / supension。

有什么好的设计理念来解决这个问题?

我拥有所有代码,因此我可以根据需要更改客户端或服务类。

3 个答案:

答案 0 :(得分:2)

您可以在功能内部进行记忆

COutput expensive(CInput input) {
    static std::map<CInput, COutput> memoized_result;
    auto resit = memoized_result.find(input);
    if (resit == memoized_result.end()) {
        // ... do calculations
        output = expensiveCalculation(input);
        resit = memoized_result.insert(std::make_pair(input, output));
    }
    return resit->second;
}

计算结果存储在静态映射(memoized_result)中,并在函数调用之间保持不变。

如果输入太昂贵而无法用作地图中的键,则可以创建一个单独的类来处理计算结果,并在所有客户端之间共享:

#include <memory>
using namespace std;
class ExpensiveResult {
    public:
    ExpensiveResult(int input) {
        out_ = input+1;
    }
    int out_;
};

class BaseCompResultUser {
public:
    BaseCompResultUser(const std::shared_ptr<ExpensiveResult>& res) {
        res_ = res;
    }

private:
    std::shared_ptr<ExpensiveResult> res_;
};

class A : public BaseCompResultUser {
public:
    A(const std::shared_ptr<ExpensiveResult>& r) : BaseCompResultUser(r) { }
};

class B : public BaseCompResultUser {
public:
    B(const std::shared_ptr<ExpensiveResult>& r) : BaseCompResultUser(r) { }
};

int main() {
    std::shared_ptr<ExpensiveResult> res(new ExpensiveResult(1));
    A a(res);
    B b(res);
    return 0;
}

这将强制在对象之间共享计算结果。

答案 1 :(得分:1)

我认为面向对象的解决方法是让expensiveFunction成为InputData的成员函数(或InputData的某个包装器)然后你的问题很多消失了。您只需在ExpensiveResult中使InputData成为可变缓存:

class InputData {
 private:
  mutable std::shared_ptr<ExpensiveResult> result_;
 public:
  InputData() : result_(nullptr) {}

  std::shared_ptr<ExpensiveResult> expensiveFunction() const {

    if (!result_) {
      // calculate expensive result...
      result_ = std::make_shared<ExpensiveResult>();
    }
    return result_;
  }
};

昂贵的计算只在第一次调用expensiveFunction时完成。如果以多线程方式调用它,则可能必须添加一些锁定。

答案 2 :(得分:1)

如果ExpensiveFunction在A和B中做同样的事情,它似乎不是真正的成员。为什么不是一个功能?

int main() {
//Get some input
//...
res = expensiveFunction (input) ;
A a(res);
B b(res);

//Do stuff with a and b

//...
}