假设有一个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
如果你看到这里,我真正想要的是一种过滤器,如果我的对象有策略,那么过滤器应该调用策略方法,不应该继续默认实现。但我不确定应用什么模式以及如何应用?
答案 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
}