#include "fly_behavior.h"#include "speak_behavior.h"#ifndef DUCK_H#define DUCK_H// "Duck" is not abstractclass Duck{public: // pass the behavior in the ctor - favour composition over inheritance Duck(FlyBehavior *, SpeakBehavior*); ~Duck(); void PerformFly(); void PerformSpeak();protected: FlyBehavior * _fly_behavior; SpeakBehavior * _speak_behavior;};#endif
#include "duck.h"// use proper constructor initialisersDuck::Duck(FlyBehavior * fly, SpeakBehavior * speak): _fly_behavior(fly), _speak_behavior(speak){}// remember to delete what you allocateDuck::~Duck(){ delete _fly_behavior; delete _speak_behavior;}void Duck::PerformFly(){ _fly_behavior->Fly();}void Duck::PerformSpeak(){ _speak_behavior->Speak();}
#include "duck.h"#include "not_fly.h"#include "squeak.h"namespace{ Duck * MakeToyDuck() { return new Duck(new NoFly, new Squeak); }}// "main" should return "int"int main(){ std::cout << "DUCK SIMULATOR" << std::endl; // prefer a factory method over inheritance Duck * toy_duck_object = MakeToyDuck(); toy_duck_object->PerformFly(); toy_duck_object->PerformSpeak(); delete toy_duck_object;}
The above doesn't reflect your idea of the design, but illustrates the intention. Compare it to the implementation that uses inheritance, which is is given below:
#ifndef DUCK_H#define DUCK_H// "Duck" is an abstract base classclass Duck{public: // note the virtual destructor virtual ~Duck(); void PerformFly(); void PerformSpeak();protected: // make the ctor protected, i.e. "Duck" cannot be instanciated Duck(FlyBehavior *, SpeakBehavior *); FlyBehavior * _fly_behavior; SpeakBehavior * _speak_behavior;};#endif
#include "duck.h"Duck::Duck(FlyBehavior * fly, SpeakBehavior * speak): _fly_behavior(fly), _speak_behavior(speak){}Duck::~Duck(){ delete _fly_behavior; delete _speak_behavior;}void Duck::PerformFly(){ _fly_behavior->Fly();}void Duck::PerformSpeak(){ _speak_behavior->Speak();}
include "duck.h"#ifndef TOY_DUCK_H#define TOY_DUCK_H// the dtor is not necessary, it's already defined in "Duck"class ToyDuck: public Duck{public: // the ctor is public and omits the behavior arguments ToyDuck();};#endif
#include "toy_duck.h"#include "not_fly.h"#include "squeak.h"// the ctor invokes the base class' ctor with the appropriate argumentsToyDuck::ToyDuck() : Duck(new NotFly, new Squeak){}
#include <iostream>#include "toy_duck.h"// "main" stays the sameint main(){ std::cout << "DUCK SIMULATOR" << std::endl; // note that you can use both "Duck" and "ToyDuck" as your / object's type. prefer the base class. Duck * toy_duck_object = new ToyDuck; toy_duck_object->PerformFly(); toy_duck_object->PerformSpeak(); delete toy_duck_object;}
Finally, your behavior "interfaces" are no real interfaces. Interfaces in C++ should be abstract classes:
#ifndef FLY_BEHAVIOR_H#define FLY_BEHAVIOR_Hclass FlyBehavior{public: // the "virtual" is required so that calls to the interface // will dispatch to the derived class. the " = 0" denotes // an abstract method, i.e. FlyBehavior::Fly() cannot be called // and FlyBehavoir cannot be instanciated. // the brackets are the default implementaion, which may be called // by derived classes using "FlyBehavior::Fly()" virtual void Fly() = 0 { }};/* Example: class NoFly : public FlyBehavior { // implement "Fly" by just calling the default implementation. // only works if a default implementation exists, though. void Fly() { FlyBehavior::Fly(); } };*/#endif
The same principle applies to "SpeakBehavior". The reason your derived classes didn't work was the fact that the behavoir "interface" methods weren't marked "virtual".
Hope that clears things up a little,
Pat
PS: No guarantees, though - I didn't work with C++ for quite some time [smile]
PPS: Seems I'm a bit slow [lol]