2016-08-22 5 views
0

ich zu vermeiden habe schon einige Fragen über die Nachteile der statischen Variablen lesen (zum Beispiel why are static variables considered evil, aber ich finde einige Schwierigkeiten bei der die beste Lösung der Wahl zu vermeiden, sie zu benutzen.best practice statische Felder

In meinem einfachen Schach-Anwendung, zum Beispiel, ich habe eine abstrakte Klasse, Stück, mit vielen Unterklassen. jedes Stück eine BufferedImage Variable haben muss, Bild, aber i jedes Bild nur einmal für jedes Stück laden mag.

Dies wäre sehr einfach mit statischen Variablen, etwa wie t Hut:

abstract class Piece 
{ 
    // ... 
    public abstract BufferedImage getImage(); 
} 
class Bishop extends Piece 
{ 
    private static final BufferedImage image = null; 

    static{ 
     try{ 
      image = ImageIO.read(ClassLoader.getSystemResource("chess/img/pieces/Bishop.png")); 
     } 
     catch(IOException ex){ 
      // ... 
     } 
    } 

    public BufferedImage getImage(){ 
     return image; 
    } 
} 

Natürlich ist dieser Code nicht folgt OOP und Vererbung für die Bild Variable nicht verwendet.

Eine Lösung wäre, alle Bilder in einer externen Klasse zu laden, und alle Unterklassen Instanzen lassen den Verweis auf das gleiche Objekt zu halten:

abstract class Piece 
{ 
    final BufferedImage image; 
    public Piece(/* ... */ BufferedImage image){ 
     this.image = image; 
    } 
    public BufferedImage getImage(){ 
     return image; 
    } 
} 
class Bishop extends Piece 
{ 
    public Bishop(/* ... */ BufferedImage image){ 
     super(/* ... */ image); 
     // ... 
    } 
} 

Aber was ist, wenn ich die Unterklassen-Instanzen erstellen müssen von n verschiedene Klassen? Die Bilder würden mindestens n mal geladen werden, vorausgesetzt, ich erstelle nur eine Instanz dieser n Klassen.

Ich habe erwogen, die Bilder Referenz in einem Singleton-Muster entworfen Klasse zu halten, aber ich möchte auch die Bild Variable in der Klasse Stück, um einfach die Methode getImage() für jedes Stück zu verwenden.

Ist es ein guter Entwurf, um die zweite Lösung kombiniert mit dem Singleton-Muster zu verwenden, so würde ich den Konstruktor jedes Stücks die Referenz auf die gleiche BufferedImage mit einer Methode wie SingletonClass.getXxxImage() übergeben?

Oder gibt es einen besseren Ansatz? Natürlich wäre es nicht so übel, ein BufferedImage mehr als einmal zu laden, aber ich hätte gerne eine allgemeine Lösung, um nutzlose Code-Wiederholungen zu vermeiden.

Danke für Ihre Hilfe!

+0

können folgen Solid-Prinzipal https://en.m.wikipedia.org/wiki/SOLID_(objectively_designed) –

+1

Die Übergabe des Bildes an den Stück Konstruktor ist definitiv der Beginn der richtigen Ansatz. –

Antwort

3

Es gibt ein paar Fälle, in denen static der Weg zu gehen ist. Aber im Allgemeinen signalisiert der Bedarf an statischem Material einen Fehler im OO-Design.

In Ihrem speziellen Fall, ich denke, Sie vermissen eine Abstraktion für die PieceType. Wenn Sie jeden Stücktyp als eine Klasse modellieren, mischen Sie die Notwendigkeit, das Anspinnen mit der allgemeinen Beschreibung einer bestimmten Art von Stück zu instanziieren.

Ihre beiden Bischöfe müssen also sowohl die Daten (das Bild) als auch das Verhalten (das Bewegungsmuster) teilen, die nicht spezifisch für den weißen oder schwarzen Bischof sind, aber für die Bischöfe allgemein üblich sind.

Während das geteilte Verhalten ziemlich gut in eine Klasse als Methodendefinition passt (die von allen Instanzen gemeinsam genutzt wird), müssen die freigegebenen Daten static oder für jede Instanz redundant kopiert werden.

Was hier fehlen könnte, ist eine PieceType Abstraktion, die sowohl die Daten als auch das Verhalten jeder Art von Stück einkapselt.

Dies kann als Enum modelliert werden (da Sie eine feste Anzahl von Typen haben), um jede Art von Stück als Instanz Listing:

enum PieceType { 
    BISHOP { 
     @Override public void move() { /* whatever */ } 
    }, 
    PEON { 
     ... 
    } 

    private final BufferedImage image; 

    PieceType() { 
     image = ImageIO.read(ClassLoader.getSystemResource("chess/img/" + name().toLowerCase() + ".png")); 
    } 

    public abstract void move(); // or whatever 

} 

jetzt nur Sie eine einzelne Piece Art benötigen, die alle delegieren Typ-related stuff seiner PieceType:

class Piece { 
    private final PieceType pieceType; 
    // position and all... 

    Piece(PieceType pieceType) { 
     this.pieceType = pieceType; 
    } 

    // behavior that delegates to the pieceType whenever necessary 
} 

Beachten Sie, dass die enum oben definiert sind von Natur aus static Variablen (seine Konstanten) erzeugt. Aber das ist eine der akzeptablen/empfohlenen Nutzungen für static (wie ich in der Einleitung erwähnt habe).

+0

Dies ist eine großartige Erklärung. Beachten Sie, dass im Beitrag des OP nicht erwähnt wird, wie er Züge validieren oder ausführen möchte. Ich bin nicht auf diese Details eingegangen, weil diese Informationen fehlen. Ich kann mir mindestens einen anderen Ansatz vorstellen, bei dem die Stücke von der Spiellogik getrennt angezeigt werden. –

+0

Große Antwort, danke! – Ansharja

1

Beachten Sie, dass static Variablen an sich nicht schlecht sind. Sie neigen einfach dazu, von neuen Programmierern, die OOP nicht verstehen, zu sehr zu nutzen. Diese Situation scheint tatsächlich eine vernünftige Verwendung eines static Feldes zu sein. Jede Bishop hat genau das gleiche Bild, daher erscheint es verschwenderisch, das gleiche Bild mehrmals zu laden.

Auf der anderen Seite ist die Übergabe eines Bildes an den Konstruktor flexibler. Es überlässt die Verantwortung für das Laden von Bildern jemand anderem. Außerdem können Sie das verwendete Bild einfacher ändern.