Open Closed Principle (OCP) – Wissenshäppchen #4
IT-Berufe-Podcast - En podcast af Stefan Macke - Mandage
Kategorier:
Mein viertes Wissenshäppchen hat das Open Closed Principle zum Thema. 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“.