Composition over encapsulation

aleksikauppila

Aleksi Kauppila

Posted on August 8, 2019

Composition over encapsulation

A big problem with inheritance is that it easily gets out of hand a becames an unmaintainable mess. It very quickly becomes a tree that reaches out to all different directions. Also, when you change a class, it may have unexpected side-effects on inheriting objects.

Many times you hear that you should prefer composition over inheritance. I definitely get that. It tackles a lot of problems what inheritance causes.

But what about encapsulation?

abstract class Report
{
    abstract protected function title(): string;
    abstract protected function contents(): string;

    public function display(): string
    {
        return sprintf('%s\n%s', this.title(), this.contents());
    }
}

class SalaryReport extends Report
{
    private string title;
    private array salaries;

    public function constructor(string title, array salaries)
    {
        this.title = title;
        this.salaries = salaries;
    }

    protected function title(): string
    {
        return this.title;
    }

    protected function contents(): string
    {
        return this.salaries.join('\n');
    }
}

If i'd choose composition over inheritance, i would have to loosen the encapsulation of SalaryReport.title() and SalaryReport.contents() to public. The code would turn into this.

interface ReportData
{
    public function title(): string;
    public function contents(): string;
}

class Report
{
    private ReportData reportData;

    public function constructor(ReportData reportData)
    {
        this.reportData = reportData
    }
    public function display(): string
    {
        return sprintf('%s\n%s', this.reportData.title(), this.reportData.contents());
    }
}

class SalaryReportData implements ReportData
{
    private string title;
    private array salaries;

    public function constructor(string title, array salaries)
    {
        this.title = title;
        this.salaries = salaries;
    }

    public function title(): string
    {
        return this.title;
    }

    public function contents(): string
    {
        return this.salaries.join('\n');
    }
}

Now SalaryReport isn't a real object anymore since it really hasn't got any notable behaviour, it's just data. Of course you could argue that Report doesn't do much either, but for the sake of the example let's go with this.

How can i use composition over inheritance without weakening encapsulation and turning my objects into data structures? It seems both approaches have severe drawbacks.

💖 💪 🙅 🚩
aleksikauppila
Aleksi Kauppila

Posted on August 8, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related