避免静态字段的最佳做法

时间:2016-08-22 22:59:09

标签: java inheritance static singleton

我已经读过一些关于静态变量缺点的问题(例如why are static variables considered evil),但我发现在选择避免使用它们的最佳解决方案时遇到了一些困难。

在我的简单国际象棋应用程序中,例如,我有一个抽象类,Piece,有许多子类。 每件必须有一个BufferedImage变量,图像,但是我想为每件作品加载每张图片

使用静态变量非常容易:

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;
    }
}

当然,此代码不遵循OOP,并且继承不用于 image 变量。

解决方案是将所有图像加载到外部类中,并让所有子类实例保持对同一对象的引用:

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);
        // ...
    }
}

但是如果我需要从 n 不同的类创建子类实例呢? 假设我只创建那些 n 类的一个实例,那么图像将至少加载 n 次。

我考虑过将图像引用保留在Singleton模式设计类中,但我还想在Piece类中保留 image 变量,只需使用getImage()方法每一件。

使用与Singleton模式结合的第二个解决方案是一个好的设计,所以我会向每个Piece的子类构造函数传递对使用SingletonClass.getXxxImage()等方法获得的相同BufferedImage的引用吗? / p>

还是有更好的方法吗?当然,多次加载BufferedImage并不是那么邪恶,但我希望有一个通用的解决方案来避免无用的重复代码。

感谢您的帮助!

2 个答案:

答案 0 :(得分:3)

在某些情况下static是可行的方法。但一般来说,对静态材料的需求标志着OO设计的一个缺陷。

在您的特定情况下,我认为您错过了PieceType的抽象。如果您将每个片段类型建模为一个类,则需要将实例化拼接与某些特定片段的一般描述混合在一起。

即,你的两位主教需要分享不是白人或黑人主教特有的数据(图像)和行为(移动模式),但对于一般的主教来说是常见的。

虽然共享行为非常适合作为方法定义(将由所有实例共享)放入类中,但共享数据需要为static或为每个实例冗余复制。

这里可能缺少的是一些PieceType抽象包含数据和每种作品的行为。

这可以建模为枚举(因为您有固定数量的类型)将每种类型的作品列为实例:

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

}

现在您只需要一个Piece类型,它将所有与类型相关的内容委托给其PieceType:

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

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

    // behavior that delegates to the pieceType whenever necessary
}

请注意,上面定义的enum固有地创建了static个变量(它的常量)。但这是static可接受/鼓励的用法之一(正如我在引言中提到的那样)。

答案 1 :(得分:1)

请注意,static变量本身并不坏。他们只是倾向于被不懂OOP的新程序员过度使用。这种情况实际上似乎是合理使用static字段。每个Bishop都具有完全相同的视觉效果,因此多次加载相同的图像似乎很浪费。

另一方面,将图像传递给构造函数更灵活。它将图像加载的责任留给了其他人。它还允许您更轻松地更改使用的图像。