【问题标题】:Decorator pattern: how can I call(back) some members of decorator wrappers from the core class装饰器模式:如何从核心类调用(返回)装饰器包装器的一些成员
【发布时间】:2017-01-04 14:33:51
【问题描述】:

我正在按照here 中的示例实现装饰器模式:

  • Class I:是接口类,core类和decorator base类都通用
  • A:是核心类
  • D:是装饰器基类
  • XYZ:继承自装饰器基类并动态扩展核心类的功能

A (A::endTraining()) 类中有一种方法,它在计时器变量结束时触发(也存在于A 类中)。此方法需要调用XYZ 类的某个成员。
可能吗?这是好习惯吗?怎么样?

例如,创建一种机制来在A 类中注册 pointers-to-XYZ::endTraining 是一种正确的方法吗?

(仅显示相关位)

typedef void (D::*pCallback_fn)(void);

class I 
{
  public:
    virtual void endTraining() = 0;
    virtual void regTrainingCallbacks(pCallback_fn ptrFn) = 0;
};

class A: public I {
  public:

    void endTraining() {
        //do stuff
        //then do stuff in D (and its derivatives)
        //   by iterating through fnList
    }
    void regTrainingCallbacks(pCallback_fn ptrFn) 
    { 
        fnList.push_back( ptrFn ); 
    }
  private:
    std::list<pCallback_fn> fnList;
};

class D: public I {
  public:
    D(I *inner) {
        m_wrappee = inner;
    }
    void regTrainingCallbacks(pCallback_fn ptrFn) 
    { 
        m_wrappee->regTrainingCallbacks(ptrFn); 
    }
  private:
    I *m_wrappee;
};

class X /*,Y,Z*/ : public D {
  public:
    X(I *core): D(core)
    {
        D::regTrainingCallbacks( this->*endTraining() ); //
    }
  private:
    void endTraining(){
      //do stuff when called by A::endTraining() through D
    }
};

可以做些什么呢?

【问题讨论】:

  • 你还需要一个对象D来调用fnList中注册的方法。实际上,您可以通过添加另一个将由D 的构造函数调用的虚拟方法virtual void regDecorator(D* decorator) 在类A 中注册所有装饰器。该方法将在A中通过在fnList之外的字段中注册来实现。那么A::endTraining就可以在所有装饰器上调用fnList的所有回调方法。如果您只想通知 X 装饰器或已注册回调的 X,可能会有所不同。
  • 为什么不使用std::functions 列表而不是指向成员方法的指针列表?
  • @Franck regDecorator 似乎是个好主意,但是我必须检查 XYZ 是否都有“endTraining()` 方法。我正在寻找有关如何做到这一点的信息,但找不到任何. 是不是像测试null一样简单?
  • @skypjack 如果我最终没有通过装饰器本身,那么我会:)
  • 事实上,如果regDecorator的调用在X构造函数中,你只能注册具有endTraining方法的装饰器。 skypjack的解决方案是因为它可以通过调用D::regTrainingCallBacks(std::bind(&amp;X::endTraining, this))之类的方式将回调函数关联到对象。

标签: c++ callback decorator


【解决方案1】:

解决原始设计中的一个错误,其中“培训师”(注册培训回调的实体)本身必须是一个回调(通知器 - 前类 A - 是否有任何理由必须是一个回调本身? )。

我更改了班级名称以证明他们的职责。

MainTrainingProcess 替换了原始的 class A(其中的实例将被 D-es 包装)和 D 本身。

class EndTrainingListener
{
public:
  virtual ~EndTrainingListener() { }

  virtual void endTraining()=0;
};

class ITrainingProcess
{
public:
  virtual ~ITrainingProcess() { }
  virtual void regTrainingCallbacks(EndTrainingListener* callback) = 0;
};

class MainTrainingProcess : public ITrainingProcess {
public:
  virtual ~MainTrainingProcess() {
    // destroy other resources used during training
  }

  virtual void regTrainingCallbacks(EndTrainingListener* callback) {
    this->listeners.push_back(callback);
  }

  void train() {
    // do training stuff
    // ...
    // do my specific actions at the end of training
    // ...

    // finish by notifying all listeners
    this->atEndTraining();
  }
protected:
  void atEndTraining() {
    for(auto l : this->listeners) {
      l->endTraining();
    }
  }

  std::list<EndTrainingListener*> listeners;
};

class X /*Y, Z*/ : public EndTrainingListener {
public:
  virtual ~X();

  virtual void endTraining() {
    // do specific stuff
  }
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-19
    • 1970-01-01
    • 2020-02-10
    • 2012-02-29
    • 2021-06-24
    • 2023-03-13
    • 2013-05-07
    相关资源
    最近更新 更多