Open Closed Principle (OCP) – Wissenshäppchen #4

Dieser Beitrag ist Teil 4 von 8 in der Serie Wissenshäppchen.

Mein viertes Wissenshäppchen hat das Open Closed Principle zum Thema.

Probeabo bei Audible (Affiliate)

Inhalt

Das OCP ist das zweite der SOLID-Prinzipien. Es wurde vor Robert „Uncle Bob“ Martin bereits 1988 von Bertrand Meyer definiert:

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

Ursprünglich ging es darum, durch Vererbung die Anpassung („modification“) bereits bestehender Klassen zu verhindern, indem Subklassen abgeleitet werden, die die vorhandene Funktionalität erweitern („extension“). Heutzutage ist Vererbung aber eher nicht mehr so gerne gesehen, da es eine starke Kopplung an die Basisklasse zur Folge hat, was das Design inflexibel macht. Stattdessen sollte man besser von vornherein Interfaces mit potentiell verschiedenen Implementierungen einsetzen.

Erklärung

  • Bei Änderungen an einem Programm sollen bestehende Komponenten bestenfalls gar nicht angepasst werden müssen, da diese Anpassungen Fehler nach sich ziehen können, die auch das bereits bestehende Verhalten betreffen.
  • Neue Funktionalität wird dem Programm immer durch neue Komponenten hinzugefügt, also z.B. indem neue Klassen oder Methoden ergänzt werden.
  • Fallunterscheidungen auf Basis von Typen (z.B. mit instanceof in Java) oder Enumerations (insb. wenn diese in einem switch verwendet werden) sind oft ein Hinweis auf Verletzung des OCP und sollten durch Polymorphie ersetzt werden.

Beispiel

class Manager
end

class Developer
end

class Payroll
    def self.calculate_bonus(employee)
        if employee.is_a?(Manager)
            return 1000
        end
        if employee.is_a?(Developer)
            return 500
        end
    end
end

puts Payroll.calculate_bonus(Manager.new) # 1000
puts Payroll.calculate_bonus(Developer.new) #500

Wenn diese Bonusberechnung um einen neuen Mitarbeitertyp Administrator erweitert werden soll, muss die bestehende Implementierung von calculate_bonus() angepasst werden:

def self.calculate_bonus(employee)
    if employee.is_a?(Manager)
        return 1000
    end
    if employee.is_a?(Developer)
        return 500
    end
    if employee.is_a?(Administrator)
        return 250
    end
end

Stattdessen sollte von Anfang an die Berechnung des Bonuses an die Mitarbeiterklassen delegiert werden.

class Manager
    def bonus
        1000
    end
end

class Developer
    def bonus
        500
    end
end

class Payroll
    def self.calculate_bonus(employee)
        employee.bonus
    end
end

puts Payroll.calculate_bonus(Manager.new) # 1000
puts Payroll.calculate_bonus(Developer.new) # 500

Dadurch ist eine spätere Erweiterung um neue Mitarbeitertypen ohne Anpassung des bisherigen Codes möglich:

class Administrator
    def bonus
        250
    end
end

puts Payroll.calculate_bonus(Administrator.new) # 250

In dynamisch typisierten Sprachen (wie im Beispiel Ruby) ist das OCP übrigens meist noch einfacher umzusetzen als in statisch typisierten, da durch das Duck-Typing umfangreiche Klassenhierarchien oder Interfaces unnötig werden. Dies sieht man auch im Beispiel oben: Manager und Developer haben keinerlei Beziehung zueinander. Trotzdem funktioniert der Code, da beide Klassen die Methode bonus() anbieten.

Vorteile

  • Bei Erweiterungen der Funktionalität sinkt die Wahrscheinlichkeit, Fehler in den bereits funktionierenden Code einzubauen.
  • Klassen behalten ihre Aufgabe „ein Leben lang“. Abhängige Klassen können sich darauf verlassen, dass sich die Funktionalität nicht ändert.
  • Erweiterungen sind ggfs. einfacher zu implementieren, da nicht an mehreren Stellen Code angepasst, sondern „nur“ neuer Code geschrieben werden muss.

Nachteile

  • Meist ist gar nicht im Voraus bekannt, welche Stellen im Code später einmal erweitert werden müssen. Einfach nur noch mit Abstraktionen zu arbeiten, macht den Code aber schwer verständlich. Außerdem verletzten wir damit das YAGNI-Prinzip.

Literaturempfehlungen

Da Uncle Bob die SOLID-Prinzipien so schön aufgeschrieben hat, kann ich wieder einmal seinen Klassiker Clean Code* empfehlen.

Robert C. Martin - Clean Code: A Handbook of Agile Software Craftsmanship (Affiliate)*

Links

Probeabo bei Audible (Affiliate)

Navigation der Serie<< Single Responsibility Principle (SRP) – Wissenshäppchen #3Liskov Substitution Principle (LSP) – Wissenshäppchen #5 >>
Polyglot Clean Code Developer
About the Author
Ausbildungsleiter für Fachinformatiker Anwendungsentwicklung und Systemintegration, IHK-Prüfer und Hochschuldozent für Programmierung und Software-Engineering.

2 comments on “Open Closed Principle (OCP) – Wissenshäppchen #4

  1. Flo sagt:

    Die SOLID-Prinzipien wurden zwar von Uncle Bob aufgeschrieben, aber der Begriff stammt von Michael Feathers. 😉

    Kann es sein, dass das OCP in einigen Situationen konträr zum SRP ist? Wenn ich bspw. eine Angestellter-Klasse habe, muss ich sie jetzt mit allen möglichen polymorphen Methoden versehen, z.B. getGehalt(), getUrlaubstage() etc. Somit führe ich doch wieder Verantwortung ein, die die Klasse nicht unbedingt haben sollte?

  2. Stefan Macke sagt:

    Hallo Flo, ja das stimmt! Die Prinzipien alle gleichzeitig einzuhalten wird wohl schwierig bis unmöglich. Ich sehe sie auch nur als Richtlinie zur Orientierung beim eigenen Design. Je nach Anwendungsfall kann es ja auch sein, dass sich die Klassen so gut wie nie ändern oder erweitern werden. Dann sind auch die jeweiligen Prinzipien irrelevant.

Schreibe einen Kommentar

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

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax