I'm trying to encapsulate existing functionality in a wide swathe of classes so it can be uniformly modified (e.g. mutexed, optimized, logged, etc.) For some reason, I've gotten it into my head that (multiple) private inheritance is the way to go, but I can't find what led me to that conclusion.
The question is: what is the name for what I am trying to do, and where I can see it done right?
What I think this isn't:
Decorator: All the descriptions I see for this pattern wrap a class to provide extra methods as viewed from the outside. I want to provide functionality to the inside (extract existing as well as add additional.)
Interface: This is close, because the functionality has a well-defined interface (and one I would like to mock for testing.) But again this pattern deals with the view from the outside.
I'm also open to alternatives, but the jackpot here is finding an article on it written by someone much smarter than me (a la Alexandrescu, Meyers, Sutter, etc.)
Example code:
// Original code, this stuff is all over
class SprinkledFunctionality
{
void doSomething()
{
...
int id = 42;
Db* pDb = Db::getDbInstance(); // This should be a reference or have a ptr check IRL
Thing* pThing = pDb->getAThing(id);
...
}
}
// The desired functionality has been extracted into a method, so that's good
class ExtractedFunctionality
{
void doSomething()
{
...
int id = 42;
Thing* pThing = getAThing(id);
...
}
protected:
Thing* getAThing(int id)
{
Db* pDb = Db::getDbInstance();
return pDb->getAThing(id);
}
}
// What I'm trying to do, or want to emulate
class InheritedFunctionality : private DbAccessor
{
void doSomething()
{
...
int id = 42;
Thing* pThing = getAThing(id);
...
}
}
// Now modifying this affects everyone who accesses the DB, which is even better
class DbAccessor
{
public:
Thing* getAThing(int id)
{
// Mutexing the DB access here would save a lot of effort and can't be forgotten
std::cout << "Getting thing #" << id << std::endl; // Logging is easier
Db* pDb = Db::getDbInstance(); // This can now be a ptr check in one place instead of 100+
return = pDb->getAThing(id);
}
}
One useful technique you might be overlooking is the non-virtual interface (NVI) as coined by Sutter in his writings about virtuality. It requires a slight inversion of the way you're looking at it, but is intended to address those precise concerns. It also tackles those concerns from within as opposed, to say, decorator which is about extending functionality non-intrusively from the outside.
class Foo
{
public:
void something()
{
// can add all the central code you want here for logging,
// mutex locking/unlocking, instrumentation, etc.
...
impl_something();
...
}
private:
virtual void impl_something() = 0;
};
The idea is to favor non-virtual functions for your public interfaces, but make them call virtual functions (with private or protected access) which are overridden elsewhere. This gives you both the extensibility you typically get with inheritance while retaining central control (something otherwise often lost).
Now Bar can derive from Foo and override impl_something to provide specific behavior. Yet you retain the central control in Foo to add whatever you like and affect all subclasses in the Foo hierarchy.
Initially Foo::something might not even do anything more than call Foo::impl_something, but the value here is the breathing room that provides in the future to add any central code you want -- something which can otherwise be very awkward if you're looking down at a codebase which has a boatload of dependencies directly to virtual functions. By depending on a public non-virtual function which depends on an overridden, non-public virtual function, we gain an intermediary site to which we can add all the central code we like.
Note that this can be overkill too. Everything can be overkill in SE, as a simple enough program might actually be the easiest to maintain if it just used global variables and a big main function. All of these techniques have trade-offs, but the pros begin to outweigh the cons with sufficient scale, complexity, changing requirements*.
* I noticed in one of your other questions that you wrote that the right tool for the job should have zero drawbacks, but everything tends to have drawbacks, everything is a trade-off. It's whether the pros outweigh the cons that ultimately determines whether it was a good design decision, and it's far from easy to realize all of this in foresight instead of hindsight.
As for your example:
// What I'm trying to do, or want to emulate
class InheritedFunctionality : private DbAccessor
{
void doSomething()
{
...
int id = 42;
Thing* pThing = getAThing(id);
...
}
}
... there is a significantly tighter coupling here than is necessary for this example. There might be more to it than you've shown which makes private inheritance a necessity, but otherwise composition would generally loosen the coupling considerably without much extra effort, like so:
class ComposedFunctionality
{
...
void doSomething()
{
...
int id = 42;
Thing* pThing = dbAccessor.getAThing(id);
...
}
...
private:
DbAccessor dbAccessor;
};
Basically what you're doing is decoupling the way you getAThing from the way you doSomething. Looks a lot like the Factory Method object-oriented design pattern. Have a look here:
Factory Method Pattern
Related
i'm creating particle system and i want to have possibility to choose what kind of object will be showing on the screen (like simply pixels, or circle shapes). I have one class in which all parameters are stored (ParticleSettings), but without those entities that stores points, or circle shapes, etc. I thought that i may create pure virtual class (ParticlesInterface) as a base class, and its derived classes like ParticlesVertex, or ParticlesCircles for storing those drawable objects. It is something like that:
class ParticlesInterface
{
protected:
std::vector<ParticleSettings> m_particleAttributes;
public:
ParticlesInterface(long int amount = 100, sf::Vector2f position = { 0.0,0.0 });
const std::vector<ParticleSettings>& getParticleAttributes() { return m_particleAttributes; }
...
}
and :
class ParticlesVertex : public ParticlesInterface
{
private:
std::vector<sf::Vertex> m_particleVertex;
public:
ParticlesVertex(long int amount = 100, sf::Vector2f position = { 0.0,0.0 });
std::vector<sf::Vertex>& getParticleVertex() { return m_particleVertex; }
...
}
So... I know that i do not have access to getParticleVertex() method by using polimorphism. And I really want to have that access. I want to ask if there is any better solution for that. I have really bad times with decide how to connect all that together. I mean i was thinking also about using template classes but i need it to be dynamic binding not static. I thought that this idea of polimorphism will be okay, but i'm really need to have access to that method in that option. Can you please help me how it should be done? I want to know what is the best approach here, and also if there is any good answer to that problem i have if i decide to make that this way that i show you above.
From the sounds of it, the ParticlesInterface abstract class doesn't just have a virtual getParticleVertex because that doesn't make sense in general, only for the specific type ParticlesVertex, or maybe a group of related types.
The recommended approach here is: Any time you need code that does different things depending on the actual concrete type, make those "different things" a virtual function in the interface.
So starting from:
void GraphicsDriver::drawUpdate(ParticlesInterface &particles) {
if (auto* vparticles = dynamic_cast<ParticlesVertex*>(&particles)) {
for (sf::Vertex v : vparticles->getParticleVertex()) {
draw_one_vertex(v, getCanvas());
}
} else if (auto* cparticles = dynamic_cast<ParticlesCircle*>(&particles)) {
for (CircleWidget& c : cparticles->getParticleCircles()) {
draw_one_circle(c, getCanvas());
}
}
// else ... ?
}
(CircleWidget is made up. I'm not familiar with sf, but that's not the point here.)
Since getParticleVertex doesn't make sense for every kind of ParticleInterface, any code that would use it from the interface will necessarily have some sort of if-like check, and a dynamic_cast to get the actual data. The drawUpdate above also isn't extensible if more types are ever needed. Even if there's a generic else which "should" handle everything else, the fact one type needed something custom hints that some other future type or a change to an existing type might want its own custom behavior at that point too. Instead, change from a thing code does with the interface to a thing the interface can be asked to do:
class ParticlesInterface {
// ...
public:
virtual void drawUpdate(CanvasWidget& canvas) = 0;
// ...
};
class ParticlesVertex {
// ...
void drawUpdate(CanvasWidget& canvas) override;
// ...
};
class ParticlesCircle {
// ...
void drawUpdate(CanvasWidget& canvas) override;
// ...
};
Now the particles classes are more "alive" - they actively do things, rather than just being acted on.
For another example, say you find ParticlesCircle, but not ParticlesVertex, needs to make some member data updates whenever the coordinates are changed. You could add a virtual void coordChangeCB() {} to ParticlesInterface and call it after each motion model tick or whenever. With the {} empty definition in the interface class, any class like ParticlesVertex that doesn't care about that callback doesn't need to override it.
Do try to keep the interface's virtual functions simple in intent, following the Single Responsibility Principle. If you can't write in a sentence or two what the purpose or expected behavior of the function is in general, it might be too complicated, and maybe it could more easily be thought of in smaller steps. Or if you find the virtual overrides in multiple classes have similar patterns, maybe some smaller pieces within those implementations could be meaningful virtual functions; and the larger function might or might not stay virtual, depending on whether what remains can be considered really universal for the interface.
(Programming best practices are advice, backed by good reasons, but not absolute laws: I'm not going to say "NEVER use dynamic_cast". Sometimes for various reasons it can make sense to break the rules.)
I'm wondering how to avoid code-duplication in a scenario as given below.
(There's this question:
How do I check if an object's type is a particular subclass in C++?
The answer there is that it's not possible and even with dynamic casts member access wouldn't be possible, I guess.)
So I'm wondering how you avoid having almost the same code in different methods, where just one method would have a few additional operations.
class Basic {
...
}
class Advanced : public Basic {
...
}
AnotherClass::lengthy_method(Basic *basic) {
// do a lot of processing, access members of basic
if (*basic is actually of class Advanced)
// do one or a few specific things with members of Advanced
// more processing just using Basic
if (*basic is actually of class Advanced)
// do one or a few specific things with members of Advanced
// more processing just using Basic
}
Also from a design perspective AnotherClass::lengthy_method wouldn't want to be defined in Basic or Advanced since it's not really belonging to either of them. It's just operating on their kind.
I'm curious what the language experts know and I hope there's a nice solution, maybe at least through some functionality from C++11.
dynamic_cast can be used here, as long as the Advanced members you want to access are declared as public, or AnotherClass is declared as a friend of Advanced:
AnotherClass::lengthy_method(Basic *basic) {
// do a lot of processing, access members of basic
Advanced *adv = dynamic_cast<Advanced*>(basic);
if (adv != NULL) {
// use adv as needed...
}
// more processing just using Basic
if (adv != NULL) {
// use adv as needed...
}
// more processing just using Basic
}
Another option is to use polymorphism instead of RTTI. Expose some additional virtual methods in Basic that do nothing, and then have Advanced override them:
class Basic {
...
virtual void doSomething1() {}
virtual void doSomething2() {}
}
class Advanced : public Basic {
...
virtual void doSomething1();
virtual void doSomething2();
}
void Advanced::doSomething1() {
...
}
void Advanced::doSomething2() {
...
}
AnotherClass::lengthy_method(Basic *basic) {
// do a lot of processing, access members of basic
// do one or a few specific things with members of Advanced
basic->doSomething1();
// more processing just using Basic
// do one or a few specific things with members of Advanced
basic->doSomething2();
// more processing just using Basic
}
In the code I am now creating, I have an object that can belong to two discrete types, differentiated by serial number. Something like this:
class Chips {
public:
Chips(int shelf) {m_nShelf = shelf;}
Chips(string sSerial) {m_sSerial = sSerial;}
virtual string GetFlavour() = 0;
virtual int GetShelf() {return m_nShelf;}
protected:
string m_sSerial;
int m_nShelf;
}
class Lays : Chips {
string GetFlavour()
{
if (m_sSerial[0] == '0') return "Cool ranch";
else return "";
}
}
class Pringles : Chips {
string GetFlavour()
{
if (m_sSerial.find("cool") != -1) return "Cool ranch";
else return "";
}
}
Now, the obvious choice to implement this would be using a factory design pattern. Checking manually which serial belongs to which class type wouldn't be too difficult.
However, this requires having a class that knows all the other classes and refers to them by name, which is hardly truly generic, especially if I end up having to add a whole bunch of subclasses.
To complicate things further, I may have to keep around an object for a while before I know its actual serial number, which means I may have to write the base class full of dummy functions rather than keeping it abstract and somehow replace it with an instance of one of the child classes when I do get the serial. This is also less than ideal.
Is factory design pattern truly the best way to deal with this, or does anyone have a better idea?
You can create a factory which knows only the Base class, like this:
add pure virtual method to base class: virtual Chips* clone() const=0; and implement it for all derives, just like operator= but to return pointer to a new derived. (if you have destructor, it should be virtual too)
now you can define a factory class:
Class ChipsFactory{
std::map<std::string,Chips*> m_chipsTypes;
public:
~ChipsFactory(){
//delete all pointers... I'm assuming all are dynamically allocated.
for( std::map<std::string,Chips*>::iterator it = m_chipsTypes.begin();
it!=m_chipsTypes.end(); it++) {
delete it->second;
}
}
//use this method to init every type you have
void AddChipsType(const std::string& serial, Chips* c){
m_chipsTypes[serial] = c;
}
//use this to generate object
Chips* CreateObject(const std::string& serial){
std::map<std::string,Chips*>::iterator it = m_chipsTypes.find(serial);
if(it == m_chipsTypes.end()){
return NULL;
}else{
return it->clone();
}
}
};
Initialize the factory with all types, and you can get pointers for the initialized objects types from it.
From the comments, I think you're after something like this:
class ISerialNumber
{
public:
static ISerialNumber* Create( const string& number )
{
// instantiate and return a concrete class that
// derives from ISerialNumber, or NULL
}
virtual void DoSerialNumberTypeStuff() = 0;
};
class SerialNumberedObject
{
public:
bool Initialise( const string& serialNum )
{
m_pNumber = ISerialNumber::Create( serialNum );
return m_pNumber != NULL;
}
void DoThings()
{
m_pNumber->DoSerialNumberTypeStuff();
}
private:
ISerialNumber* m_pNumber;
};
(As this was a question on more advanced concepts, protecting from null/invalid pointer issues is left as an exercise for the reader.)
Why bother with inheritance here? As far as I can see the behaviour is the same for all Chips instances. That behaviour is that the flavour is defined by the serial number.
If the serial number only changes a couple of things then you can inject or lookup the behaviours (std::function) at runtime based on the serial number using a simple map (why complicate things!). This way common behaviours are shared among different chips via their serial number mappings.
If the serial number changes a LOT of things, then I think you have the design a bit backwards. In that case what you really have is the serial number defining a configuration of the Chips, and your design should reflect that. Like this:
class SerialNumber {
public:
// Maybe use a builder along with default values
SerialNumber( .... );
// All getters, no setters.
string getFlavour() const;
private:
string flavour;
// others (package colour, price, promotion, target country etc...)
}
class Chips {
public:
// Do not own the serial number... 'tis shared.
Chips(std::shared_ptr<SerialNumber> poSerial):m_poSerial{poSerial}{}
Chips(int shelf, SerialNumber oSerial):m_poSerial{oSerial}, m_nShelf{shelf}{}
string GetFlavour() {return m_poSerial->getFlavour()};
int GetShelf() {return m_nShelf;}
protected:
std::shared_ptr<SerialNumber> m_poSerial;
int m_nShelf;
}
// stores std::shared_ptr but you could also use one of the shared containers from boost.
Chips pringles{ chipMap.at("standard pringles - sour cream") };
This way once you have a set of SerialNumbers for your products then the product behaviour does not change. The only change is the "configuration" which is encapsulated in the SerialNumber. Means that the Chips class doesn't need to change.
Anyway, somewhere someone needs to know how to build the class. Of course you could you template based injection as well but your code would need to inject the correct type.
One last idea. If SerialNumber ctor took a string (XML or JSON for example) then you could have your program read the configurations at runtime, after they have been defined by a manager type person. This would decouple the business needs from your code, and that would be a robust way to future-proof.
Oh... and I would recommend NOT using Hungarian notation. If you change the type of an object or parameter you also have to change the name. Worse you could forget to change them and other will make incorrect assumptions. Unless you are using vim/notepad to program with then the IDE will give you that info in a clearer manner.
#user1158692 - The party instantiating Chips only needs to know about SerialNumber in one of my proposed designs, and that proposed design stipulates that the SerialNumber class acts to configure the Chips class. In that case the person using Chips SHOULD know about SerialNumber because of their intimate relationship. The intimiate relationship between the classes is exactly the reason why it should be injected via constructor. Of course it is very very simple to change this to use a setter instead if necessary, but this is something I would discourage, due to the represented relationship.
I really doubt that it is absolutely necessary to create the instances of chips without knowing the serial number. I would imagine that this is an application issue rather than one that is required by the design of the class. Also, the class is not very usable without SerialNumber and if you did allow construction of the class without SerialNumber you would either need to use a default version (requiring Chips to know how to construct one of these or using a global reference!) or you would end up polluting the class with a lot of checking.
As for you complaint regarding the shared_ptr... how on earth to you propose that the ownership semantics and responsibilities are clarified? Perhaps raw pointers would be your solution but that is dangerous and unclear. The shared_ptr clearly lets designers know that they do not own the pointer and are not responsible for it.
I have a C background and am a newb on C++. I have a basic design question. I have a class (I'll call it "chef" b/c the problem I have seems very analogous to this, both in terms of complexity and issues) that basically works like this
class chef
{
public:
void prep();
void cook();
void plate();
private:
char name;
char dish_responsible_for;
int shift_working;
etc...
}
in pseudo code, this gets implemented along the lines of:
int main{
chef my_chef;
kitchen_class kitchen;
for (day=0; day < 365; day++)
{
kitchen.opens();
....
my_chef.prep();
my_chef.cook();
my_chef.plate();
....
kitchen.closes();
}
}
The chef class here seems to be a monster class, and has the potential of becoming one. chef also seems to violate the single responsibility principle, so instead we should have something like:
class employee
{
protected:
char name;
int shift_working;
}
class kitchen_worker : employee
{
protected:
dish_responsible_for;
}
class cook_food : kitchen_worker
{
public:
void cook();
etc...
}
class prep_food : kitchen_worker
{
public:
void prep();
etc...
}
and
class plater : kitchen_worker
{
public:
void plate();
}
etc...
I'm admittedly still struggling with how to implement it at run time so that, if for example plater (or "chef in his capacity as plater") decides to go home midway through dinner service, then the chef has to work a new shift.
This seems to be related to a broader question I have that if the same person invariably does the prepping, cooking and plating in this example, what is the real practical advantage of having this hierarchy of classes to model what a single chef does? I guess that runs into the "fear of adding classes" thing, but at the same time, right now or in the foreseeable future I don't think maintaining the chef class in its entirety is terribly cumbersome. I also think that it's in a very real sense easier for a naive reader of the code to see the three different methods in the chef object and move on.
I understand it might threaten to become unwieldy when/if we add methods like "cut_onions()", "cut_carrots()", etc..., perhaps each with their own data, but it seems those can be dealt with by having making the prep() function, say, more modular. Moreover, it seems that the SRP taken to its logical conclusion would create a class "onion_cutters" "carrot_cutters" etc... and I still have a hard time seeing the value of that, given that somehow the program has to make sure that the same employee cuts the onions and the carrots which helps with keeping the state variable the same across methods (e.g., if the employee cuts his finger cutting onions he is no longer eligible to cut carrots), whereas in the monster object chef class it seems that all that gets taken care of.
Of course, I understand that this then becomes less about having a meaningful "object oriented design", but it seems to me that if we have to have separate objects for each of the chef's tasks (which seems unnatural, given that the same person is doing all three function) then that seems to prioritize software design over the conceptual model. I feel an object oriented design is helpful here if we want to have, say, "meat_chef" "sous_chef" "three_star_chef" that are likely different people. Moreover, related to the runtime problem is that there is an overhead in complexity it seems, under the strict application of the single responsibility principle, that has to make sure the underlying data that make up the base class employee get changed and that this change is reflected in subsequent time steps.
I'm therefore rather tempted to leave it more or less as is. If somebody could clarify why this would be a bad idea (and if you have suggestions on how best to proceed) I'd be most obliged.
To avoid abusing class heirarchies now and in future, you should really only use it when an is relationship is present. As yourself, "is cook_food a kitchen_worker". It obviously doesn't make sense in real life, and doesn't in code either. "cook_food" is an action, so it might make sense to create an action class, and subclass that instead.
Having a new class just to add new methods like cook() and prep() isn't really an improvement on the original problem anyway - since all you've done is wrapped the method inside a class. What you really wanted was to make an abstraction to do any of these actions - so back to the action class.
class action {
public:
virtual void perform_action()=0;
}
class cook_food : public action {
public:
virtual void perform_action() {
//do cooking;
}
}
A chef can then be given a list of actions to perform in the order you specify. Say for example, a queue.
class chef {
...
perform_actions(queue<action>& actions) {
for (action &a : actions) {
a.perform_action();
}
}
...
}
This is more commonly known as the Strategy Pattern. It promotes the open/closed principle, by allowing you to add new actions without modifying your existing classes.
An alternative approach you could use is a Template Method, where you specify a sequence of abstract steps, and use subclasses to implement the specific behaviour for each one.
class dish_maker {
protected:
virtual void prep() = 0;
virtual void cook() = 0;
virtual void plate() = 0;
public:
void make_dish() {
prep();
cook();
plate();
}
}
class onion_soup_dish_maker : public dish_maker {
protected:
virtual void prep() { ... }
virtual void cook() { ... }
virtual void plate() { ... }
}
Another closely related pattern which might be suitable for this is the Builder Pattern
These patterns can also reduce of the Sequential Coupling anti-pattern, as it's all too easy to forget to call some methods, or call them in the right order, particularly if you're doing it multiple times. You could also consider putting your kitchen.opens() and closes() into a similar template method, than you don't need to worry about closes() being called.
On creating individual classes for onion_cutter and carrot_cutter, this isn't really the logical conclusion of the SRP, but in fact a violation of it - because you're making classes which are responsible for cutting, and holding some information about what they're cutting. Both cutting onions and carrots can be abstracted into a single cutting action - and you can specify which object to cut, and add a redirection to each individual class if you need specific code for each object.
One step would be to create an abstraction to say something is cuttable. The is relationship for subclassing is candidate, since a carrot is cuttable.
class cuttable {
public:
virtual void cut()=0;
}
class carrot : public cuttable {
public:
virtual void cut() {
//specific code for cutting a carrot;
}
}
The cutting action can take a cuttable object and perform any common cutting action that's applicable to all cuttables, and can also apply the specific cut behaviour of each object.
class cutting_action : public action {
private:
cuttable* object;
public:
cutting_action(cuttable* obj) : object(obj) { }
virtual void perform_action() {
//common cutting code
object->cut(); //specific cutting code
}
}
How is this situation usually dealt with. For example, an object may need to do very specific things:
class Human
{
public:
void eat(Food food);
void drink(Liquid liquid);
String talkTo(Human human);
}
Say that this is what this class is supposed to do, but to actually do these might result in functions that are well over 10,000 lines. So you would break them down. The problem is, many of those helper functions should not be called by anything other than the function they are serving. This makes the code confusing in a way. For example, chew(Food food); would be called by eat() but should not be called by a user of the class and probably should not be called anywhere else.
How are these cases dealt with generally. I was looking at some classes from a real video game that looked like this:
class CHeli (7 variables, 19 functions)
Variables list
CatalinaHasBeenShotDown
CatalinaHeliOn
NumScriptHelis
NumRandomHelis
TestForNewRandomHelisTimer
ScriptHeliOn
pHelis
Functions list
FindPointerToCatalinasHeli (void)
GenerateHeli (b)
CatalinaTakeOff (void)
ActivateHeli (b)
MakeCatalinaHeliFlyAway (void)
HasCatalinaBeenShotDown (void)
InitHelis (void)
UpdateHelis (void)
TestRocketCollision (P7CVector)
TestBulletCollision (P7CVectorP7CVectorP7CVector)
SpecialHeliPreRender (void)
SpawnFlyingComponent (i)
StartCatalinaFlyBy (void)
RemoveCatalinaHeli (void)
Render (void)
SetModelIndex (Ui)
PreRenderAlways (void)
ProcessControl (void)
PreRender (void)
All of these look like fairly high level functions, which mean their source code must be pretty lengthy. What is good about this is that at a glance it is very clear what this class can do and the class looks easy to use. However, the code for these functions might be quite large.
What should a programmer do in these cases; what is proper practice for these types of situations.
For example, chew(Food food); would be called by eat() but should not be called by a user of the class and probably should not be called anywhere else.
Then either make chew a private or protected member function, or a freestanding function in an anonymous namespace inside the eat implementation module:
// eat.cc
// details of digestion
namespace {
void chew(Human &subject, Food &food)
{
while (!food.mushy())
subject.move_jaws();
}
}
void Human::eat(Food &food)
{
chew(*this, food);
swallow(*this, food);
}
The benefits of this approach compared to private member functions is that the implementation of eat can be changed without the header changing (requiring recompilation of client code). The drawback is that the function cannot be called by any function outside of its module, so it can't be shared by multiple member functions unless they share an implementation file, and that it can't access private parts of the class directly.
The drawback compared to protected member functions is that derived classes can't call chew directly.
The implementation of one member function is allowed to be split in whatever way you want.
A popular option is to use private member functions:
struct Human
{
void eat();
private:
void chew(...);
void eat_spinach();
...
};
or to use the Pimpl idiom:
struct Human
{
void eat();
private:
struct impl;
std::unique_ptr<impl> p_impl;
};
struct Human::impl { ... };
However, as soon as the complexity of eat goes up, you surely don't want a collection of private methods accumulating (be it inside a Pimpl class or inside a private section).
So you want to break down the behavior. You can use classes:
struct SpinachEater
{
void eat_spinach();
private:
// Helpers for eating spinach
};
...
void Human::eat(Aliment* e)
{
if (e->isSpinach()) // Use your favorite dispatch method here
// Factories, or some sort of polymorphism
// are possible ideas.
{
SpinachEater eater;
eater.eat_spinach();
}
...
}
with the basic principles:
Keep it simple
One class one responsibility
Never duplicate code
Edit: A slightly better illustration, showing a possible split into classes:
struct Aliment;
struct Human
{
void eat(Aliment* e);
private:
void process(Aliment* e);
void chew();
void swallow();
void throw_up();
};
// Everything below is in an implementation file
// As the code grows, it can of course be split into several
// implementation files.
struct AlimentProcessor
{
virtual ~AlimentProcessor() {}
virtual process() {}
};
struct VegetableProcessor : AlimentProcessor
{
private:
virtual process() { std::cout << "Eeek\n"; }
};
struct MeatProcessor
{
private:
virtual process() { std::cout << "Hmmm\n"; }
};
// Use your favorite dispatch method here.
// There are many ways to escape the use of dynamic_cast,
// especially if the number of aliments is expected to grow.
std::unique_ptr<AlimentProcessor> Factory(Aliment* e)
{
typedef std::unique_ptr<AlimentProcessor> Handle;
if (dynamic_cast<Vegetable*>(e))
return Handle(new VegetableProcessor);
else if (dynamic_cast<Meat*>(e))
return Handle(new MeatProcessor);
else
return Handle(new AlimentProcessor);
};
void Human::eat(Aliment* e)
{
this->process(e);
this->chew();
if (e->isGood()) this->swallow();
else this->throw_up();
}
void Human::process(Aliment* e)
{
Factory(e)->process();
}
One possibility is to (perhaps privately) compose the Human of smaller objects that each do a smaller part of the work. So, you might have a Stomach object. Human::eat(Food food) would delegate to this->stomach.digest(food), returning a DigestedFood object, which the Human::eat(Food food) function processed further.
Function decomposition is something that is learnt from experience, and it usually implies type decomposition at the same time. If your functions become too large there are different things that can be done, which is best for a particular case depends on the problem at hand.
separate functionality into private functions
This makes more sense when the functions have to access quite a bit of state from the object, and if they can be used as building blocks for one or more of the public functions
decompose the class into different subclasses that have different responsibilities
In some cases a part of the work falls naturally into its own little subproblem, then the higher level functions can be implemented in terms of calls to the internal subobjects (usually members of the type).
Because the domain that you are trying to model can be interpreted in quite a number of different ways I fear trying to provide a sensible breakdown, but you could imagine that you had a mouth subobject in Human that you could use to ingest food or drink. Inside the mouth subobject you could have functions open, chew, swallow...