根据给定对象的类型更改类的行为

时间:2012-12-14 15:45:06

标签: c++ templates

我想设计一个通用的C ++类,它定义一个operator()函数,它返回一个点上的数学函数的值。但是,我希望该类能够使用已定义operator()的对象或使用该类将插值以提供值的值表来实例化。我想到了以下方法:

class MathFunction
{
    MathFunction(Functor& function);
    MathFunction(Mat<double> interpolationValues);

    double operator()(double radius, double angle);
};

构造函数可以设置成员变量,而double operator()可以使用switch语句来确定如何输出我想要的double。但这看起来非常不优雅。

这个类的模板化版本可能有用,但由于这两个方法不共享任何代码,这样做是否明智?模板版本究竟是如何设计的?

不确定我是否清楚。如果需要澄清,请发表评论。

编辑:虽然我选择了Useless的答案来完整,但我想强调dasblikenlight的贡献的速度和清晰度。谢谢。

5 个答案:

答案 0 :(得分:6)

这是使用strategy pattern的主要情况:根据传递给构造函数的参数,MathFunction应该实例化依赖于Functor的策略对象或策略对象依赖于插值的列表。然后operator ()的调用应该将调用转发给策略,并通过公共虚函数的实现获得结果:

class MathFunction {
    struct Strategy {
        virtual double Calculate(double radius, double angle)=0;
        virtual ~Strategy(){}
    };
    class FunctorStrategy : public Strategy {
        Functor& _function;
    public:
        FunctorStrategy(Functor& function)  : _function(function) {}
        virtual double Calculate(double radius, double angle) {
            return _function(radius, angle);
        }
    }
    class InterpolationStrategy : public Strategy {
        Mat<double> _interpolationValues;
    public:
        InterpolationStrategy (Mat<double> interpolationValues) 
        : _interpolationValues(interpolationValues) {}
        virtual double Calculate(double radius, double angle) {
            return ...; // Use _interpolationValues to do calculation
        }
    };
    unique_ptr<Strategy> _strategy;
public:
    MathFunction(Functor& function)
    : _strategy(new FunctorStrategy(function)) {}
    MathFunction(Mat<double> interpolationValues)
    : _strategy(new InterpolationStrategy(interpolationValues)) {}
    // And now for the complicated part:
    double operator()(double radius, double angle) {
        return _strategy->Calculate(radius, angle); // TA-DA!!!
    }
};

答案 1 :(得分:1)

  

但由于这两种方法不共享任何代码,因此这样做是否明智   它?

如果通过operator()生成输出的方式不是通过这两种机制协同进行的,我认为将它们放在一个类中并不是一个好主意。我会说这违反了单一责任原则a.k.a SRP

相反,请尝试使用Strategy Design Pattern将功能分配到两个单独的策略类中。然后可以在策略上模板化您的课程

答案 2 :(得分:1)

如果MathFunction没有任何常见的实现,它的用途是什么?

如果其目的是为两个实现提供通用接口,请考虑使用它的位置:

  1. 在非模板化(或非依赖)代码中,其中实现类型可以在运行时合理地变化
  2. 在模板化代码中,可能合理地依赖于MathFunction的类型,或者代码中始终静态地知道它使用的是哪种类型

  3. 在案例1中,使用策略模式之类的东西是合理的。 MathFunction可以是调度到虚拟策略的静态类型包装器,也可以编写抽象的MathFunction基类,并实例化两个具体子类中的一个。

    dasblinkenlight 已经演示了策略版本,所以这里是ABC等价物(请注意它如何使用工厂函数来隐藏实现细节)

    // public header
    class MathFunction {
    public:
        virtual ~MathFunction() {}
        virtual double operator() (double radius, double angle) = 0;
    };
    
    MathFunction* make_function(Functor& function);
    MathFunction* make_function(Mat<double> interpolationValues);
    
    // implementation
    class FunctorMathFunction : public MathFunction {
        Functor& _function;
    public:
        FunctorMathFunction(Functor& function) : _function(function) {}
        virtual double operator() (double radius, double angle) {
            return _function(radius, angle);
        }
    };
    class TableMathFunction : public MathFunction {
        Mat<double> _interpolationValues;
    public:
        TableMathFunction (Mat<double> interpolationValues) 
        : _interpolationValues(interpolationValues) {}
        virtual double operator() (double radius, double angle) {
            return ...; // Use _interpolationValues to do calculation
        }
    };
    
    MathFunction* make_function(Functor& function) {
        return new FunctorMathFunction(function);
    }
    MathFunction* (Mat<double> interpolationValues) {
        return new TableMathFunction(interpolationValues);
    }
    

    在案例2中,模板化MathFunction是一个不错的选择:您可以(部分地)对其进行专门化,或者使用策略模式的编译时等效项,即在Policy类上参数化MathFunction,它委托评估。如果考虑性能,保存虚拟函数调用(并启用内嵌)可能很有用。

    template <typename EvaluationPolicy>
    class MathFunction
    {
        EvaluationPolicy eval;
    public:
        MathFunction() {}
        MathFunction(EvaluationPolicy const &ep) : eval(ep) {}
    
        double operator() (double radius, double angle) {
            return eval.calc(radius, angle);
        }
    };
    
    class FunctionPolicy
    {
        std::function<double(double,double)> func;
    public:
        FunctionPolicy(std::function<double(double,double)> f) : func(f) {}
        double calc(double r, double a) { return func(r, a); }
    };
    
    class TablePolicy
    {
        Mat<double> mat;
    public:
        TablePolicy(Mat<double> values) : mat(values) {}
        double calc(double r, double a) { return ....; }
    };
    

    使用策略而不是编写两个不同的具体类(FunctionMathFunctionTableMathFunction)的好处在于显示它们之间的关系,共享的任何代码取决于政策,以及将不相关的问题分成单独的政策的能力。

答案 3 :(得分:0)

我不确定您的代码的模板版本是否真的是对设计的改进。正如你所指出的那样,主要问题是你在这里有两个已完成的不同机制,这些机制被捆绑在一起成为一个类。使模板与模板一起使用的一种方法是,您实际上创建了两个专用模板,其中一个是采用函数的案例的专门化,另一个是采用表格。我不相信这是正确的机制。

除非您可以通过分析验证您绝对肯定无法忍受虚函数调用的开销,否则我会选择运行时多态而不是编译时多态,并将您的MathFunction类转换为提供纯虚operator(),然后从中导出两个实现,一个处理函数案例,另一个处理表格案例。

答案 4 :(得分:0)

你可以在这里使用多态:

class MathFunction
{
private:

    class Impl
    {
    public:

       virtual ~Impl() {}
       virtual double compute(double radius, double angle) = 0;
    };

    class FunctorImpl : public Impl
    {
    public:

       explicit FunctorImpl(Functor & function);
       double compute(double radius, double angle);
    };

    class MatImpl : public Impl
    {
       public:

       explicit MatImpl( Mat<double> interpolationValues);
       double compute(double radius, double angle);
    };

    std::unique_ptr< Impl > impl;

public:

    MathFunction(Functor& function) : impl( new FunctorImpl( function ) ) {}
    MathFunction(Mat<double> interpolationValues) : impl( new MatImpl( interpolationValues ) ) {}

    double operator()(double radius, double angle)
    {
       return impl->compute( radius, angle );
    }
};