应用自定义过滤器

时间:2016-11-22 04:27:01

标签: c++ design-patterns template-meta-programming

假设有一个Foo类,它使用F1,F2,F3函数定义其行为。很少有客户端可以扩展Foo的行为并根据需要对其进行自定义。

对于这种情况,它直接将其实现为

class Foo
{
public:
virtual void F1() { // default behavior }
virtual void F2() { // default behavior }
virtual void F3() { // default behavior }
}


class CustomFoo1 : public Foo
{
public:
void F1() override { // customized behavior }
void F11() { // CustomFoo1's own methods }
}


class CustomFoo2 : public Foo
{
public:
void F1() override { // customized behavior }
void F21() { // CustomFoo2's own methods }
void F22() { // CustomFoo2's own methods }
}

右?但是,只有当我的库为其他客户端公开Class Foo以创建CustomFoo1和CustomFoo2时,这才有可能。我不想公开我的具体实现,而是使用其他人可以使用的界面。所以我可以改变我的实现:

interface IFoo // Public interface
{
    public:
    virtual void F1() = 0;
    virtual void F2() = 0;
    virtual void F3() = 0;
}

Class Foo : public IFoo // Internal to library
{
    public:
    virtual void F1() override { // default behavior }
    virtual void F2() override { // default behavior }
    virtual void F3() override { // default behavior }
}

static IFoo* CreateFoo() { return new Foo(); }

class CustomFoo1 : public IFoo
{
public: 
   void F1() override { // Custom Behavior }
   void F2() override { pFoo->F2(); // Need default so delegate call to default implementation }
   void F3() override { pFoo->F3(); // Need default so delegate call to default implementation }    
   void F11() { // personal own implementation }
private:
IFoo* pFoo;
} 

CustomFoo2也是如此。

但是在这里,如果你看到,我能够隐藏我在接口背后的具体实现,但这导致我的客户实际实现整个接口并委托从库中调用默认实现,如果自定义类不想覆盖默认行为中的任何内容。所以我也不认为这是一个很好的解决方案。

这让我想到下一个可能的解决方案。如果你仔细看看它,图书馆的客户真的只想定制F1()。 所以我声明另一个接口说IFooCustomizePolicy

interface IFooCustomizePolicy
{
public:
virtual void F1Customized() = 0;
}

我将修改IFoo为:

interface IFoo // Public interface
{
    public:
    virtual void F1() = 0;
    virtual void F2() = 0;
    virtual void F3() = 0;
    virtual void RegisterCustomizationPolicy(IFooCustomizePolicy* pPolicy) = 0;
}

将具体实施更新为:

Class Foo : public IFoo // Internal to library
{
    public:
    void F1() override 
    { 
          if( pPolicy != nullptr ) 
             pPolicy->F1Customized();
          else
             // default behavior 
    }
    void F2() override { // default behavior }
    void F3() override { // default behavior }
    void RegisterCustomizationPolicy(IFooCustomizePolicy* pPolicy) override 
      { this->pPolicy = pPolicy; }

private:
IFooCustomizePolicy* pPolicy; 
}

其他客户将被修改为:

class CustomFoo1 : public IFooCustomizePolicy
{
public:
   void F1Customized() override { // customized behavior }
   void F11() { //CustomFoo1's own methods  }
}

现在,如果您看到,通过这种方法,我可以隐藏我的具体类,但能够为客户端提供默认行为以及自定义它的方式。

现在假设,需求更改和新客户端出现并要求新方法可自定义。在这种情况下,我必须更新可接受的Policy Interface,这是可以接受的。但与此同时,我需要做的是更新Foo的相应方法以尊重策略,并在每次将它们添加到策略时向方法添加逻辑。

if( policy present ) then call policy customized method 
else continue with default behavior 

如果你看到这里,我真正想要的是一种过滤器,如果我的对象有策略,那么过滤器应该调用策略方法,不应该继续默认实现。但我不确定应用什么模式以及如何应用?

2 个答案:

答案 0 :(得分:0)

你倾向于的是"组成而不是继承" - 这通常是一件好事。

您使用默认方法遇到的痛苦是因为您没有采取足够的措施。

您可以避免将Foo中的默认方法设置为始终 F1Policy,但最初会将其设置为默认策略。

class Foo : public IFoo {
public:
  void F1() override { 
     f1Policy->f();
  }
  void F2() override {
     f2Policy->f();
  }

  void setF1Policy( const shared_ptr<F1Policy> &newPolicy ) {
    f1Policy = newPolicy;
  }

protected:
  shared_ptr<F1Policy> f1Policy(new DefaultF1Policy);
  shared_ptr<F2Policy> f2Policy(new DefaultF2Policy);
  ...
};

现在通常你不会在默认策略中存储状态 - 这将允许你重用一个实例。因此,构造将变得像:

shared_ptr<F1Policy> f1Policy = DefaultF1Policy.instance;
shared_ptr<F1Policy> f2Policy = DefaultF2Policy.instance;

您可能遇到的下一件事是,策略需要他们的f()能够调用Foo类的F2()等。您可以通过将额外的IFoo作为参数传递给其f()函数来实现此目的。

struct F1Policy {
   virtual void f(IFoo * foo)=0;
};

struct DefaultF1Policy : public F1Policy {
   void f(IFoo * foo) override {
      foo->F2();
   }
}; 

void Foo::F1() { 
  f1Policy->f(this);
}

答案 1 :(得分:0)

您可以为客户端提供不同的基类,而不是让客户端自己实现委派,而是默认情况下将调用转发给相应的实现。这样,您的客户只需要覆盖他们需要的功能。看看下面

#include <memory>
#include <iostream>

// expose this to your clients
struct interface {

    virtual void f1() = 0;
    virtual void f2() = 0;
    virtual void f3() = 0;

    virtual ~interface() = default;

};

// this is your default implementation
struct default_implementation
: public interface {

    void f1() override {std::cout << "default f1" << std::endl;}
    void f2() override {std::cout << "default f2" << std::endl;}
    void f3() override {std::cout << "default f3" << std::endl;}
};

// this is a way for your client to create a pointer to your default implementation
std::shared_ptr<interface> create_default_implementation() {
    return std::make_shared<default_implementation>();
}

// expose this to your clients too
// this is the class your clients will inherit from
// this essentially uses the decorator pattern and ensures that by default all f1, f2, f3 delegate to the appropriate implementation
class client_base
: public interface {

    std::shared_ptr<interface> m_impl;

public:

    client_base(std::shared_ptr<interface> default_impl = create_default_implementation())
    : m_impl(default_impl){}

    void f1() override {
        m_impl->f1();
    }

    void f2() override {
        m_impl->f2();
    }

    void f3() override {
        m_impl->f3();
    }
};

// now your clients can override whichever functions they feel like
class client_foo1
: public client_base {

public:
    using client_base::client_base;

    void f1() override {std::cout << "foo1 f1" << std::endl;}
    void f11() {/* put whatever here */ }
};


class client_foo2
: public client_base {

public:
    using client_base::client_base;

    void f2() override {std::cout << "foo2 f2" << std::endl;}
    void f12() {/* put whatever here */ }
};

int main() {

    client_foo1 foo1;
    client_foo2 foo2;

    foo1.f1(); // prints foo1 f1
    foo1.f2(); // prints default f2
    foo1.f3(); // prints default f3

    foo2.f1(); // prints default f1
    foo2.f2(); // prints foo2 f2
    foo2.f3(); // prints default f3

}