Statische Klassen sind in Ordnung, solange sie an den richtigen Stellen verwendet werden.
Nämlich: Methoden, die 'Blatt'-Methoden sind (sie ändern den Status nicht, sie transformieren lediglich die Eingabe irgendwie). Gute Beispiele hierfür sind Dinge wie Path.Combine. Diese Art von Dingen sind nützlich und sorgen für eine engere Syntax.
Die Probleme, die ich mit der Statik habe, sind zahlreich:
Erstens, wenn Sie statische Klassen haben, werden Abhängigkeiten ausgeblendet. Folgendes berücksichtigen:
public static class ResourceLoader
{
public static void Init(string _rootPath) { ... etc. }
public static void GetResource(string _resourceName) { ... etc. }
public static void Quit() { ... etc. }
}
public static class TextureManager
{
private static Dictionary<string, Texture> m_textures;
public static Init(IEnumerable<GraphicsFormat> _formats)
{
m_textures = new Dictionary<string, Texture>();
foreach(var graphicsFormat in _formats)
{
// do something to create loading classes for all
// supported formats or some other contrived example!
}
}
public static Texture GetTexture(string _path)
{
if(m_textures.ContainsKey(_path))
return m_textures[_path];
// How do we know that ResourceLoader is valid at this point?
var texture = ResourceLoader.LoadResource(_path);
m_textures.Add(_path, texture);
return texture;
}
public static Quit() { ... cleanup code }
}
In TextureManager können Sie anhand eines Konstruktors nicht erkennen, welche Initialisierungsschritte ausgeführt werden müssen. Sie müssen sich mit der Klasse befassen, um ihre Abhängigkeiten zu finden und die Dinge in der richtigen Reihenfolge zu initialisieren. In diesem Fall muss der ResourceLoader vor der Ausführung initialisiert werden. Skalieren Sie nun diesen Abhängigkeitsalptraum und Sie können wahrscheinlich erraten, was passieren wird. Stellen Sie sich vor, Sie versuchen, Code zu verwalten, wenn keine explizite Reihenfolge für die Initialisierung vorliegt. Vergleichen Sie dies mit der Abhängigkeitsinjektion mit Instanzen - in diesem Fall wird der Code nicht einmal kompiliert, wenn die Abhängigkeiten nicht erfüllt sind!
Wenn Sie außerdem Statiken verwenden, die den Status ändern, ist dies wie ein Kartenhaus. Man weiß nie, wer Zugang zu was hat, und das Design ähnelt eher einem Spaghetti-Monster.
Schließlich und ebenso wichtig ist, dass die Verwendung von Statik ein Programm an eine bestimmte Implementierung bindet. Statischer Code ist das Gegenteil von Design für Testbarkeit. Das Testen von Code, der mit Statik durchsetzt ist, ist ein Albtraum. Ein statischer Aufruf kann niemals gegen ein Test-Double ausgetauscht werden (es sei denn, Sie verwenden Test-Frameworks, die speziell zum Verspotten statischer Typen entwickelt wurden). Ein statisches System bewirkt daher, dass alles, was ihn verwendet, ein sofortiger Integrationstest ist.
Kurz gesagt, Statik ist für einige Dinge in Ordnung und für kleine Werkzeuge oder Wegwerfcode würde ich ihre Verwendung nicht entmutigen. Darüber hinaus sind sie jedoch ein blutiger Albtraum für Wartbarkeit, gutes Design und einfache Tests.
Hier ist ein guter Artikel zu den Problemen: http://gamearchitect.net/2008/09/13/an-anatomy-of-despair-managers-and-contexts/