桥接模式:如何定义调用派生实现的函数class

Bridge pattern: how to define functions that call the implementation of the derived class

我正在研究一种聚合物模拟代码,该代码采用桥接模式来分离抽象(模拟中涉及的对象)及其实现(它们的表示方式,例如每个单体一个粒子、连续网格等)。我面临的一个问题是,我正在努力定义一个 class 抽象层次结构,其中较低的抽象可以执行在层次结构中更高层的其他 classes 上定义的函数。

我想定义一个基础抽象 Monomer,它包含许多调用 MonomerImpl 实现的方法。比如说,一种方法 applyDisplacement 可以移动 space 中的单体。但是,当我用几个实现 MonomerTypeAImplGridMonomerTypeAImplParticles 等定义派生抽象 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 绝对是正确的选择