C++ 在非虚拟接口习惯用法中添加不变量
假设我使用NVI习惯用法具有以下层次结构:C++ 在非虚拟接口习惯用法中添加不变量,c++,design-patterns,idioms,template-method-pattern,non-virtual-interface,C++,Design Patterns,Idioms,Template Method Pattern,Non Virtual Interface,假设我使用NVI习惯用法具有以下层次结构: class Base { public: virtual ~Base() {} void foo() { cout << "Base::foo" << endl; foo_impl(); } private: virtual void foo_impl() = 0; }; class A : public Base { private:
class Base
{
public:
virtual ~Base() {}
void foo() { cout << "Base::foo" << endl; foo_impl(); }
private:
virtual void foo_impl() = 0;
};
class A : public Base
{
private:
virtual void foo_impl() { cout << "A::foo_impl" << endl; }
};
类基
{
公众:
虚拟~Base(){}
void foo(){cout在我的理解中,NVI是一种防止/阻止向非虚拟基方法添加不变量的方法,因此,此时您想要添加不变量的事实表明NVI或者根本不是您想要的模式,或者您可能想要重新构造您的设计,以便不需要添加此类不变量
也就是说,除了简单地将以前的非虚拟界面虚拟化之外,还可以使用C++11中的最后一个关键字:
class Base
{
public:
virtual ~Base() {}
virtual void foo() { base_foo(); foo_impl(); }
protected:
void base_foo() { cout << "Base::foo" << endl; }
virtual void foo_impl() = 0;
};
class SpecialBase : public Base
{
public:
virtual void foo() final // note the use of 'final'
{ base_foo(); specialbase_foo(); foo_impl(); }
protected:
void specialbase_foo() { cout << "SpecialBase::foo" << endl; }
};
class B : public SpecialBase
{
private:
virtual void foo_impl() { cout << "B::foo_impl" << endl; }
};
类基
{
公众:
虚拟~Base(){}
虚拟void foo(){base_foo();foo_impl();}
受保护的:
void base_foo(){cout这篇文章是向我建议的,类似于我前几天浏览的与NVI相关的内容,因此necro
我建议在基类中添加一个检查添加机制,这样派生类就可以添加需求。只要可以使用基类访问函数测试需求,这是一种非常简单的方法,否则您的特殊MyInvariant类必须动态转换doCheckInvariantOK()的基参数让不变量工作
编辑:我理解“不变量”与foo()的前置和后置条件一致,就像在正式验证中一样。如果您想在base_foo()之前和/或之后添加功能,我认为您实际上在追求什么,您可以用类似的方式来做
class Base
{
public:
virtual ~Base() {}
void foo()
{
cout << "Base::foo" << endl;
//Can use invariants as pre and/or postconditions for foo_impl
for(const std::unique_ptr<InvariantBase>& pInvariant : m_invariants)
{
//TODO cout << "Checking precondition " << pInvariant->GetDescription() << endl;
if(!pInvariant->CheckInvariantOK(*this))
{
//Error handling
}
}
foo_impl();
}
protected:
void AddInvariant(std::unique_ptr<InvariantBase>&& pInvariant)
{
m_invariants.push_back(std::move(pInvariant));
}
struct InvariantBase
{
bool CheckInvariantOK(const Base& base)
{
return doCheckInvariantOK(base);
}
private:
virtual bool doCheckInvariantOK(const Base& base) = 0;
};
private:
std::list<std::unique_ptr<InvariantBase>> m_invariants;
virtual void foo_impl() = 0;
};
class A : public Base
{
private:
virtual void foo_impl() { cout << "A::foo_impl" << endl; }
};
class SpecialBase : public Base
{
public:
SpecialBase()
: Base()
{
AddInvariant(std::unique_ptr<MyInvariant>(new MyInvariant() ) );
}
private:
void foo_impl() { cout << "SpecialBase::foo" << endl; bar_impl(); }
virtual void bar_impl() = 0;
struct MyInvariant : public InvariantBase
{
virtual bool doCheckInvariantOK(const Base& base) override
{
//TODO: special invariant code
}
};
};
class B : public SpecialBase
{
private:
virtual void bar_impl() { cout << "B::bar_impl" << endl; }
};
类基
{
公众:
虚拟~Base(){}
void foo()
{
cout“因为我不想为添加到层次结构中的每个派生基添加方法(使用不同的名称)”我不确定这是否是一个坏主意。毕竟,你想要添加不变量,即派生类也需要维护这些新的不变量。考虑到你的编辑,我几乎想知道装饰器模式是否会为你提供你想要的自定义层。我理解你的观点,而且很可能NVI不是我想要的但是,我正在寻找一种更通用的习惯用法,而不仅仅是有一个可选的定制点……在我的层次结构中,SpecialBase是必需的,它不仅覆盖了foo()的实现。也可能有从SpecialBase派生的基类会再次重新实现foo()…所以我真的在寻找一种更通用的设计模式。我编辑了我的问题,以添加我正在寻找的真实工作流。有趣的是,我个人会选择一个std::vector
:然后你就可以将不变量注册为lambda表达式,它可以从派生构造函数中捕获它们的上下文,从而消除对downca的需要sting.@Quentin:你能详细说明一下吗?lambda表达式如何知道它在doCheckInvariantOK()中的构造上下文?我的意思是以AddInvariant([]{return/*check*/;})结尾
内置SpecialBase::SpecialBase
。lambda可以使用正确的静态类型捕获此
,然后通过std::function
而不是OO多态性来完成类型擦除。
Base <- A
<- B
<- SpecialBase <- C
<- D
<- VerySpecialBase <- E
<- StrangeBase <- F
class Base
{
public:
virtual ~Base() {}
virtual void foo() { base_foo(); foo_impl(); }
protected:
void base_foo() { cout << "Base::foo" << endl; }
virtual void foo_impl() = 0;
};
class SpecialBase : public Base
{
public:
virtual void foo() final // note the use of 'final'
{ base_foo(); specialbase_foo(); foo_impl(); }
protected:
void specialbase_foo() { cout << "SpecialBase::foo" << endl; }
};
class B : public SpecialBase
{
private:
virtual void foo_impl() { cout << "B::foo_impl" << endl; }
};
class Base
{
public:
virtual ~Base() {}
virtual void foo() { base_foo(); bar_impl(); foo_impl(); }
protected:
void base_foo() { cout << "Base::foo" << endl; }
virtual void bar_impl() {} // bar_impl is an optional point of customization
// by default it does nothing
virtual void foo_impl() = 0; // foo_impl is not optional derived classes
// must implement foo_impl or else they will be abstract
};
class B : public Base
{
private:
virtual void bar_impl() { cout << "SpecialBase::foo" << endl; }
virtual void foo_impl() { cout << "B::foo_impl" << endl; }
};
class Base
{
public:
virtual ~Base() {}
void foo()
{
cout << "Base::foo" << endl;
//Can use invariants as pre and/or postconditions for foo_impl
for(const std::unique_ptr<InvariantBase>& pInvariant : m_invariants)
{
//TODO cout << "Checking precondition " << pInvariant->GetDescription() << endl;
if(!pInvariant->CheckInvariantOK(*this))
{
//Error handling
}
}
foo_impl();
}
protected:
void AddInvariant(std::unique_ptr<InvariantBase>&& pInvariant)
{
m_invariants.push_back(std::move(pInvariant));
}
struct InvariantBase
{
bool CheckInvariantOK(const Base& base)
{
return doCheckInvariantOK(base);
}
private:
virtual bool doCheckInvariantOK(const Base& base) = 0;
};
private:
std::list<std::unique_ptr<InvariantBase>> m_invariants;
virtual void foo_impl() = 0;
};
class A : public Base
{
private:
virtual void foo_impl() { cout << "A::foo_impl" << endl; }
};
class SpecialBase : public Base
{
public:
SpecialBase()
: Base()
{
AddInvariant(std::unique_ptr<MyInvariant>(new MyInvariant() ) );
}
private:
void foo_impl() { cout << "SpecialBase::foo" << endl; bar_impl(); }
virtual void bar_impl() = 0;
struct MyInvariant : public InvariantBase
{
virtual bool doCheckInvariantOK(const Base& base) override
{
//TODO: special invariant code
}
};
};
class B : public SpecialBase
{
private:
virtual void bar_impl() { cout << "B::bar_impl" << endl; }
};