Talk:Composition over inheritance

From Wikipedia, the free encyclopedia
Jump to: navigation, search
WikiProject Computer science (Rated C-class, Low-importance)
WikiProject icon This article is within the scope of WikiProject Computer science, a collaborative effort to improve the coverage of Computer science related articles on Wikipedia. If you would like to participate, please visit the project page, where you can join the discussion and see a list of open tasks.
C-Class article C  This article has been rated as C-Class on the project's quality scale.
 Low  This article has been rated as Low-importance on the project's importance scale.
 

Bad?[edit]

"Using inheritance we either have to do multiple inheritance, which is bad,[vague]"

Why is it bad? Use multiple inheritance for the example given (using mix-in classes) seems like a much more conceptually cleaner way to do it. Is this referring to some performance/gotcha specific to C++.

This article doesn't outline a clear argument against multiple inheritance (specifically mix-ins), and reeks of opinion.

Also it seems to be very biassed to statically typed languages (C++).

For example in dynamic language (such as Python, Perl, etc) duck typing would be a much more appropriate solution to the example given.

It's not even clear in the C++ example why supplying delegate classes is any better than just defining the methods for each of the sub-interfaces (Visibility, Update, Collision) and using single inheritance. — unsigned comment by 121.45.113.228 on 2011-03-12

yes, bad[edit]

[the article] C++ and C# code snippets, even if of some use to those familiar with the languages, don't help others to understand this concept at all better. — Preceding unsigned comment added by 68.183.23.147 (talk) 22:27, 16 January 2013 (UTC)

I agree, there is waaaay too much code here. It should have a succinct, simple example using pseudocode, if anything. 213.216.142.12 (talk) 13:52, 4 October 2013 (UTC)

Confusion[edit]

I started off writing that just the diagram was confused but have come to the conclusion that whole article is.

Is the article trying to explain that 'HAS-A can be better than IS-A' or is it trying to explain that 'many narrow interfaces can be better than a single broad interface'?

From the title, it should surely be about the former, but it certainly seems to blur into the latter in places (without identifying when it is crossing the line...).

As for the diagram... it shows (by inheritance arrows) that a Duck IS-A Quackable and (transitively) IS-A Flyable, whereas the point of p.22..23 of Head First Design Patterns on which this diagram is based is that 'HAS-A can be better than IS-A'.

Conversely the diagram does not actually depict the HAS-A relaionship in a clear exemplary way at all.

What the diagram's author has done is make Duck inherit from (implement the interfaces of) Flyable and Quackable (whereas the Head First Design Patterns book does not do so). And by doing this the diagram confuses rather than clarifies the distinction betwen IS-A and HAS-A - because the diagram actually depicts that a Duck both IS-A and HAS-A Flyable and Quackable behaviour.

This is likely to confuse rather than aid understanding, I think.

Note that the code examples do not do the same thing - the composite does not inherit the interfaces of it's components. However the fact that it ends up with a series of single line 'forwarding' calls might make the reader wonder whether it might not be better if it did...

Suggested resolution: identify/segregate and reference the 'Interface segregation principle' wherever the article is in fact talking about it.

Boiler plate in the C++ example[edit]

The C++ example contains a lot of boiler plate code (which is probably essentially the same for every "interface" that is created). A lot of it can be removed by allowing the constructor of Object to be called with null pointers instead of new NotMovable(), new NotSolid() etc. For example, Object::update() would check the pointer _u and only if _u is non-null it would call _u->update(). Then only the interfaces that actually do something (Movable, Solid etc.) have to be defined. For readibility and type safety, Movable * const NotMovable=0; etc. should probably be defined at namespace scope anyway. With this definition, the constructor of the Smoke class would be Smoke():Object(new Visible(), new Movable(), NotSolid) {}; for example.

This solution has less lines of code, but is no less readable, and causes less namespace polution (no Delegates), so I wonder: Is there a reason why it is not advocated in the "Composition over inheritance" approach in C++? — Preceding unsigned comment added by Matthieumarechal (talkcontribs) 16:11, 19 February 2014 (UTC)

