Banner THe IT Service
Adresse:Friedrichsbrunner Straße 11, 12347 Berlin

Architekturmuster Composite Components in C++ – Böse und gute Abhängigkeiten (Teil 1.1)

Good Dependencies Classdiagram

Wie in der realen Welt gibt es in der Software-Entwicklung jede Menge Abhängigkeiten. In beiden Kontexten ist das Erkennen, Überblicken und Auflösen von Abhängigkeiten sehr komplex. Ein Ziel könnte es daher sein, gar nicht erst Abhängigkeiten entstehen zu lassen. Dies ist in der realen Welt und in der Software-Entwicklung nicht möglich, da ein gewisser Grad an Abhängigkeiten durchaus notwendig und unumgänglich ist. Die Klasse A braucht z.B. die Daten/Methoden von Klasse B oder die Komponente X benötigt Teile von Komponente Y. Diese Abhängigkeiten können als „gute Abhängigkeiten“ bezeichnet werden, aber wo es etwas Gutes gibt, ist das Böse nicht weit.

Die „bösen Abhängigkeiten“ machen die Software-Entwicklung an sich, die Implementierung von Features, sowie die Wartung der Software mit fortschreitender Lebensdauer so gut wie unmöglich bzw. so komplex, dass neue Features immer längere Entwicklungszeiten benötigen, die Wartung schwierig wird und am Ende das Software-Projekt mit damit verbundenen Kosten zum scheitern verurteilt ist.

Bad Dependency Classdiagram
Böse Abhängigkeiten Klassendiagramm
Bad Dependency Flowdiagram
Böse Abhängigkeiten Sequenzdiagramm

Erzeugungsabhängigkeiten in gute Abhängigkeiten umwandeln

Das UML Diagramm Böse Abhängigkeiten zeigt die Zusammenhänge zwischen Klasse A und Klasse B. Klasse A entscheidet selbst, wie sie Klasse B erzeugt und verwendet. Das bedeutet, dass sich ein Austausch der Funktionalität von Klasse B oder das separate Testen von Klasse A und B nur mit einigem Aufwand bewerkstelligen lässt. Eine Lösung für dieses Problem ist die Dependency Injection. Dabei wird einem Objekt der Klasse A ein Interface der Klasse B (InterfaceB) von außen z.B. über den Konstruktor indiziert und die Klasse A weiß bei deren Erzeugung gar nicht, welche Implementierung sie von Klasse B bekommt. Ein Interface, welches die öffentliche Funktionalität der Klasse B kapselt ist daher unumgänglich. Die folgenden UML-Diagramme zeigen eine mögliche Implementierung:

Good Dependencies Classdiagram
Gute Abhängigkeiten – Klassendiagramm
Good Dependencies Flowdiagram
Gute Abhängigkeiten – Sequenzdiagramm

In C++-Code könnte eine Implementierung so aussehen:

Codelisting 1: Gute Abhängigkeiten

#include <iostream>
#include <memory>

class InterfaceB
{
public:
    virtual ~InterfaceB() = default;
    virtual void foo() const = 0;
};

class KlasseB1 : public InterfaceB
{
public:
    KlasseB1()
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
    void foo() const override
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};

class KlasseB2 : public InterfaceB
{
public:
    KlasseB2()
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
    void foo() const override
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};

class KlasseA
{
private:
    std::shared_ptr<InterfaceB> interfaceB = {};
public:
    KlasseA(const std::shared_ptr<InterfaceB>& iB) :
        interfaceB(iB)
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
    void bar() const
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        if(interfaceB)
            interfaceB->foo();
    }
};

int main(int argc, char *argv[])
{
    auto klasseB1 = std::make_shared<KlasseB1>();
    auto klasseB2 = std::make_shared<KlasseB2>();
    auto klasseA1 = std::make_shared<KlasseA>(klasseB1);
    auto klasseA2 = std::make_shared<KlasseA>(klasseB2);
    klasseA1->bar();
    klasseA2->bar();
    return 0;
}

Hier die Ausgabe des Programms:

Terminalausgabe des Programms
Terminalausgabe des Programms “Gute Abhängigkeiten”

Fazit und Ausblick

In diesem Teil habe ich einen Hauptgrund für das Scheitern von Softwareprojekten benannt und eine mögliche Lösung für das Problem mit den “Bösen Abhängigkeiten” dargestellt. Sie ist die Grundlage dafür, dass die Software überhaupt erst wartbar, testbar und erweiterbar wird und das, in späteren Projektphasen, mit relativ wenig Aufwand. Im nächsten Teil steige noch etwas tiefer in die Materie ein und freue mich über Feedback.

To be continued…

THe IT Service – Tim Henckel
Kundenorientierte Softwareentwicklung, Webdesign & Hosting
Einfachheit ist das Ziel – Software ist die Lösung

Quellen

Clean C++ / Sustainable Software Development – Patterns and Best Practices / Stephan Roth / ISBN 978-1-4842-27-92-3

David Tielke – Freiberuflich/Selbstständig / Trainer & Berater / https://www.david-tielke.de

UML 2 kompakt / 3. Auflage / Heide Balzert / ISBN978-3-8274-2506-5

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.