扩展设计模式以解决实际问题:解耦参数不断变化,公式相对稳定的逻辑。

在数值模拟编程过程中,会遇到这样一个问题:有些参数或者物理量的值通过其他参数或者变量通过各种运算得到,如果这些参数和变量的值都是常量,一旦被初始化我们就不会再改变它,那么问题变得非常简单,例如:

const double pai = 3.1415926;
...
double variable = 2 * pai + 0.05;

但是很多时候变量variable依赖的参数或者值是一个变量,它的取值往往会因为各种因素变化,当程序运行时这些变量被不断的修改,并且这些修改是根据你调用时刻的上下文所决定,这意味着每次你需要用这些参数和值导出一个新的变量时,你都要显式的获取每个可能改变的参数的当前值。例如:
double s1;//s1是随着程序运行时间变化的变量。
double s2;//s2是随着当前实体三维空间坐标变化的变量。
double s3;//s3是随着当前实体三维空间坐标以及该处温度值变化的变量。
这时候有一个物理量由这三个变量导出:
double s4 = s1 6.24 + (s2 + s1) s3;
当你每次要使用s4时,你需要做什么:
s1 = s1_change_from_time();
s2 = s2_change_from_xyz(x,y,z);
s3 = s3_change_from_xyzt(x,y,z,t);
...
s4 = s1 / 6.24 + (s2 + s1) * s3;
如果还有变量依赖于s4呢?这样的累加会让你编写程序过程中不断重复同样的逻辑,极易出错,如果将来扩展程序,s4的求值新添加了扩展项,或者s2的改变不仅由xyz坐标决定,而且增添了新的影响变量,这些都意味着你要修改每一段重复的逻辑。
设计模式的意义就是逻辑复用代码复用,以增加系统的健壮性,可维护性和可扩展性。本文的目的就是提出一种设计模式(或者说一个框架)解决这个问题,达到解耦变量更新的逻辑和变量运算的逻辑,这意味着不必每次需要s4时都因为不得不跟新其依赖变量而重写其计算过程,减少出错概率。


首先用树的结构保留变量的运算逻辑:
s4 = s1 + s3 * s2--->

 s4 =            +
                / \
               s1  *
                  / \
                 s3 s2

见如下代码:基类node_是表示运算、变量和常量所有类的基类,类plus_、sub_、multi_和divi_是代表+-*/四个二元运算的类,flip是代表一元运算取负的类。

#include 
using std::shared_ptr;

template
class node_ {
public:
  node_() = default;
  virtual ~node_() {

  }

  virtual double getValue() const = 0;
  virtual void update(const T& t) = 0;
};

template
class op_ : public node_ {
public:
  op_(shared_ptr> l,shared_ptr> r):left_(l),right_(r) {}
  virtual ~op_() = default;
  virtual void update(const T& t) override {
    left_->update(t);
    right_->update(t);
  }

protected:
  shared_ptr> left_;
  shared_ptr> right_;
};

template
class plus_ : public op_ {
public:
  plus_():op_(nullptr,nullptr) {}
  plus_(shared_ptr> left,shared_ptr> right): op_(left,right) {}
  virtual ~plus_() = default;
  virtual double getValue() const override {
    return this->left_->getValue() + this->right_->getValue();
  }
};

template
class sub_ : public op_ {
public:
  sub_() : op_(nullptr,nullptr) {}
  sub_(shared_ptr> left, shared_ptr> right) : op_(left, right) {}
  virtual ~sub_() = default;
  virtual double getValue() const override {
    return this->left_->getValue() - this->right_->getValue();
  }
};

template
class multi_ : public op_ {
public:
  multi_() : op_(nullptr,nullptr) {}
  multi_(shared_ptr> left, shared_ptr> right) : op_(left, right) {}
  virtual ~multi_() = default;
  virtual double getValue() const override {
    return this->left_->getValue() * this->right_->getValue();
  }
};

template
class divi_ : public op_ {
public:
  divi_() : op_(nullptr,nullptr) {}
  divi_(shared_ptr> left, shared_ptr> right) : op_(left, right) {}
  virtual ~divi_() = default;
  virtual double getValue() const override {
    return this->left_->getValue() / this->right_->getValue();
  }
};

template
class flip_ : public node_ {
private:
  shared_ptr> child_;

public:
  flip_() : child_(nullptr) {}
  flip_(shared_ptr> c):child_(c){}
  virtual ~flip_() = default;
  virtual void update(const T& t) override {
    this->child_->update(t);
  }
  virtual double getValue() const override {
    return 0.0 - this->child_->getValue();
  }
};

