Temporary Field
Beschreibung
Ein Field (oder auch Member-Variable genannt) wird in der Klasse vorgehalten aber nur temporär verwendet. Das heißt die Verwendung der Member-Variable und ihre Lifetime stimmt nicht mit der Lifetime des Domänenobjekts überein, sondern wird nur temporär verwendet und ist dementsprechend nur temporär zu einem bestimmten Zeitpunkt gültig und valide.
Beispiel:
Schlechter Code | Guter Code |
class Velocity
{ private double curVelocity; private double curAcceleration; public double CalcVelocityMeterPerSec(double acceleration, int accelerationTime) { this.curAcceleration = acceleration; this.curVelocity = CalculateVelocity(accelerationTime); return this.curVelocity / 3.6; } public double CalculateVelocity(int accelerationTime) { curVelocity = curVelocity + curAcceleration * accelerationTime; return curVelocity; } … } |
class Velocity
{ public double CalcVelocityMeterPerSec(double velocity, double acceleration, int accelerationTime) { velocity= CalculateVelocity(velocity, acceleration, accelerationTime); return velocity / 3.6; } public double CalculateVelocity(double velocity, double acceleration, int accelerationTime) { velocity = velocity + acceleration * accelerationTime; return velocity; } … } |
Warum ist der Source-Code schlecht?
Ein Software-Engineer, der ein Objekt und seine Member-Variablen verwendet, möchte nicht zunächst den Source-Code analysieren, um herauszufinden ob eine Member-Variable unter allen Umständen in einen validen State ist oder nicht. Er rechnet damit, dass die Lifetime der Member-Variablen parallel zum Domänenobjekt existieren und die Daten nicht nur temporär sind. Durch Temporary Fields wird dieses Bedürfnis verletzt und der aufrufende Software-Engineer muss den komplexen und schwer zu verstehenden Source-Code analysieren. Temporary Fields sind sehr fehleranfällig und führen zu schwerer Testbarkeit und Wartbarkeit.
Wie wird man den Code-Smell los?
Transformiere die temporäre Member-Variable in eine Parameter-Variable der Methode und/oder einen Return-Wert. Wenn möglich und nötig kann auch die temporäre Member-Variable in eine eigene Klasse ausgelagert werden.
Wie entsteht der Code-Smell?
Der Code-Smell entsteht durch fehlendes Knowhow des Software-Engineers über Member-Variablen und Klassen bzw. Objekt-Orientierung. Es wird versucht Member-Variablen als Umgehungsweg zwischen Methoden zu verwenden und um die Anzahl von Methodenparameter zu verringern.
Wann ist es OK?
Bei einer Lazy-Inititialisierung der Klasse entstehen ebenfalls temporäre Member-Variablen, hier ist es aber meist kein Problem, weil die Member-Variablen typischerweise mit Getter und Setter gegen externen und internen Zugriff gekapselt sind.
Deep Inheritance Hierarchy
Beschreibung
Die Klassenhierarchie eines Systems ist extrem tief, sodass für kleinste Abwandlungen eigene Klassen angelegt werden. Als bestes Beispiel könnte man Fahrzeuge nehmen, bei denen für jede mögliche Sonderausstattung eine eigene Klasse angelegt wird. So gibt es den VW-Golf-Normal, den VW-Golf-ESP, den VW-Golf-Klima-und-Sicherheit etc.
Beispiel:
Car
–>CompactCar
–>VwGolf
–>VwGolfEsp
–>VwGolfAirconditionAndSecurity
–>…
Warum ist der Source-Code schlecht?
Vererbung und Objektorientierung machen dann Sinn, wenn der Unterschied zur Elternklasse wirklich so gravierend ist, dass man von einem neuen Objekt ausgeht.
Gefäß à Tasse, Glas, Krug, etc. Alle Kindklassen haben einen eigenen Zweck und ein eigenes Einsatzgebiet. Ein VW-Golf wird unabhängig von der Ausstattung immer als VW-Golf bezeichnet obwohl vielleicht technisch etwas ganz anderes dahinter steckt.
Um eine abgeleitete Kindklasse verstehen zu können muss ein Software-Engineer immer auch die darüberliegenden Elternklassen verstehen. Je mehr abgeleiteten Klassen in der Hierarchie existieren, desto schwieriger und komplexer wird das. Des Weiteren schadet es auch der Testbarkeit und ist fehleranfälliger, weil beispielsweise abstrakte Klassen nicht instanziiert werden können und nicht dementsprechend getestet werden können. Änderungen in Elternklassen haben außerdem immer unmittelbare Auswirkungen auf Kindklassen und dies kann ebenfalls zu höherer Fehlerwahrscheinlichkeit führen. Es wird hier vor allem auch das KISS-Principle verletzt.
Wie wird man den Code-Smell los?
Am besten werden Ableitungen in die Elternklasse gehoben und mittels anderer Methoden und Designpatterns wie Annotations, Attribute, States etc. gelöst.
Wie entsteht der Code-Smell?
Der Code-Smell entsteht durch Overengineering des Systems und Big-Bang Programmierung bei der gleich zu Beginn das System geplant wird. Meist entsteht dies, wenn Software-Architekten und Software-Engineer getrennte rollen sind und hier die Zusammenarbeit nicht gut funktioniert und der Software-Architekt das System detailgetreu plant und dies „umsetzen lässt“
Wann ist es OK?
Wenn die Klassenhierarchie die echten Domänenobjekte abbildet ist eine tief verschachtelte Hierarchie durchaus möglich und üblich.
Unnecessary Inheritance
Beschreibung
Eine wichtige Regel in der objektorientierten Programmierung ist es Objektkomposition vor Klassenvererbung zu bevorzugen. Dieser Code-Smell verletzt diese Regel. Es wird Vererbung eingesetzt und von einer Elternklasse geerbt, anstatt die Aufgabe mittels Komposition zu lösen. Die Kindklasse überschreibt dabei keine Methoden der Elternklasse und wird nicht an ein anderes Objekt als Typ des Elternobjekts in einem Methodenparameter weitergegeben.
Beispiel:
Schlechter Code | Guter Code |
class Car
{ public virtual void Drive (); } class ElectricCar : Car { } class TeslaModelS : ElectricCar { public override void Drive() { … } } |
class Car
{ public virtual void Drive (); } class TeslaModelS : Car { public override void Drive() { … } } |
Warum ist der Source-Code schlecht?
Jede nicht notwendige Vererbung ist schlecht, weil es den Code schwieriger zu lesen macht und unnötig die Komplexität erhöht. Des Weiteren werden Elternklassen stark mit den Kindklassen gekoppelt was wiederum ein leichtgewichtiges Design der Kindklassen erschwert. Das Testen der Funktionalität wird ebenfalls erschwert, weil von Funktionalität von den Elternklassen nicht mittels Mocks abgekapselt und ausgelassen werden können. Da sich die Komplexität erhöht, erhöht sich automatisch auch die Fehlerwahrscheinlichkeit. Es kann auch sein, dass das Liskovsche-Substitutions-Prinzip verletzt wird, wenn die Kindklasse in einen Kontext verwendet wird, in der eigentlich die Elternklasse erwartet wird (Verwechslung zwischen Car und ElectricCar).
Wie wird man den Code-Smell los?
Den Code-Smell wird man los, indem die unnötige Vererbung eliminiert wird und durch eine Komposition aufgelöst wird. Die Kindklasse erbt direkt von der darüberliegenden Klasse.
Wie entsteht der Code-Smell?
Der Code-Smell entsteht durch eine Zweckentfremdung und Missbrauch von Vererbung und Objektorientierung zu Gunsten von Code-Reduktion.
Wann ist es OK?
Der Code-Smell sollte nicht im produktiven Source-Code vorkommen, bei der Entwicklung von Test-Klassen ist es aber durchaus tolerierbar.
Simulated Polymorphic Behavior
Beschreibung
Bei diesem Code-Smell wird polymorphes Verhalten durch wiederkehrenden Einsatz von Switch-Case-Statements oder If-Else-Kaskaden nachgeahmt. Grundlegende Features und Möglichkeiten, die die Objektorientierung bietet, werden nicht verwendet und der Code wird dementsprechend komplexer und aufgeblasen.
Beispiel:
Schlechter Code | Guter Code |
public string GetCarInfos(Car car)
{ if(car is SUV) { return „Type: SUV Model: “ + car.Model + “ Engine: “ + car.Engine + “ SerialNumber “ + car.SerialNumber + “ HorsePower: “ + car.HorsePower; } else if(car is ElectricCar) { return „Type: Electric Model: “ + car.Model + “ Engine: “ + car.Engine + “ SerialNumber “ + car.SerialNumber + “ Battery “ + car.Battery; } else if(car is Van) { return „Type: Van Model: “ + car.Model + “ Engine: “ + car.Engine + “ SerialNumber “ + car.SerialNumber + “ Max Weight “ + car.MaxWeight; } return „Type: “ + Type + “ Model: “ + car.Model + “ Engine: “ + car.Engine + “ SerialNumber “ + car.SerialNumber; } |
class Car
{ public string Type { get; set; } public string Engine { get; set; } public string SerialNumber { get; set; } public string Model { get; set; } public virtual string GetCarInfos() { return „Type: “ + Type + “ Model: “ + car.Model + “ Engine: “ + car.Engine + “ SerialNumber “ + car.SerialNumber; } } class SUV : Car { public string HorsePower { get; set; } public override string GetCarInfos() { return base.GetCarInfos()+ “ HorsePower: “ + HorsePower; } } class ElectricCar : Car { public string Battery { get; set; } public override string GetCarInfos() { return base.GetCarInfos()+ “ Battery: “ + Battery; } } class Van : Car { public string MaxWeight { get; set; } public override string GetCarInfos() { return base.GetCarInfos()+ “ MaxWeight: “ + MaxWeight; } } |
Warum ist der Source-Code schlecht?
Der Code-Smell führt dazu, dass Abläufe und Funktionen verteilt im System vorliegen. Er macht daher den Code schwer zu lesen und zu verstehen. Soll zukünftig der Source-Code gewartet und Funktionalität erweitert werden, so muss dies an mehreren Stellen gemacht werden. Dies führt zu einer gesteigerten Fehlerwahrscheinlichkeit und Fehleranfälligkeit und macht dem Source-Code extrem schwer zu warten, weil es leicht möglich sein kann, dass die Ausgelagerten Funktionalitäten übersehen werden. Es werden grundlegende Möglichkeiten, die die Objektorientierung bietet, nicht verwendet. Außerdem verletzt der Code-Smell das D.R.Y. Prinzip und das Open-Closed-Principle.
Wie wird man den Code-Smell los?
Den Code-Smell wird man los, indem Objektorientierung richtig verwendet wird und die Funktionalität in die Vererbungshierarchie eingepflegt wird und Polymorphismus verwendet wird. Gegebenenfalls und wenn nötig können die Subtypen mit dem Factory-Pattern erzeugt werden. Wenn unterschiedliche Aktionen basierend auf dem Typ des Objekts ausgeführt werden müssen, empfiehlt sich auch ein Visitor-Pattern.
Wie entsteht der Code-Smell?
Der Code-Smell entsteht durch eine falsche Verwendung von Vererbung und Objektorientierung. Meist Denken die Software-Engineers die diesen Code-Smell einbauen prozedural, anstatt objektorientiert und wollen die gesamte Funktionalität an einem Ort gebunden haben, ohne dabei aber höhere Abstraktionsebenen und polymorphe Vererbungen zu berücksichtigen.
Wann ist es OK?
Im Normalfall sollte dieser Code-Smell nicht vorkommen, es kann aber sein, dass ein Refactoring und ein Bewegen der Logik in die Sub-Klassen nur dazu führt, dass die Komplexität der Lösung extrem steigt und die Kosten für dieses Refactoring überwiegen und die Vorteile zu gering erscheinen – dann sollte nicht refactored werden
Refused Bequest
Beschreibung
Dieser Code-Smell bedeutet, dass eine abgeleitete Kindklasse „das Erbe“ der Elternklasse „verweigert“. Eine Subklasse leitet von der Elternklasse ab, ruft aber nicht die Base-Methode auf und verhält sich ggf. anders als erwartet.
Beispiel:
Schlechter Code | Guter Code |
class ThingsWithEngine { protected int Velocity = 0; protected int EnginePower { get; set; } protected int ResolutionsPerMinutes {get; set; } protected int Position = 0; public virtual void Drive(int time ) { Position = Position + Velocity * time; } } class Car : ThingsWithEngine { private Point PositionPoint = new Point(0,0); private Vector MoveVector = new Vector(1,2); public Car() { ResolutionsPerMinutes = 3000; EnginePower = 310; Position = 12; Velocity = 10; } public override void Drive(int time) { //Position is not updated PositionPoint = new Point(Position.X + MoveVector.X * Velocity, Position.Y + MoveVector.Y * Velocity); } } class Hairdryer : ThingsWithEngine { public Hairdryer() { ResolutionsPerMinutes = 200; EnginePower = 1; Position = 1; Velocity = 0; } public override void Drive(int time) { throw new NotSupportedException(„Can’t drive“); } } |
class Engine { protected int EnginePower { get; set; } protected int ResolutionsPerMinutes {get; set; } } class Car { private Point PositionPoint = new Point(0,0); private Vector MoveVector = new Vector(1,2); private Engine Engine = new Engine(); public Car() { Engine = new Engine(); Engine.ResolutionsPerMinutes = 3000; Engine.EnginePower = 310; } public override void Drive(int time) { PositionPoint = new Point(Position.X + MoveVector.X * Velocity, Position.Y + MoveVector.Y * Velocity); } } class Hairdryer { private Engine Engine = new Engine(); public Hairdryer() { Engine = new Engine(); Engine.ResolutionsPerMinutes = 200; Engine.EnginePower = 1; } public override void Blowdry(int time) { …; } }
|
Warum ist der Source-Code schlecht?
Der Code-Smell modifiziert das verhalten der Elternklasse oder verweigert die Implementierung einer abgeleiteten Methode und verhindert somit dass Funktionalität von der Basisklasse aufgerufen und ausgeführt wird. Das führt dazu dass Trigger die auf Statusänderungen in der Elternklasse gesetzt sind nicht mehr funktionieren und spätere Änderungen in der Elternklasse nicht in der Kindklasse wirksam werden. Somit ist der Code schlecht verständlich, wartbar und fehleranfällig. Potentiell wird auch das Liskovsche-Substitutions-Prinzip verletzt. Meist weist die Existenz dieses Code-Smells auch auf einen Fehler im objektorientierten Design des Source-Codes hin.
Wie wird man den Code-Smell los?
Wenn möglich rufe die Basis-Methode der Elternklasse auf oder verbessere das Design der Klassenhierarchie und Struktur des Source-Codes durch Verändern der Vererbungshierarchie oder gegebenenfalls verwenden von Komposition. Bevorzuge immer Komposition über Vererbung.
Wie entsteht der Code-Smell?
Der Code-Smell entsteht, weil der Software-Engineer zum Zwecke der Code-Reduktion die Möglichkeiten der Vererbung ausnutzt, jedoch Grundregeln der Polymorphie und der Objektorientierung verletzt. Oft wird auch einfach vergessen die Basisklasse und die Basis-Methode zu callen.
Wann ist es OK?
Es ist OK nicht die Basis-Methode zu callen wenn die Methode keine Änderungen an States in der Klasse bewirkt, dies ist beispielsweise bei ToString() der Fall. Ein weiteres Beispiel wäre bei der Implementierung von Framework-Methoden die sonst eine NotSupportedException oder eine NotImplementedException schmeißen würden.
Alle Code-Smells im praktischen eBook: F*ck it smells – Clean Code in C#
Weitere Blogs zum Thema Softwarequalität
Code-Smells: Confusers |
Code-Smells: Bloaters |
|
![]() |
![]() |
Weitere Buchempfehlungen
[Links zu Amazon]
![]() |
||
[Amazon 9,99€] | [Amazon 34,99 €] | [Amazon 24,95 €] |
[Amazon 34,00 €] | [Amazon 25,99 €] | [Amazon 42,00 €] |
Zum Autor:
David Theil aus Linz Oberösterreich ist Digitalisierungs-Coach, Software-Engineer und als Head of Software-Development für über 30 Softwareentwickler verantwortlich. Beruflich beschäftigt er sich bereits jahrelang mit der Digitalisierung und hat bereits bei vielen Digitalisierungs-Projekten in der Wirtschaft federführend mitgewirkt. Er bewegt sich in Themen wie Digitalisierung, IoT, oder Industrie 4.0 sowohl beratend als auch praktisch mit echten Lösungen.
https://www.xing.com/profile/David_Theil
https://www.linkedin.com/in/david-theil-1a4190148/
https://www.linkedin.com/groups/8678887