Вопрос

Lets say I have a class Person.

class Person{

    Person(std::string, int);
    void walk();
    void talk();
    void eat();

private:
    int age;
    std::string() name;

};

Now lets say I want to add personality to this person object. How should add this feature to this person?

I was thinking of inheritance

class Persona: public Person{

    void charisma();
    void voice();
    void style();
}; 

or should I use composition?

class Persona{

    void charisma();
    void voice();
    void style();
}; 

class Person{
 .....
private:
    int age;
    std::string name;
    Persona _persona;
};

or should I just add the new feature directly to the class Person, which I really don't want because this will make my class Person big and clumsy.

One thing that I came across was Decorator Pattern but I am still wondering if this pattern is of any help for me.

Это было полезно?

Решение

This is an almost philosophical question.

If you use inheritance:

  • the instantiations of Persona will be very stereotyped persons. More variety would require more sub-typing, giving you very rigid character traits.
  • the inheritance would not reflect the interdependency between the functions. For example, talk() would not depend on voice() unless you'd override talk(). And style() could depend on walk() but walk() should certainly also depend on style()

If you use composition:

  • You could refine and subclass Persona further and populate it using a factory. Similarly, you could independently subclass Person further. This could allow for a bigger variety of persons more easily than with inheritance.
  • The dependency of talk() on voice() and walk() on style() would be easily implemented. However, there is still no interdependency.

Can the decorator help ? Sure ! A decorator is an alternative to subclassing for extending functionality (see GoF). Combining it with your composition approach, you could further variate the Person. It is very flexible. However, you still refine something by adding responsibility or overriding methods.

If you'd want something more creative, you could inspire your design from a technique commonly used in game coding: make your person a collection of components. Each behavior or property would have its own component. You could then easily add new character traits (e.g. introvert, extrovert). You could then combine dynamically the components (e.g. for talk(), look for the voice component for audio rendering, and look at other character traits to decide on strength (volume) of voice, and length of sentences.

Look also at this answer related to design patterns for game characters and this answer on implementing such a design. You'll find there additional references to this architecture, promoted among others by McShaffry.

If you want another alternative

Другие советы

The traditional way of framing this decision is the distinction between "is-a" and "has-a". Is a persona a special type of a person, or is a persona something that a person has? "Is-a" makes inheritance a reasonable starting place, and "has-a" points to composition.

You're correct to avoid making Person too "large" in terms of the data that objects of its type will contain.

How you should structure the solution depends in part on whether you intend for all Person objects to have a personality. If you do, I'd strongly favor composition. Otherwise, the Person class is redundant. Inheritance is not the best idea for reducing the complexity of classes.

However, if you want some Person objects to not have a personality, inheritance like you describe is one straightforward approach.

Лицензировано под: CC-BY-SA с атрибуция
scroll top