桥接模式:如何定义调用派生类实现的函数
Posted
技术标签:
【中文标题】桥接模式:如何定义调用派生类实现的函数【英文标题】:Bridge pattern: how to define functions that call the implementation of the derived class 【发布时间】:2021-06-25 15:55:10 【问题描述】:我正在研究一种聚合物模拟代码,该代码使用桥模式来分离抽象(模拟中涉及的对象)及其实现(它们是如何表示的,例如每个单体一个粒子、连续网格等)。我面临的一个问题是我正在努力定义抽象的类层次结构,其中较低的抽象可以执行在层次结构中更高的其他类上定义的函数。
我想定义一个基本抽象Monomer
,它包含许多调用MonomerImpl
实现的方法。比如说,一种方法applyDisplacement
在空间中移动单体。但是,当我定义派生抽象MonomerTypeA
和几个实现MonomerTypeAImplGrid
、MonomerTypeAImplParticles
等时,我想调用applyDisplacement
,定义在层次类的顶部,但使用我的具体实现单体类。
这是我尝试过的一个例子:
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
函数只能返回一个MonomerImpl*
指针,那么我就无法实现任何附加功能。或者,如果我尝试放弃继承而只定义一堆不同的实现,我将有很多代码重复。
有没有两全其美的解决方案?
【问题讨论】:
如图所示,您没有任何派生类。您是否打算让MonomerTypeA
继承自Monomer
?
【参考方案1】:
我的答案将分为代码、代码中的一些 cmets 和此处的文本。
一、代码:
#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
类(未提供),所以我只是将其更改为 int
以便它可以编译。所做的更改并不大,因为大部分内容已经存在。如果您将有许多不同的实现,我的建议是为实现创建一个纯虚拟基类。它标准化接口并将代码放在它所属的具体(对象是可声明的)类中。就像我在评论中所说,如果许多派生类都做同样的事情,使用 NVI 或 Non-Virtual I接口可以帮助解决这个问题。 NVI 的一个例子是在MonomerImpl
类中。
(NVI 背后的想法是,基类可调用(公共)函数不是虚拟的。基类即使是抽象的,也可以做所有子类共有的事情,那么 调用一个受保护的/私有的虚拟函数,允许孩子们做他们的专门工作,并且只做他们专门的工作。虚拟函数被调用的顺序自然取决于共同的工作)
通过提供的代码,我完全不觉得从Monomer
派生得到任何东西。如果不同的 MonomerImpl*
s 还不够,您可能有理由这样做。同样,我推荐一个抽象基类作为起点。
如果还有问题,我很乐意尝试回答。作为这个答案的参考,https://refactoring.guru/design-patterns/bridge
【讨论】:
【参考方案2】:你有这个想法,但你的实现有点离岸。
如果我使用虚函数 getImplementation 函数只能返回一个
MonomerImpl*
指针,那么我无法实现任何附加功能
更正:您将能够实现附加功能,但仅限于 MonomerImpl
类。并且虚函数也与继承一起工作,因此要使其工作,您必须从基类或抽象类继承。即接口。
问题是您没有制作胶水来连接Monomer
的各种实现。 Monomer
和它的实现类之间的这种连接可以使用继承或聚合来实现。但在这种情况下,聚合并不理想,因为 Monomer
与其实现之间存在 "is a" 关系。
例如
#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 肯定是要走的路
【讨论】:
以上是关于桥接模式:如何定义调用派生类实现的函数的主要内容,如果未能解决你的问题,请参考以下文章