【问题标题】:Design pattern to apply Customization Filter应用自定义过滤器的设计模式
【发布时间】:2016-11-22 04:27:00
【问题描述】:

假设有一个类 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  }
}

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

现在让我们说,需求变化和新客户来了,并要求新的方法是可定制的。在这种情况下,我将不得不更新策略接口,这没关系,这是可以接受的。但除此之外,我需要做的是更新 Foo 的相应方法以尊重策略,并在每次将它们添加到策略时将以下逻辑添加到方法中。

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

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

【问题讨论】:

  • 我觉得桥接设计模式在这里可以很好地工作,这样接口就可以与实现分离。

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


【解决方案1】:

您倾向于“组合而不是继承”——这通常是一件好事。

您在使用默认方法时遇到的痛苦是因为您没有走得足够远。

您可以通过always 使用F1Policy 来避免将默认方法放入Foo,但最初将其设置为默认策略。

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);
}

【讨论】:

    【解决方案2】:

    您可以为您的客户提供一个不同的基类来继承,而不是让客户端自己实现委托,默认情况下它将调用转发到适当的实现。这样,您的客户只需覆盖他们需要的功能。往下看

    #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
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-01-01
      • 2021-01-13
      • 1970-01-01
      • 2019-03-08
      • 2016-07-31
      • 2014-02-01
      • 1970-01-01
      相关资源
      最近更新 更多