桥接模式:如何定义调用派生实现的函数class
Bridge pattern: how to define functions that call the implementation of the derived class
我正在研究一种聚合物模拟代码,该代码采用桥接模式来分离抽象(模拟中涉及的对象)及其实现(它们的表示方式,例如每个单体一个粒子、连续网格等)。我面临的一个问题是,我正在努力定义一个 class 抽象层次结构,其中较低的抽象可以执行在层次结构中更高层的其他 classes 上定义的函数。
我想定义一个基础抽象 Monomer
,它包含许多调用 MonomerImpl
实现的方法。比如说,一种方法 applyDisplacement
可以移动 space 中的单体。但是,当我用几个实现 MonomerTypeAImplGrid
、MonomerTypeAImplParticles
等定义派生抽象 MonomerTypeA
时,我想调用 applyDisplacement
,定义在层次结构的顶部 class,而是使用我单体的具体实现class.
这是我尝试过的示例:
class Monomer
{
public:
...
void applyDisplacement(Vector displacement)
{
getImplementation()->applyDisplacement(displacement);
}
private:
std::unique_ptr<MonomerImpl> p_impl;
virtual MonomerImpl* getImplementation();
};
class MonomerImpl
{
public:
...
void applyDisplacement(Vector displacement)
{
for (int i=0; i<positions.size(); i++)
{
positions[i] += displacement;
}
}
private:
std::vector<Vector> positions;
};
并且:
class MonomerTypeA : Monomer
{
public:
...
private:
std::unique_ptr<MonomerTypeAImpl> p_impl;
MonomerImpl* getImplementation()
{
return (MonomerImpl*) p_impl.get();
}
};
class MonomerTypeAImpl : MonomerImpl
{
...
}
如果我使用虚函数getImplementation
函数只能return一个MonomerImpl*
指针,那么我就无法实现任何附加功能的实现。或者,如果我尝试放弃继承并只定义一堆不同的实现,我将有很多代码重复。
有没有两全其美的解决方案?
我的回答将分为代码、代码中的一些注释和此处的文本。
一、代码:
#include <iostream>
#include <memory>
#include <vector>
// Create a pure virtual interface for your MonomerImpls
class MonomerImpl {
public:
virtual ~MonomerImpl() = default; // Required in non-concrete classes
void applyDisplacement(int displacement) {
std::cout << "From pure virtual interface " << displacement << ":\n";
applyDisplacement_Helper(displacement);
}
// Protected so inherited classes can access it freely
protected:
std::vector<int> positions{};
private:
// Helper
virtual void applyDisplacement_Helper([[maybe_unused]] int displacement) = 0;
};
// Public inheritance is what you want a majority of the time
class MonomerTypeAImpl : public MonomerImpl {
public:
MonomerTypeAImpl() = default;
private:
void applyDisplacement_Helper([[maybe_unused]] int displacement) override {
std::cout << "Specialization From Monomer A Impl\n";
}
};
// Second MonomerImpl child to illlustrate
class MonomerTypeXImpl : public MonomerImpl {
public:
MonomerTypeXImpl() = default;
private:
void applyDisplacement_Helper([[maybe_unused]] int displacement) override {
std::cout << "specialization From Monomer X Impl\nYowza\n";
}
};
// If you'll have many Monomers, it's better for the base to be abstract
// In cases where this similar operation is being done, NVI (non-virtual
// interface) can be quite helpful.
//
// I just have a Monomer class, and you can feed different objects different
// MonomerImpls to get the desired behavior
class Monomer {
public:
Monomer(MonomerImpl* impl) : p_impl(impl) {}
void applyDisplacement(int displacement) {
p_impl->applyDisplacement(displacement);
}
private:
std::unique_ptr<MonomerImpl> p_impl;
// I got rid of the get_impl() function, it added an extra step for no gain
};
int main() {
MonomerImpl* monoImpl = new MonomerTypeAImpl();
Monomer mono(monoImpl);
mono.applyDisplacement(42);
std::cout << "\n\n";
Monomer xMono(new MonomerTypeXImpl());
xMono.applyDisplacement(350);
}
输出:
From pure virtual interface:
From Monomer A Impl: 42
From pure virtual interface:
From Monomer X Impl: 350
Yowza
我必须删除您的 Vector
class(未提供),所以我只是将其更改为 int
以便编译。所做的更改并不大,因为大部分内容已经存在。如果您要有许多不同的实现,我的建议是为这些实现创建一个纯虚拟基础 class。它标准化接口并将代码放在具体的(objects 是可声明的)classes 它所属的地方。就像我在评论中所说的那样,如果许多派生的 classes 会做同样的事情,使用 NVI 或 Non-V虚拟 I 接口可以帮助解决这个问题。 NVI 的一个例子在 class MonomerImpl
.
(NVI 背后的想法是基础 class 可调用(public)函数不是虚拟的。基础 class,即使它是抽象的,也可以做那些事情对所有 children 都是通用的,并且 然后 调用一个 protected/private 虚拟函数,允许 children 执行他们的专门工作,并且只执行他们的专门工作. 调用虚函数的顺序自然取决于共同的工作)
根据提供的代码,我完全没有觉得从Monomer
派生有什么收获。如果不同的 MonomerImpl*
还不够,您可能有理由这样做。同样,我建议以抽象基础 class 作为起点。
如果还有问题,我很乐意尝试解答。作为此答案的参考,https://refactoring.guru/design-patterns/bridge
您已经有了想法,但您的实施有点离岸。
If I use a virtual function getImplementation function can only return a MonomerImpl*
pointer, then I can't implement any additional functionality to the implementation
更正:您将能够实现附加功能,但仅限于 MonomerImpl
class。并且虚拟功能也与继承一起工作,因此要使其工作,您必须从基础 class 继承或抽象 class .ie 接口。
问题是您还没有制作连接 Monomer
的各种实现的胶水。 Monomer
和它的实现 classes 之间的这种联系可以使用继承或聚合来实现。但在这种情况下,聚合将不是理想的,因为 "是 Monomer
和它实现之间的" 关系。
例如
#include <iostream>
#include <memory>
class vector
{
};
// design an abstract base class .ie interface with pure virtual functions
// of all the functions that an implementation must define .
// All these functions will be callable from our base class with polymorphism
class monomer_intf
{
virtual auto
do_apply_displacement(vector displacement) -> void
= 0;
};
// monomer is our concrete base class which will help use call our desired
// implementations in the various derived class
class monomer : public monomer_intf
{
// defined because pure virtual functions need to be defined in the derived
// class else the derived class also becomes an abstract class
auto
do_apply_displacement(vector displacement) -> void override
{
// this dispatches to the dynamic class implementation
// ie. the class monmer will point to or reference to
do_apply_displacement(displacement);
}
public:
auto
apply_displacement(vector displacement) -> void
{
// calls the monomer class do_apply_displacement
// which then dispatches to the implementation
do_apply_displacement(displacement);
}
};
// to get our implementation functionality only the virtual or overriden
// function need to be implemented but we can have several functions as pleases
// us
class monomer_impl : public monomer
{
auto
do_apply_displacement(vector displacement) -> void override
{
std::cout << "monomer called do_apply_displacement of monomer_impl \n";
}
};
class monomer_type_a : public monomer
{
auto
do_apply_displacement(vector displacement) -> void override
{
std::cout << "monomer called do_apply_displacement of monomer_type_a \n";
}
};
class monomer_type_a_impl_grid : public monomer_type_a
{
auto
do_apply_displacement(vector displacement) -> void override
{
std::cout << "monomer called do_apply_displacement of "
"monomer_type_a_impl_grid \n";
}
};
class monomer_type_a_impl : public monomer_impl
{
auto
do_apply_displacement(vector displacement) -> void override
{
std::cout
<< "monomer called do_apply_displacement of monomer_type_a_impl \n";
}
};
// if we like this style . It simplifies the creation of the implementations
// and the execution of our need functions
class monomer_abstraction
{
std::unique_ptr<monomer> implementation_;
public:
explicit monomer_abstraction(monomer *inmplementation)
: implementation_{ inmplementation }
{
}
auto
apply_displacement(vector displacement)
{
implementation_->apply_displacement(displacement);
}
};
auto
main() -> int
{
// using stack allocated monomer_type_a_impl_grid
monomer_type_a_impl_grid impl_grid;
monomer &monomer_grid = impl_grid;
monomer_grid.apply_displacement(vector{});
// using heap allocated monomer_impl
auto impl_ = std::make_unique<monomer_impl>();
monomer &monomer = *impl_;
/// using virtual dispach apply_displacement will call the
/// do_apply_displacement of it dynamic type
monomer.apply_displacement(vector{});
// using monomer_abstraction as an abstraction to the implementations
// of need functionalities
auto abstraction
= monomer_abstraction(std::make_unique<monomer_type_a_impl>().release());
abstraction.apply_displacement(vector{});
}
DRY 绝对是正确的选择
我正在研究一种聚合物模拟代码,该代码采用桥接模式来分离抽象(模拟中涉及的对象)及其实现(它们的表示方式,例如每个单体一个粒子、连续网格等)。我面临的一个问题是,我正在努力定义一个 class 抽象层次结构,其中较低的抽象可以执行在层次结构中更高层的其他 classes 上定义的函数。
我想定义一个基础抽象 Monomer
,它包含许多调用 MonomerImpl
实现的方法。比如说,一种方法 applyDisplacement
可以移动 space 中的单体。但是,当我用几个实现 MonomerTypeAImplGrid
、MonomerTypeAImplParticles
等定义派生抽象 MonomerTypeA
时,我想调用 applyDisplacement
,定义在层次结构的顶部 class,而是使用我单体的具体实现class.
这是我尝试过的示例:
class Monomer
{
public:
...
void applyDisplacement(Vector displacement)
{
getImplementation()->applyDisplacement(displacement);
}
private:
std::unique_ptr<MonomerImpl> p_impl;
virtual MonomerImpl* getImplementation();
};
class MonomerImpl
{
public:
...
void applyDisplacement(Vector displacement)
{
for (int i=0; i<positions.size(); i++)
{
positions[i] += displacement;
}
}
private:
std::vector<Vector> positions;
};
并且:
class MonomerTypeA : Monomer
{
public:
...
private:
std::unique_ptr<MonomerTypeAImpl> p_impl;
MonomerImpl* getImplementation()
{
return (MonomerImpl*) p_impl.get();
}
};
class MonomerTypeAImpl : MonomerImpl
{
...
}
如果我使用虚函数getImplementation
函数只能return一个MonomerImpl*
指针,那么我就无法实现任何附加功能的实现。或者,如果我尝试放弃继承并只定义一堆不同的实现,我将有很多代码重复。
有没有两全其美的解决方案?
我的回答将分为代码、代码中的一些注释和此处的文本。
一、代码:
#include <iostream>
#include <memory>
#include <vector>
// Create a pure virtual interface for your MonomerImpls
class MonomerImpl {
public:
virtual ~MonomerImpl() = default; // Required in non-concrete classes
void applyDisplacement(int displacement) {
std::cout << "From pure virtual interface " << displacement << ":\n";
applyDisplacement_Helper(displacement);
}
// Protected so inherited classes can access it freely
protected:
std::vector<int> positions{};
private:
// Helper
virtual void applyDisplacement_Helper([[maybe_unused]] int displacement) = 0;
};
// Public inheritance is what you want a majority of the time
class MonomerTypeAImpl : public MonomerImpl {
public:
MonomerTypeAImpl() = default;
private:
void applyDisplacement_Helper([[maybe_unused]] int displacement) override {
std::cout << "Specialization From Monomer A Impl\n";
}
};
// Second MonomerImpl child to illlustrate
class MonomerTypeXImpl : public MonomerImpl {
public:
MonomerTypeXImpl() = default;
private:
void applyDisplacement_Helper([[maybe_unused]] int displacement) override {
std::cout << "specialization From Monomer X Impl\nYowza\n";
}
};
// If you'll have many Monomers, it's better for the base to be abstract
// In cases where this similar operation is being done, NVI (non-virtual
// interface) can be quite helpful.
//
// I just have a Monomer class, and you can feed different objects different
// MonomerImpls to get the desired behavior
class Monomer {
public:
Monomer(MonomerImpl* impl) : p_impl(impl) {}
void applyDisplacement(int displacement) {
p_impl->applyDisplacement(displacement);
}
private:
std::unique_ptr<MonomerImpl> p_impl;
// I got rid of the get_impl() function, it added an extra step for no gain
};
int main() {
MonomerImpl* monoImpl = new MonomerTypeAImpl();
Monomer mono(monoImpl);
mono.applyDisplacement(42);
std::cout << "\n\n";
Monomer xMono(new MonomerTypeXImpl());
xMono.applyDisplacement(350);
}
输出:
From pure virtual interface:
From Monomer A Impl: 42
From pure virtual interface:
From Monomer X Impl: 350
Yowza
我必须删除您的 Vector
class(未提供),所以我只是将其更改为 int
以便编译。所做的更改并不大,因为大部分内容已经存在。如果您要有许多不同的实现,我的建议是为这些实现创建一个纯虚拟基础 class。它标准化接口并将代码放在具体的(objects 是可声明的)classes 它所属的地方。就像我在评论中所说的那样,如果许多派生的 classes 会做同样的事情,使用 NVI 或 Non-V虚拟 I 接口可以帮助解决这个问题。 NVI 的一个例子在 class MonomerImpl
.
(NVI 背后的想法是基础 class 可调用(public)函数不是虚拟的。基础 class,即使它是抽象的,也可以做那些事情对所有 children 都是通用的,并且 然后 调用一个 protected/private 虚拟函数,允许 children 执行他们的专门工作,并且只执行他们的专门工作. 调用虚函数的顺序自然取决于共同的工作)
根据提供的代码,我完全没有觉得从Monomer
派生有什么收获。如果不同的 MonomerImpl*
还不够,您可能有理由这样做。同样,我建议以抽象基础 class 作为起点。
如果还有问题,我很乐意尝试解答。作为此答案的参考,https://refactoring.guru/design-patterns/bridge
您已经有了想法,但您的实施有点离岸。
If I use a virtual function getImplementation function can only return a
MonomerImpl*
pointer, then I can't implement any additional functionality to the implementation
更正:您将能够实现附加功能,但仅限于 MonomerImpl
class。并且虚拟功能也与继承一起工作,因此要使其工作,您必须从基础 class 继承或抽象 class .ie 接口。
问题是您还没有制作连接 Monomer
的各种实现的胶水。 Monomer
和它的实现 classes 之间的这种联系可以使用继承或聚合来实现。但在这种情况下,聚合将不是理想的,因为 "是 Monomer
和它实现之间的" 关系。
例如
#include <iostream>
#include <memory>
class vector
{
};
// design an abstract base class .ie interface with pure virtual functions
// of all the functions that an implementation must define .
// All these functions will be callable from our base class with polymorphism
class monomer_intf
{
virtual auto
do_apply_displacement(vector displacement) -> void
= 0;
};
// monomer is our concrete base class which will help use call our desired
// implementations in the various derived class
class monomer : public monomer_intf
{
// defined because pure virtual functions need to be defined in the derived
// class else the derived class also becomes an abstract class
auto
do_apply_displacement(vector displacement) -> void override
{
// this dispatches to the dynamic class implementation
// ie. the class monmer will point to or reference to
do_apply_displacement(displacement);
}
public:
auto
apply_displacement(vector displacement) -> void
{
// calls the monomer class do_apply_displacement
// which then dispatches to the implementation
do_apply_displacement(displacement);
}
};
// to get our implementation functionality only the virtual or overriden
// function need to be implemented but we can have several functions as pleases
// us
class monomer_impl : public monomer
{
auto
do_apply_displacement(vector displacement) -> void override
{
std::cout << "monomer called do_apply_displacement of monomer_impl \n";
}
};
class monomer_type_a : public monomer
{
auto
do_apply_displacement(vector displacement) -> void override
{
std::cout << "monomer called do_apply_displacement of monomer_type_a \n";
}
};
class monomer_type_a_impl_grid : public monomer_type_a
{
auto
do_apply_displacement(vector displacement) -> void override
{
std::cout << "monomer called do_apply_displacement of "
"monomer_type_a_impl_grid \n";
}
};
class monomer_type_a_impl : public monomer_impl
{
auto
do_apply_displacement(vector displacement) -> void override
{
std::cout
<< "monomer called do_apply_displacement of monomer_type_a_impl \n";
}
};
// if we like this style . It simplifies the creation of the implementations
// and the execution of our need functions
class monomer_abstraction
{
std::unique_ptr<monomer> implementation_;
public:
explicit monomer_abstraction(monomer *inmplementation)
: implementation_{ inmplementation }
{
}
auto
apply_displacement(vector displacement)
{
implementation_->apply_displacement(displacement);
}
};
auto
main() -> int
{
// using stack allocated monomer_type_a_impl_grid
monomer_type_a_impl_grid impl_grid;
monomer &monomer_grid = impl_grid;
monomer_grid.apply_displacement(vector{});
// using heap allocated monomer_impl
auto impl_ = std::make_unique<monomer_impl>();
monomer &monomer = *impl_;
/// using virtual dispach apply_displacement will call the
/// do_apply_displacement of it dynamic type
monomer.apply_displacement(vector{});
// using monomer_abstraction as an abstraction to the implementations
// of need functionalities
auto abstraction
= monomer_abstraction(std::make_unique<monomer_type_a_impl>().release());
abstraction.apply_displacement(vector{});
}
DRY 绝对是正确的选择