The C# example demonstrates code reuse and polymorphism ?[edit]

How can i more easily reuse the classes Player, Cloud, Building and Trap in the C#/composition example than the same classes in the C++/inheritance example?



As for polymorphism, shouldn't the class GameObject explicitly declare implementation of the interfaces IVisible, ICollidable, IUpdatable? - that is:

  class GameObject : IVisible, IUpdatable, ICollidable { ... }

.

If not,

then i can define and run through a collection of GameObject subtype instances - say:

  GameObject[] t_go = { new Building(), new Trap(), new Player(), new Player() };
  foreach ( GameObject obj in t_go )
      obj.Draw();

but i cannot define a collection of any "interface instances" - at least, not with instances of Player / Cloud / Building / Trap - like:

  IVisible[]  = { new Building(), new Trap(), new Player(), new VisibleThingButNotGameObject() };   /* won't compile */


Zsught (talk) 17:22, 28 June 2015 (UTC)

Business domain[edit]

Why is there talk of the business domain? Is there anything peculiar to business domains and composition?

Why are there examples in different languages? As if inheritance is for C++ programs and composition for C# programs.

It would also be better if the diagram at the top was for the code below. 87.102.44.18 (talk) 16:21, 7 November 2015 (UTC)

Drawback example[edit]

The drawback example seems bogus to me, but maybe I'm misunderstanding. The drawback is described but the description doesn't make sense to me and example that follows doesn't demonstrate the drawback. Wouldn't the composition-based solution be to give Employee an object that implements an IPayable interface and which only has a pay() method?

192.234.2.90 (talk) 15:39, 19 February 2016 (UTC)

Drawbacks?[edit]

I don't understand the example under "Drawbacks". Couldn't we just use the same method as in the previous example to only duplicate the Pay method?

// The interface calculating pay
interface IPayable
{
    // Get pay for the current pay period
    // Since we don't have access to m_payRate and m_hoursWorked,
    // we need to include them as method parameters.
    decimal Pay(decimal m_payRate, int m_hoursWorked);
}

// Interface implementation
class HourlyPay: IPayable
{
    // Get pay for the current pay period
    public decimal Pay(decimal m_payRate, int m_hoursWorked)
    {
        // Time worked is in hours
        return m_hoursWorked * m_payRate;
    }
}

// Interface implementation
class SalariedPay: IPayable
{
    // Get pay for the current pay period
    public decimal Pay(decimal m_payRate, int m_hoursWorked)
    {
        // Pay rate is annual salary instead of hourly rate
        return m_hoursWorked * m_payRate/2087;
    }
}

// Base class
public abstract class Employee
{
    // Member variables
    protected string    m_name;
    protected int       m_id;
    protected decimal   m_payRate;
    protected int       m_hoursWorked;
    private readonly IPayable _p;

    // Get/set the employee's name
    public string Name
    {
        get { return m_name; }
        set { m_name = value; }
    }

    // Get/set the employee's ID
    public int ID
    {
        get { return m_id; }
        set { m_id = value; }
    }

    // Get/set the employee's pay rate
    public decimal PayRate
    {
        get { return m_payRate; }
        set { m_payRate = value; }
    }

    // Get hours worked in the current pay period
    public int HoursWorked()
    {
        return m_hoursWorked;
    }

    // Get pay for the current pay period
    public decimal Pay()
    {
        return _p.Pay(m_hoursWorked, m_payRate);
    }

    public Employee(IPayable payable) {
        _p = payable;
    }
}

// Derived subclass
public HourlyEmployee: Employee
{
    public HourlyEmployee() : base(new HourlyPay()) { }
}

// Derived subclass
class SalariedEmployee: Employee
{
    public SalariedEmployee() : base(new SalariedPay()) { }
}

Sure, we had to change the method signature a bit for the interface, but it's still cleaner than the original? (Since the subclasses doesn't touch any of the internals of the superclass) Anka.213 (talk) —Preceding undated comment added 00:35, 29 August 2016 (UTC)