How to make an adapter class to support both const and non-const underlying data appropriately?
RigidBody
is a class describing physic property of object.
Here is its very simplified version (1D):-
class RigidBody{
float position=1;
public: float getPosition()const{ return position;}
public: void setPosition(float ppos){ position=ppos;}
};
Adapter
encapsulates RigidBody
.
It provides a-little-distorted functionality to 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
}
};
I can set position of RigidBody
indirectly by using 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;
}
Everything works (demo).
I want create a new function that receives const
RigidBody* rigid
.
I should be able to read from it (e.g. getPosition()
) by using adapter.
However, I don't really know how to do it elegantly.
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
}
Create a widget :-
class AdapterWidget{
public: static Adapter createAdapter(RigidBody* a);
public: static AdapterConst createAdapter(const RigidBody* a);
};
AdapterConst
can only getPosition()
, while AdapterConst
can both get and set.
I can use it like :-
void test(const RigidBody* rigid){
auto adapter=AdapterWidget::createAdapter(rigid);
It is easy to use.
Disadvantage: Code of AdapterConst
and Adapter
will be very duplicated.
It is an improvement of the previous solution.
Let Adapter
(has setPosition()
) derive from AdapterConst
(has getPosition()
).
Disadvantage: It is not concise. I use 2 classes for the single task!
This might seem to be trivial, but in a bigger code-base, it is not fun at all.
Specifically, location of getPosition()
will be far-away from setPosition()
, e.g in different files.
This causes maintainability problem.
Create a template class. There are many ways e.g. :-
Adapter<T =RigidBody OR const RigidBody >
Adapter<bool=true is const OR false is non-const >
Disadvantage: In every ways, it is inelegant. It is an overkill. (?)
I will suffer from disadvantage of template e.g. everything in header.
I am trying to avoid it. It is evil.
class Adapter{
public: RigidBody* rigid;
void setUnderlying(const RigidBody* r){
rigid=const_cast< RigidBody*>(r);
}
....
};
I can add some assertion manually.
It just emphasizes how much it is unprofessional :-
bool isConst;
void setUnderlying(const RigidBody* r){
...
isConst=true;
}
void setUnderlying(RigidBody* r){
...
isConst=false;
}
void setPosition(float a){
if(isConst){ /*throw some exception*/ }
....
}
test(
const
RigidBody* rigid)
to test(RigidBody* rigid)
. RigidBody::setPosition()
to be const
. In either way, my program will not be const
-correct any more,
but a single Adapter
class would be enough.
Do I really have to do one of these things wherever I encounter the const/non-const pattern?
Please provide a pretty solution. (no full code is required, but I don't mind)
Sorry for the long post.
Edit: In real life, Adapter
is a parameter for many function.
It is passed around like a toy.
Most of such functions don't have knowledge about RigidBody
, so it is not quite suitable to change from a bundle calling someFunction(adapter)
to someFunction(offset,rigidbody)
.
An Adapter pattern acts as a connector between two incompatible interfaces that otherwise cannot be connected directly. An Adapter wraps an existing class with a new interface so that it becomes compatible with the client's interface.
Advantage of Adapter PatternIt allows two or more previously incompatible objects to interact. It allows reusability of existing functionality.
First, be sure you have a problem that this pattern can solve. Define the client interface that will be used to indirectly interact with incompatible objects. Make the adapter class inherit the interface defined in the previous step. In the adapter class, create a field to store a reference to the adaptee object.
Object and Class Adapters Difference: The only difference is that with class adapter we subclass the Target and the Adaptee, while the object adapter uses composition to pass requests to an adaptee. Object Adapters and Class Adapters use two different means of adapting the adaptee: composition versus inheritance.
You shouldn't keep with that idea. This is C++, not Java.
Your code is extremely Java oriented. I can see it by the way you write the code, use pointers and silently omit const
when needed.
In fact, most of the bad C++ code that I personally saw is pretty much either written to be "C inside classes" or "Java without GC". Both of them are extremely bad ways of writing C++ code.
Your question has an idiomatic solution:
Ditch most of the design patterns. they are useful for languages where object are reference types by default. C++ prefers most of the times to tread object as value types and prefer static polymorphism (templates) rather than run-time polymorphism (inherit + override).
Write two classes , one is Adapter
and one is ConstAdapter
. This is what the standard library already does. every container has different iterator
and const_iterator
implementation exactly because of that reason. you can either store something by pointer, either by const pointer. It is error prone trying to mix the two. If there were a nice solution for that problem, we wouldn't have two iterator typedefs for each container.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With