适配器模式:支持基本数据,可以是const或非const,优雅

jav*_*ver 12 c++ const function adapter c++14

如何使适配器类适当地支持const和非const底层数据?

具体例子

RigidBody是描述对象物理属性的类.
这是它非常简化的版本(1D): -

class RigidBody{
    float position=1;
    public: float getPosition()const{ return position;}
    public: void setPosition(float ppos){ position=ppos;}
};
Run Code Online (Sandbox Code Playgroud)

Adapter封装RigidBody.
它提供了一个小失真的功能get/set position: -

class Adapter{
    public: RigidBody* rigid; int offset=2;
    public: float getPosition(){
        return rigid->getPosition()+offset;     //distort
    }
    public: void setPosition(float ppos){
        return rigid->setPosition(ppos-offset); //distort
    }
};
Run Code Online (Sandbox Code Playgroud)

我可以RigidBody通过以下方式间接设定位置Adapter: -

int main() {
    RigidBody rigid;
    Adapter adapter;  //Edit: In real life, this type is a parameter of many function
    adapter.rigid=&rigid;
    adapter.setPosition(5);
    std::cout<<adapter.getPosition();//print 5
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

一切正常(演示).

目的

我想创建一个接收的新功能. 我应该能够通过使用适配器从中读取(例如). constRigidBody* rigid
getPosition()

但是,我真的不知道如何优雅地做到这一点.

void test(const RigidBody* rigid){
    Adapter adapter2; 
    //adapter2.rigid=rigid; //not work, how to make it work?
    //adapter2.setPosition(5); //should not work
    //adapter2.getPosition();  //should work 
}
Run Code Online (Sandbox Code Playgroud)

我糟糕的解决方案

解决方案A1(2个适配器+ 1个小部件)

创建一个小部件: -

class AdapterWidget{
    public: static Adapter createAdapter(RigidBody* a);
    public: static AdapterConst createAdapter(const RigidBody* a);
};
Run Code Online (Sandbox Code Playgroud)

AdapterConst只能getPosition(),虽然AdapterConst可以得到和设置.

我可以像以下一样使用它: -

void test(const RigidBody* rigid){
    auto adapter=AdapterWidget::createAdapter(rigid);
Run Code Online (Sandbox Code Playgroud)

它很容易使用.

缺点:代码 AdapterConstAdapter将非常重复.

解决方案A2(+继承)

这是对以前解决方案的改进.
Adapter(有setPosition())派生自AdapterConst(有getPosition()).

缺点:它不简洁.我为单个任务使用2个类!
这似乎是微不足道的,但在更大的代码库中,它根本不是很有趣.

具体而言,位置getPosition()将远离setPosition()例如不同的文件.
这会导致可维护性问题.

解决方案B(模板)

创建模板类.有很多方法,例如: -

  • Adapter<T =RigidBody OR const RigidBody >
  • Adapter<bool=true is const OR false is non-const >

缺点:在各方面,它都是不优雅的.这太过分了.(?)
我将遭受模板的缺点,例如标题中的所有内容.

解决方案C1(const_cast)

我试图避免它. 这是邪恶的.

class Adapter{
    public: RigidBody* rigid; 
    void setUnderlying(const RigidBody* r){
        rigid=const_cast< RigidBody*>(r);
    }
    ....
};
Run Code Online (Sandbox Code Playgroud)

解决方案C2(+手动断言)

我可以手动添加一些断言.
它只是强调它是多么不专业: -

    bool isConst;
    void setUnderlying(const RigidBody* r){
        ...
        isConst=true;
    }
    void setUnderlying(RigidBody* r){
        ...
        isConst=false;
    }
    void setPosition(float a){
        if(isConst){ /*throw some exception*/ }
        ....
    }
Run Code Online (Sandbox Code Playgroud)

解决方案D(逃跑)

  • 懒惰:改变为. test(constRigidBody* rigid)test(RigidBody* rigid)
  • 疯狂:变化RigidBody::setPosition()const.

无论哪种方式,我的程序都不会再const纠正,
但单个Adapter类就足够了.

在遇到const/non-const模式的任何地方,我真的必须做其中的一件事吗?
请提供一个漂亮的解决方案 (不需要完整的代码,但我不介意)

对不起,很长的帖子.

编辑: 在现实生活中,Adapter是许多功能的参数.
它像玩具一样传递.

大多数此类函数都没有相关知识RigidBody,因此不适合从捆绑调用更改someFunction(adapter)someFunction(offset,rigidbody).

Dav*_*aim 8

你不应该坚持这个想法.这是C++,而不是Java.

您的代码非常面向Java.我可以通过编写代码的方式看到它,使用指针并const在需要时静默省略.

实际上,我个人看到的大多数糟糕的C++代码都被写成"C inside classes"或"Java without GC".它们都是编写C++代码的极其糟糕的方法.

您的问题有一个惯用的解决方案:

  1. 沟渠大多数的设计模式.它们对于默认情况下对象是引用类型的语言很有用.C++倾向于大多数时候将对象作为值类型而不是静态多态(模板)而不是运行时多态(继承+覆盖).

  2. 写两个类,一个是Adapter一个,一个是ConstAdapter.这就是标准库已经完成的工作.每个容器都有不同iteratorconst_iterator实现正是因为这个原因.你可以通过指针存储一些东西,或者通过const指针存储.试图将两者混合是容易出错的.如果有一个很好的解决方案,我们就不会为每个容器提供两个迭代器typedef.


Cal*_*eth 1

使用引用而不是指针,并让常量传播到Adapter. 然后你可以安全地 const_cast,如

template <class Rigid>
class Adapter{
    Rigid & rigid;
    int offset;
public: 
    Adapter(Rigid & rigid, int offset = 2) : rigid(rigid), offset(offset) {}
    float getPosition() const { return rigid.getPosition() + offset; }
    void setPosition(float ppos) { rigid.setPosition(ppos - offset); }
};
Run Code Online (Sandbox Code Playgroud)