【问题标题】:design pattern for initializing algorithms which each accept different parameters用于初始化每个接受不同参数的算法的设计模式
【发布时间】:2011-10-08 07:46:57
【问题描述】:

我正在寻找重新设计我拥有的库。
目前我有一套算法,它们之间没有任何关系。

我的目标是让所有具体算法实现一个大致由Init()Run(In,Out) 组成的接口,以便我可以按以下方式执行它们:

void Process()
{
    // IAlgotrithm* algX = new CAlgX(); X:{A,B,C}
    IAlgorithm* algs[] = { algA, algB, algC }; 
    for (int i=0; i < 3; i++) {
        algs[i]->Init(...);
        algs[i]->Run(In,Out);     
    }
 }

问题是,目前每个算法都接受不同的结构体作为其输入参数。
例如,CAlgA 有一个方法Init(CAlgAParameter param),其中CAlgAParameter 是一个结构,其中包含特定于 AlgA 的字段。每个算法都有自己的结构,由不同类型的完全不同的字段组成。 解决这个问题的最佳方法是什么?

我能想到的一个选项是拥有一个包含与所有算法相关的所有参数的映射,并将初始化输入结构的工作委托给算法。也就是说,算法的客户端不会知道特定的输入结构是什么样子的——算法的工作是根据地图中的所有全局参数创建其输入结构。我不喜欢这个想法,因为它无法在编译时检查并且太容易出错。

解决这个问题的最佳方法是什么?
谢谢

【问题讨论】:

  • 您是在问如何以通用方式定义IAlgotrithm::Init 吗? algAalgB的类型是什么?
  • 为什么不用参数构造算法呢?统一初始化的目标是什么?似乎负责 uniform 初始化/运行的代码不应该知道初始化细节。
  • @iammilind 是的,我想以通用方式定义 Init。 algX 是派生自 IAlgorithm 的具体类。 @user396672 我可以用某些参数构造它们,但是每次运行时我都需要给它们特定的参数

标签: c++ design-patterns


【解决方案1】:

为什么不在构造的时候初始化,直接在循环中调用Run()呢?因此,在构建时,您可以让用户负责为给定算法提供正确的配置,即

IAlgorithm* algs[] = { new CAlgA(CAlgAParameter(...)), new CAlgB(CAlgBParameter(...)), new CAlgC(CAlgCParameter(...)) }; 

//loop

使用异常处理算法的错误配置。

【讨论】:

  • 每次运行算法管道时,“init”参数都会发生变化。我可以像你提到的那样初始化它们,但我仍然需要一种方法来在每次迭代时以通用方式调用 Init
  • 哪个迭代?每次调用管道还是每次调用管道中的算法?如果是前者,是不是每次建设都太重了?如果是后者,如果 init 在构造过程中发生,为什么需要这样做?
  • 你有初始化参数和控制参数。使用 Init 参数创建一次算法。那么每次调用Run之前,都需要设置Control参数。
  • 还是看不到 - Run() 在管道中的每个算法上都被调用,为什么不在构造中传递这些控制参数呢?或者它们是否在运行时被每个算法修改?因此后续的算法需要修改参数?这些都不是你原来的问题......
  • 是的。控制参数每次都会修改,因此每次运行算法时都需要设置。我不想在问题中涉及太多细节,因此没有写参数类型的差异。但是,从问题中可以理解,您需要在运行算法之前每次调用Init
【解决方案2】:

对于不会被任何算法改变的静态参数,基于构造函数的初始化就可以了。每次算法运行必须提供的控制参数可以由每个算法的接口(或具有所有抽象方法的抽象类)指定。

public class IAlg1ControlParams {
public: 
    virtual ~IAlg1ControlParams () {}
    virtual int getParam1() = 0;
    virtual void setParam2(char val) = 0;
}

这将使每次调用的算法占用空间尽可能小。这些接口对于测试您的算法也很有用,因为您只需要在测试中提供这两个接口的实现,而不是一些带有字符串键的大型参数映射。使用 Google Mock 之类的 Mocking 框架可以做得更好。

public void WhenInputIs10_Algorithm1SetsParam2Tob() {
    Alg1 alg1 = new Alg1("Test data", 10);
    IAlg1ControlParams context = new MockAlg1ControlParams(10);
    alg1.Run(context);
    assert(context.getParam2() == 'b');
}

然后,您可以拥有一个上下文或状态对象,为需要执行的每组算法实现接口。这将确保您在编译时检查参数字段和方法是否存在。此外,如果您需要数据从一种算法流向另一种算法,则使用相同的上下文对象将确保将更新的数据提供给管道中的后续算法。

public class AlgSet1Context: public IAlg1Context, public IAlg2Context...

在每个算法中,您需要验证控制参数中的值。我建议在这里使用模板方法模式,这样您就有了一个基本业务规则,它提供了Run(ControlParams) 的非虚拟实现,并调用了两个受保护的虚拟方法Verify(ControlParams)Execute(ControlParams)。这将确保所有在以后实施新算法的开发人员都知道他们必须验证控制参数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-31
    • 2021-01-06
    • 1970-01-01
    相关资源
    最近更新 更多