# Metriken
Macht Software anhand von Maßzahl messbar bzw. vergleichbar. Wir schauen auf Qualitäts-Metriken v. Programmcode
- Koppelung
- Kohäsion
# Koppelung
Maß der Abhängigkeit zwischen Softwareelementen.
Anstreben von: Loose Coupling
Es gibt 2 Stufen von Koppelung:
- Tight Coupling
- ”starke Koppelung”
tritt auf, wenn eine
Klasse direkt auf Implementierungen anderer Klassen zugreift
= Wartbarkeit wird beeinträchtigt
- ”starke Koppelung”
tritt auf, wenn eine
- Loose Coupling
- ”schwache Koppelung”
Klassen haben nur minimale Kenntnisse zu anderen Klassen
Zugriff auf
Funktionalität läuft über Interfaces
= flexibel & wartungsfreundlich
- ”schwache Koppelung”
Klassen haben nur minimale Kenntnisse zu anderen Klassen
Zugriff auf
2 unterschiedliche Arten v. Koppelung:
- Interaktionskoppelung
- Maß an Funktionalität eines anderen Objekt, Klasse
- tritt auf, wenn Objekte einer Klasse, Methoden von Objekten anderer Klassen aufrufen
- Vererbungskoppelung
- Ausmaß der Abhängigkeit der erbenden & der Basisklasse (falsche Vererbung)
- tritt auf, wenn ein Objekt einer Klasse direkt auf ein anderes Objekt einer Klasse verweist (erbt)
# Interaktionskoppelung
Passiert bei direkter Abhängigkeit von einer Klasse auf eine andere Klasse. Das macht beide Klassen voneinander abhängig.
Einfache Beispiele:
// Direkte Abhängigkeit
// Änderungen in B können dazu führen, dass auch A geändert werden muss
public class A { private B b = new B(); }
// Direkte Nutzung
// Erneut: A ist davon abhängig, wie B implementiert ist
public class A
{
public void DoSomething(B b) => b.SomeMethod();
}
public class Portfolio
{
private string Name { get; set; }
public decimal LastNotifiedPrice { get; private set; }
public Portfolio(string name)
{
Name = name;
}
public void NotifyPriceChange(decimal newPrice)
{
LastNotifiedPrice = newPrice;
Console.WriteLine($"Portfolio '{Name}' wurde über den neuen Preis {newPrice:C} informiert.");
}
}
public class StockMarket
{
private decimal _price;
protected List<Portfolio> Portfolios { get; set; } = new();
public decimal Price
{
set
{
_price = value;
// Interaktionskoppelung:
// Wenn es notwendig wird, auf Änderungen
// im Kurs anders zu reagieren, muss die
// Codebasis der Klasse geändert werden.
Portfolios.ForEach(p => p.NotifyPriceChange(value));
}
}}
# Beispiel ohne Interaktionskoppelung
Umgehen von Interaktionskoppelung durch Observer-Pattern (siehe Skizze).
SEW_Softwaremetriken 2024-11-20 19.13.15.excalidraw
⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠ You can decompress Drawing data with the command palette: ‘Decompress current Excalidraw file’. For more info check in plugin settings under ‘Saving’
Excalidraw Data
Text Elements
Stockmarket
Observer
Portfolio
Preisänderung
Benachrichtigung
Bank
.Subscribe()
.Unsubscribe()
.Update()
.Update()
.Update()
Link to original
namespace KoppelungClasses;
public interface IObserver
{
void Update(decimal value);
}
public class Portfolio(string name) : IObserver
{
private string Name { get; set; } = name;
public decimal LastNotifiedPrice { get; private set; }
public void Update(decimal value)
{
LastNotifiedPrice = value;
Console.WriteLine($"Portfolio '{Name}' wurde über den neuen Preis {value} informiert.");
}
}
public class Bank(string name) : IObserver
{
private string Name { get; set; } = name;
public void Update(decimal value)
{
Console.WriteLine($"Bank {Name} wurde über Preis {value} benachrichtigt!");
}
}
public class StockMarket
{
private decimal price;
private readonly List<IObserver> observers = [];
public void Subscribe(IObserver observer) => observers.Add(observer);
public void Unsubscribe(IObserver observer) => observers.Remove(observer);
private void NotifyObservers(decimal value)
{
foreach (var observer in observers)
{
observer.Update(value);
}
} public decimal Price
{
set
{
price = value;
NotifyObservers(value);
}
}
}
Ein bisschen umgewandelt (laut sew4-Skriptum):
namespace KoppelungClasses;
public class OhneInteraktionsKoppelungV2
{
public interface IObserver
{
void Update(decimal price);
}
private interface IObservable
{
void AddObserver(IObserver observer);
void RemoveObserver(IObserver observer);
void NotifyObserver();
}
public class Portfolio(string name) : IObserver
{
private string Name { get; set; } = name;
public void Update(decimal price) => Console.WriteLine($"{Name} notified about new price: {price}");
}
public class Bank(string name) : IObserver
{
private string Name { get; set; } = name;
public void Update(decimal price) => Console.WriteLine($"{Name} notified about new price: {price}");
}
public class StockMarket : IObservable
{
private decimal price;
private List<IObserver> observers = [];
public void AddObserver(IObserver observer) => observers.Add(observer);
public void RemoveObserver(IObserver observer) => observers.Remove(observer);
public void NotifyObserver() => observers.ForEach(o => o.Update(price));
public decimal Price
{
get => price;
set
{
price = value;
NotifyObserver();
}
}
}
}
# Vererbungskoppelung
Wenn von einer Basisklasse die geerbten Methoden in den Kindklassen zur Gänze überschrieben werden verliert Vererbung ihren Sinn = Vererbungskoppelung tritt auf. Mithilfe von Objektkomposition kann man diese auflösen.
Beispiel:
- Hier ist Fly() unpassend für die RubberDuck beispielsweise. Es entsteht also eine unnötige Abhängigkeit, weil RubberDuck dieses Verhalten nicht braucht/umsetzen kann.
public class Vererbungskoppelung
{
public class Duck
{
public string Quack() => "quack";
public String Fly() => "fly high in the sky";
}
public class ReadHeadDuck : Duck
{
public string Quack() => "loudly quack";
}
public class RubberDuck : Duck
{
public String Quack() => "squeeze";
public String Fly() => "can't fly";
}
}
Guter Lösungsansatz - Objektkomposition. Einfach erweiterbar & wartungsfreundlich. Der bestehende Code muss bei Erweiterung nicht geändert werden. Verwendung von Objektkomposition/“Strategie-Pattern” - Verhalten von Objekt kann zur Laufzeit verändert werden. Duck Klasse weist “Strategien” (Quack, Fly) per interfaces dynamisch zu.
SEW_Softwaremetriken 2024-11-20 21.04.38.excalidraw
⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠ You can decompress Drawing data with the command palette: ‘Decompress current Excalidraw file’. For more info check in plugin settings under ‘Saving’
Excalidraw Data
Text Elements
IQuackable
IFlyable
Duck1
string Quack()
string Fly()
IQuackable Quackbehaviour
IFlyable Flybehaviour
LoudQuack
string Quack()
ProudQuack
string Quack()
Duck2
IQuackable Quackbehaviour
IFlyable Flybehaviour
DefaultFly
string Fly()
NoFly
string Quack()
DuckFactory
Link to original
namespace KoppelungClasses;
public class OhneVererbungskoppelung
{
public interface IQuackable
{
string Quack();
}
public interface IFlyable
{
string Fly();
}
public class DefaultQuackBehaviour : IQuackable
{
public string Quack() => "quack";
}
public class LoudQuackBehaviour : IQuackable
{
public string Quack() => "loudly: quack";
}
public class ProudQuackBehaviour : IQuackable
{
public string Quack() => "proudly: quack";
}
public class DefaultFlyBehaviour : IFlyable
{
public string Fly() => "flying high in the sky";
}
public class NoFlyBehaviour : IFlyable
{
public string Fly() => "can't fly";
}
public class Duck
{
public IQuackable QuackBehaviour { get; set; }
public IFlyable FlyBehaviour { get; set; }
}
public class DuckFactory
{
public static Duck CreateRedheadDuck() => new Duck
{
QuackBehaviour = new LoudQuackBehaviour(),
FlyBehaviour = new DefaultFlyBehaviour()
};
public Duck CreateRubberDuck() => new Duck
{
QuackBehaviour = new LoudQuackBehaviour(),
FlyBehaviour = new NoFlyBehaviour()
};
}
}
# Kohäsion
Maß des inneren Zusammenhalts eines Softwareelements.
Anstreben von: Hoher Kohäsion
Definition v. Kohäsion: Klasse sollte nur Methoden/Attribute enthalten, die alle zur Lösung einer gemeinsamen Aufgabe/Verantwortungsbereich gehören.
2 unterschiedliche Arten v. Kohäsion:
- ServiceKohäsion
- Beschreibung zu
inneren Zusammenhalt einer Methode
- Methoden einer Klasse - nur EINE Lösung von Aufgabe/Problem
- Beschreibung zu
- Klassenkohäsion
- Beschreibung zu
inneren Zusammenhalt einer Klasse
- Verletzung v. Klassenkohäsion - ungenutzte Attribute/Methoden in Klasse
- Beschreibung zu
# Servicekohäsion
//------------------------------------------
// Servicekohaesion - schwache Kohäsion
//------------------------------------------
class Vector implements Serializable
{
public int X { get; set; }
public int Y { get; set; }
public float Add(Vector v)
{
this.X += v.X;
this.Y += v.Y;
return Math.SQRT(X * X + Y * Y);
}
}