Core Statische Klassen Statische Klassen
Mehr Freund als Feind!
Im September-Heft befasste sich David Tielke mit der Frage, ob statische Klassen eher
Freund oder Feind des Entwicklers sind. Wie Stefan Lieser das sieht, lesen Sie hier.
V
öllig zutreffend erläutert Autor David Tielke in
seinem Artikel [1] die technischen Unterschiede
zwischen Instanzmethoden und statischen Methoden.
Er weist ferner auf die Risiken statischer Klassen hin,
die im Zusammenhang mit der Garbage Collection
entstehen können. Im weiteren Verlauf des Artikels
zieht er den Schluss, dass „normale Klassen“ eher ge
eignet seien, ein „qualitativ hochwertiges Software
system“ zu bauen. Er weist auf die SOLID-Prinzipien
hin und lehnt, wie mir scheint, die statischen Klassen
vor allem mit dem Argument ab, dass sich damit kei
ne wahre Objektorientierung realisieren lasse.
Da stellte sich mir die Frage, wie meine eigene Po
sition zu statischen Klassen denn eigentlich ist. Da
von will ich im Folgenden berichten – ich möchte ei
ne Lanze für die statische Klasse brechen und ihre
Nützlichkeit herausstellen.
Am Anfang stand für mich die Frage, wozu wir über
haupt Methoden und Klassen brauchen. Softwaresys
teme bestehen in erster Linie aus Logik, also Berech
nungen, Schleifen, bedingter Ausführung und so
weiter. Theoretisch können wir die gesamte Logik ei
ner Anwendung in eine einzige oder wenigstens in
sehr wenige Methoden packen. Und auch Klassen be
nötigen wir nicht, um die Funktionalität herzustellen.
Dann mag das System nicht objektorientiert aufgebaut
sein – na und? Was ist der Zweck der Objektorientierung? Sie
hat ja keinen Wert an sich. Worin besteht also der Wert für
den Kunden, wenn ein Softwaresystem in guter Weise in Me
thoden und Klassen aufgeteilt ist? Der Überbegriff für Metho
de, Klasse, Bibliothek (Assembly), Komponente (plattformab
hängiger Kontrakt) und Service (plattformneutraler Kontrakt)
lautet für uns [2]: Modul.
Module sind Orte, an denen Logik ihren Platz finden kann.
Der Nutzen von Modulen besteht darin, Wandelbarkeit her
zustellen. Wandelbarkeit steht für den Kunden für Investiti
onsschutz. Er will auch noch in vielen Jahren an der Software
Änderungen und Fehlerkorrekturen vornehmen lassen, ohne
dass dabei die Kosten dramatisch ansteigen. Dazu bedarf es
einer Struktur innerhalb des Softwaresystems, die ein Ver
ständnis der Zusammenhänge ermöglicht. Wir verwenden
also Methoden und Klassen sowie die anderen Module, um
eine Struktur herzustellen, die Softwareentwickler nachvoll
ziehen können. Daraus leiten sich Prinzipien ab, die wir für
www.dotnetpro.de 11.2015
Bild: shutterstock@Macrovector
Wozu Methoden?
Module in Anschlag bringen: So soll ein Modul beispielswei
se für nur einen Aspekt (oder Responsibility oder Concern)
zuständig sein. Dem stimmt David Tielke zu, wenn er vom
SRP, dem Single Responsibility Principle, schreibt.
Wozu Objektorientierung?
Wir brauchen also Module, um wandelbare Softwaresysteme
herstellen zu können. Brauchen wir die Objektorientierung,
um Wandelbarkeit herzustellen? Ich meine nein. Sie ist ein
Paradigma, das in den letzten Jahrzehnten sehr in Mode ge
kommen ist. Doch es gibt andere, wie etwa die funktionale
Programmierung oder Data Flows. Nur eines davon als das
„richtige“ hinzustellen wäre dogmatisch. Im Sinne der Wan
delbarkeit geht es darum, in pragmatischer Weise geeignete
Paradigmen und Module auszuwählen, als Orte für Logik.
Und nun bin ich beim Punkt der statischen Methoden und
statischen Klassen: Im Detail mögen damit Herausforderun
gen zum Beispiel bei der Speicherverwaltung verbunden
sein. Aber Ähnliches gilt für viele Werkzeuge: Man kann ▶
75
Core Statische Klassen
mit ihnen einen gewissen Nutzen erzie
Das eigentliche Problem ist hier nicht
len, muss aber im Umgang mit ihnen ge
die Abhängigkeit von A zu einer stati
übt sein, um keinen Schaden anzurich
schen Klasse S, sondern die Abhängig
ten. An einer Schere kann ich mich
keit an sich. Im Kern geht es bei Davids
schneiden. Vermeide ich es deshalb, ei
Beispiel um die Frage, was die Zustän
ne Schere zu benutzen? Natürlich nicht.
digkeit von A und S ist. Beide enthalten
Die ALT.NET Community hat daher vor Das Logo der ALT.NET Community,
Logik (wenn auch, der Kürze des Bei
einiger Zeit eine Schere in ihr Logo inte Quelle: [3] (Bild 1)
spiels geschuldet, keine komplizierte).
griert (Bild 1).
S produziert einen Wert. In einem
Insofern greifen die SOLID-Prinzipien
realen Projekt käme der vielleicht aus
als Einwand gegen statische Klassen für mich nicht. Denn am
einer Ressource oder würde berechnet. A nimmt diesen Wert
Ende steht dabei nur das Resultat, dass statische Klassen kei
und transformiert ihn. Weil beide Klassen Logik enthalten,
ne Objektorientierung zulassen. Das mag sein, ist für mich
sind sie Operationen. A ist gleichzeitig aber auch für die In
aber kein Argument, sie zum Feind zu erklären.
tegration zuständig.
Genau darin liegt das Kernproblem des Beispiels: Die Klas
Wenn statische Methoden oder Klassen die Wandelbarkeit
se A verletzt das Integration Operation Segregation Principle
negativ beeinflussen würden, wäre das für mich ein Argu
(IOSP). Trennt man nämlich Integration und Operation, sind
ment gegen ihre Verwendung. Doch sie sind wunderbar ge
die Operationen, also die Teile, welche Logik enthalten, sehr
eignet für die Paradigmen Funktional oder Data Flow, und
gut automatisiert zu testen. Hier das umformulierte Beispiel:
mit beiden kann Wandelbarkeit hergestellt werden. Und
auch in einem objektorientiert angelegten Softwaresystem
class Integration
kann Logik in statischen Methoden abgelegt werden, sofern
{
sie nicht auf Zustand zugreifen muss.
public string DoSandA() {
Das wichtigste Kriterium scheint mir hier zu sein, ob Logik
var A = new A();
auf Zustand angewiesen ist oder nicht. Benötigt sie Zustand,
bieten sich Instanzmethoden an, weil der Zustand dann als
var fromS = S.DoS();
Feld der Klasse realisiert werden kann. Wird kein Zustand
var result = A.DoA(fromS);
benötigt, spricht für mich nichts gegen statische Klassen. Da
zwischen liegen dann Fälle, in denen statischer Zustand be
return result;
nötigt wird. Das Singleton Pattern hat ja durchaus Anwen
}
dungsfälle. Muss also der Zustand über die gesamte Laufzeit
}
der Anwendung erhalten bleiben, kann dies mit einer stati
schen Klasse oder einer Variante des Singletons implemen
class A
tiert werden. Der Einsatz eines IoC-Containers schafft hier
{
ohnehin so viel Flexibilität, dass ein Singleton praktisch nicht
public string DoA(string fromS) {
selbst implementiert werden muss, sondern eine Frage der
return fromS.ToUpper();
IoC-Container-Konfiguration ist.
Automatisiertes Testen
Beim Thema Testen kommt David zu dem, wie ich meine, vor
eiligen Schluss, dass mit statischen Klassen die Testbarkeit
nicht gegeben sei. Er führt als Beispiel eine Klasse A an, die
von einer statischen Klasse beziehungsweise Methode ab
hängig ist. Sein Beispiel lautet:
}
}
static class S
{
public static string DoS() {
return "David";
}
class A
}
{
public string DoA() {
return S.DoS().ToUpper();
}
}
static class S
{
public static string DoS() {
return "David";
}
}
76
Die statische Klasse S ist unverändert. Sie war schon zuvor
gut testbar. In Klasse A habe ich die Methode DoS jedoch so
verändert, dass sie nun nicht mehr für Integration und Ope
ration zuständig ist. Ich habe den Integrationsanteil heraus
gezogen, in dem die Methode per Parameter den Wert erhält,
auf dem sie ihren Anteil Logik (ToUpper) ausführt. Die Inte
gration ist in eine eigene Klasse gewandert. Die Klasse Integration ist nun ausschließlich für das gewünschte Zusammen
spiel der beiden Operationen zuständig.
In der Methode DoSandA wird erst eine Instanz von A er
zeugt. Anschließend werden die beiden Operationen nachei
11.2015 www.dotnetpro.de
Core Statische Klassen nander aufgerufen, um das Resultat herzustellen. A und S
sind nun leicht automatisiert zu testen, da sie beide keinerlei
(!) Abhängigkeiten haben.
DoSandA ist im Sinne eines Unit-Tests schwer zu testen.
Das liegt daran, dass A und S für einen isolierten Unit-Test
durch eine Attrappe ersetzt werden müssen. Für A ginge das
per Dependency Injection mittels Interface. Für S ginge es
mittels Dependency Injection einer Func<string>:
class Integration
{
private readonly IA a;
private readonly Func<string> doS;
Allerdings bleibt auf diese Weise das Integration Operation
Segregation Principle (IOSP) verletzt, weil die Methode DoA
nach wie vor S.DoS integriert und selbst Logik enthält.
Noch einmal SOLID: ISP
Das „I“ in SOLID steht für das Interface Segregation Princi
ple (ISP). Es besagt, dass Interfaces so abzutrennen sind, dass
Konsumenten sich nur an den tatsächlich von ihnen benötig
ten kleinsten Teil eines Interface binden.
Betrachten wir noch einmal den injizierten Funktionszeiger
im oben gezeigten Listing, dann wird klar, dass dies die ma
ximale Form des ISP darstellt. Hier wird überhaupt kein In
terface benötigt, weil sich der Konsument lediglich an eine
Signatur
public Integration() : this(new A(), S.DoS) {
}
void -> string
internal Integration(IA A, Func<string> DoS) {
bindet. Typische objektorientiert aufgebaute Systeme sind
voll von Interfaces, und es stellt sich die Frage, ob diese tat
sächlich alle benötigt werden oder ob nicht bei extremerer
Auslegung des ISP viel häufiger Func<> und Action<> zum
Einsatz kommen sollten.
Natürlich soll auch das nicht zum Dogma werden, Inter
faces haben ihre Berechtigung. Es geht um einen angemes
senen Umgang. So ergibt es durchaus Sinn, Methoden und
Funktionen hoher Kohärenz in einem Interface zusammenzu
fassen. Gleichzeitig sind Interfaces kein Selbstzweck oder
Allheilmittel.
Ohnehin steht für mich die Vermeidung von Abhängigkei
ten wie oben gezeigt im Vordergrund. Wenn es keine Abhän
gigkeiten zwischen Operationen gibt, also zwischen Funkti
onseinheiten, die Logik enthalten, braucht nichts injiziert
werden. In der Folge ist der Einsatz von Interfaces auf selte
ne Fälle begrenzt.
a = A;
doS = DoS;
}
public string DoSandA() {
var fromS = doS();
var result = a.DoA(fromS);
return result;
}
}
Nun kann die Klasse Integration isoliert getestet werden. Am
Ende des Tages benötigen wir zusätzlich zu Unit-Tests auch
Integrationstests, also solche, bei denen die Bestandteile des
Softwaresystems voll integriert im Zusammenspiel getestet
werden. Ich würde daher auf Unit-Tests an den Stellen ver
zichten, an denen reine Integration stattfindet.
Wie auch immer Sie sich hier entscheiden, testbar ist der ge
samte Code nun, obschon die unveränderte statische Klasse S
zum Einsatz kommt. Und natürlich kann auch die ursprüngli
che Klasse A testbar gemacht werden, ohne den statischen
Methodenaufruf durch eine Instanzmethode zu ersetzen:
class A
{
private readonly Func<string> doS;
Fazit
Statische Klassen können bei ungeeigneter Verwendung
Schwierigkeiten bei der Speicherverwaltung verursachen.
Sie haben dennoch ihren Platz unter den Modulen. Am Ende
geht es um eine differenzierte Betrachtung. ◾
[1] David Tielke, Freund oder Feind?,
dotnetpro 9/2015, Seite 54,
www.dotnetpro.de/A1509DDD
[2] http://flow-design.org
[3] http://ayende.com/blog/3187/alt-net-logo
public A() : this(S.DoS) {
}
internal A(Func<string> doS) {
this.doS = doS;
}
public string DoA() {
return doS().ToUpper();
}
}
www.dotnetpro.de 11.2015
Stefan Lieser
sucht ständig nach Verbesserung und neuen
Wegen, um die innere Qualität von Software zu
optimieren. Gemeinsam mit Ralf Westphal hat
er die Clean Code Developer Initiative (http://
clean-code-developer.de) ins Leben gerufen.
http://lieser-online.de
dnpCodeA1511StatischeKlassen
77
© Copyright 2026 ExpyDoc