constant_代表常量类,该类的对象在调用update时候不会发生任何事,并且该类的构造函数仅接受一个double类型的值,这意味着constant_的表现与我们平常意义上理解的常量一致。variable_代表变量类,我们注意该类的构造函数,其接受一个reflect_类型的指针。这里的reflect_是一个虚基类,而就是该类的派生类真正实现了“变量”这一概念,变量不是一个确切的值,根据具体情况,变量是一个接受某种输入(或者说上下文),输出一个值的函数。在variable_的update函数中,就是根据其reflect_from函数得到该变量在t情况下的确切值。而这个一直跟着我们的模板类型T是干嘛的也就明白了,该类型的对象代表了变量相关的上下文

template
class value_ : public node_ {
protected:
  double v_;

public:
  value_(double v):v_(v) {}
  virtual ~value_() {

  }
  virtual double getValue() const override {
    return v_;
  }
};

template
class constant_ : public value_ {
public:
  constant_(double v):value_(v) {}
  virtual ~constant_() {}
  virtual void update(const T& t) override {
    ;
  }
};

template
class reflect_ {
public:
  virtual double reflect_from(const T& t) = 0;
};

template
class variable_ : public node_ {
private:
  shared_ptr> ptr_;
  double now_v_;

public:
  explicit variable_(reflect_* ptr):ptr_(ptr),now_v_(0.0) {}
  double getValue() const override {
    return now_v_;
  }
  void update(const T& t) override {
    now_v_ = ptr_->reflect_from(t);
  }
  virtual ~variable_() {

  }
};

举个例子,如果某个变量是根据三维空间的坐标在变换,那么我们要将T替换为如下类型:

struct position {
double x;
double y;
double z;
}

接下来是第三部分,类coeff通过重载运算符,实现了coeff和coeff对象在运算过程中构建了代表运算的树结构。一个coeff对象内含一个shared_ptr> root_对象,该对象要么是一个变量,要么是一个常量,要么是一个代表运算的类。

template
class coeff {
public:
  coeff():root_(nullptr) {}
  coeff(shared_ptr> v):root_(v) {}
  coeff(const coeff& other) = default;
  coeff(coeff&& other) = default;
  coeff& operator=(const coeff& other) = default;
  coeff& operator=(coeff&& other) = default;

  coeff operator+(const coeff& other) const {
    coeff result;
    shared_ptr> tmp(new plus_(root_, other.root_));
    result.root_ = tmp;
    return result;
  }

  coeff operator-(const coeff& other) const {
    coeff result;
    shared_ptr> tmp(new sub_(root_, other.root_));
    result.root_ = tmp;
    return result;
  }

  coeff operator*(const coeff& other) const {
    coeff result;
    shared_ptr> tmp(new multi_(root_, other.root_));
    result.root_ = tmp;
    return result;
  }

  coeff operator/(const coeff& other) const {
    coeff result;
    shared_ptr> tmp(new divi_(root_, other.root_));
    result.root_ = tmp;
    return result;
  }

  coeff operator-() const {
    coeff result;
    shared_ptr> tmp(new flip_(root_));
    result.root_ = tmp;
    return result;
  }

  double getValue() const {
    return root_->getValue();
  }

  void update(const T& t) {
    root_->update(t);
  }

private:
  shared_ptr> root_;
};

template
shared_ptr> make_variable(Types&&... args) {
  return shared_ptr>(new variable_(new ref(std::forward(args)...)));
}

template
shared_ptr> make_constant(double v) {
  return shared_ptr>(new constant_(v));
}

最后看一个example例子:

#include "expression.h"
#include 

struct position {
  double x;
  double y;
  double z;
  double t;
  position(double xx, double yy, double zz,double tem) :x(xx), y(yy), z(zz),t(tem) {

  }
};

class tem : public reflect_ {
public:
  tem() = default;
  double reflect_from(const position& p) override {
    return p.x + p.y * 0.2 + p.z * 0.2;
  }
};

using co = coeff;

int main() {
  auto v1 = make_variable();
  auto v2 = make_constant(2);
  co c = co(v1) + co(v2) * co(v1);
  c = c * co(v1);
  c.update(position(2, 3, 4, 2.4));
  std::cout << c.getValue();
  system("pause");
  return 0;
}

注意:这里的position类就是代替上述中的模板类型T的类型,当update的时候输入一个该类型的对象,并在内存中代表表达式的树中依次调用update函数,树中的运算符类对象会将该对象传递给其左右子表达式的update函数,而常量类对象不做任何处理,变量类对象通过构造时候传入的reflect对象调用reflect_from(const position& p)得到具体的值。
这里的tem类就是reflect类的一个派生类,这里它代表了三维空间中随着空间变化的温度值。
以上,当变量c被构造后,任何时候需要获取其具体值时,只要调用update函数并传入代表当前情况的position对象,就会完成所有变量的更新。然后调用getValue函数获得当前值。
这种方法类似于设计模式中的观察者模式但又不完全相同。我把它称为状态-反馈模式。

你可能感兴趣的:(设计模式,c++)