How can be changed the behavior of an object at runtime? (using C++)
I will give a simple example.
I have a class Operator that contains a method Operate. Let’s suppose it looks like this:
double operate(double a, double b)
{
return 0.0;
}
The user will give some input values for a and b, and will choose what operation to perform let’s say that he can choose to compute addition or multiplication. Given it’s input all I am allowed to do is instantiate Operator and call operate(a, b), which is written exactly how I mentioned before.
The methods that compute multiplication or addition will be implemented somewhere (no idea where).
In conclusion I have to change the behavior of my Operator object depending of users input.
views:
414answers:
11Objects always have the behaviour that's defined by their class.
If you need different behaviour, you need a different class...
There are many ways to do this proxying, pImpl idiom, polymorphism, all with pros and cons. The solution that is best for you will depend on exactly which problem you are trying to solve.
You can achieve it through dynamic binding (polymorphism)... but it all depends on what you are actually trying to achieve.
Many many ways:
Try if
at first. You can always change the behavior with if statement. Then you probably find the 'polymorphism' way more accurate, but it depends on your task.
The standard pattern for this is to make the outer class have a pointer to an "implementation" class.
// derive multiple implementations from this:
class Implementation
{
virtual ~Implementation() {} // probably essential!
virtual void foo() = 0;
};
class Switcheroo
{
Implementation *impl_;
public:
// constructor, destructor, copy constructor, assignment
// must all be properly defined (any that you can't define,
// make private)
void foo()
{
impl_->foo();
}
};
By forwarding all the member functions of Switcheroo
to the impl_
member, you get the ability to switch in a different implementation whenever you need to.
There are various names for this pattern: Pimpl (short for "private implementation"), Smart Reference (as opposed to Smart Pointer, due to the fowarding member functions), and it has something in common with the Proxy and Bridge patterns.
You can't change the behavior of arbitrary objects using any sane way unless the object was intended to use 'plugin' behaviour through some technique (composition, callbacks etc).
(Insane ways might be overwriting process memory where the function code lies...)
However, you can overwrite an object's behavior that lies in virtual methods by overwriting the vtable (An approach can be found in this article ) without overwriting memory in executable pages. But this still is not a very sane way to do it and it bears multiple security risks.
The safest thing to do is to change the behavior of objects that were designed to be changed by providing the appropriate hooks (callbacks, composition ...).
I'm mentioning this only as trivia and can't unrecommend it more, but here we go...
WARNING DANGER!!!
A stupid trick I've seen is called clutching, I think, but it's only for the truely foolish. Basically you swap the virtualtable pointer to that of another class, it works, but it could theoretically destroy the world or cause some other undefined behavior :)
Anyways instead of this just use dynamic classing and kosher C++, but as an experiment the above is kind of fun...
You can change the object behavior using dynamic binding. The design patterns like Decorator, Strategy would actually help you to realize the same.
Coplien's Envelope/Letter Pattern (in his must read book Advanced C++ Programming Styles and Idioms) is the classic way to do this.
Briefly, an Envelope and a Letter are both subclasses of an abstract base class/interfcae that defines the public interface for all subclasses.
An Envelope holds (and hides the true type of) a Letter.
A variety of Letter classes have different implementations of the abstract class's public interface.
An Envelope has no real implementation; it just forards (delegates) to its Letter. It holds a pointer to the abstract base class, and points that at a concrete Letter class instance. As the implementation needs to be changed, the type of Letter subclass pointer to is changed.
As users only have a reference to the Envelope, this change is invisible to them except in that the Envelope's behavior changes.
Coplien's examples are particularly clean, because it's the Letters, not the envelope that cause the change.
One example is of a Number class hierarchy. The abstract base declares certain operations over all Numbers, e.g, addition. Integer and a Complex are examples of concrete subclasses.
Adding an Integer and an Integer results in an Integer, but adding a Interget and a Complex results in a Complex.
Here's what the Envelope looks like for addition:
public class Number {
Number* add( const Number* const n ) ; // abstract, deriveds override
}
public class Envelope : public Number {
private Number* letter;
...
Number* add( const Number& rhs) { // add a number to this
// if letter and rhs are both Integers, letter->add returns an Integer
// if letter is a a Complex, or rhs is, what comes back is a Complex
//
letter = letter->add( rhs ) ) ;
return this;
}
}
Now in the client's pointer never changes, and they never ever need to know what the Envelop is holding. Here's the client code:
int main() {
// makeInteger news up the Envelope, and returns a pointer to it
Number* i = makeInteger( 1 ) ;
// makeComplex is similar, both return Envelopes.
Number* c = makeComplex( 1, 1 ) ;
// add c to i
i->add(c) ;
// to this code, i is now, for all intents and purposes, a Complex!
// even though i still points to the same Envelope, because
// the envelope internally points to a Complex.
}
In his book, Coplien goes into greater depth -- you'll note that the add method requires multi-dispatch of some form --, and adds syntactic sugar. But this is the gist of how you can get what's called "runtime polymorphism".
You could also consider the Role Pattern with dynamic binding..i'm struggling with the same thing that you do..I read about the Strategy pattern but the role one sounds like a good solution also...