【问题标题】:best practice to avoid static fields避免静态字段的最佳实践
【发布时间】:2016-08-22 22:59:09
【问题描述】:

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

例如,在我的简单国际象棋应用程序中,我有一个抽象类 Piece,它有许多子类。 每个 Piece 都必须有一个 BufferedImage 变量,image,但我只想为每个 Piece 加载每个图像一次

使用静态变量会很容易,比如:

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 次。

我考虑将图像引用保留在单例模式设计的类中,但我也想保留 Piece 类中的 image 变量,以便对每个 Piece 简单地使用 getImage() 方法.

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

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

感谢您的帮助!

【问题讨论】:

标签: java inheritance static singleton


【解决方案1】:

在少数情况下,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 可接受/鼓励的用法之一(正如我在介绍中提到的)。

【讨论】:

  • 这是一个很好的解释。请注意,OP 的帖子没有提到他计划如何验证或执行移动。我没有深入了解这些细节,因为缺少这些信息。我可以想到至少另一种方法,将显示部分与游戏逻辑分开。
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 2019-04-03
    • 1970-01-01
    • 2012-02-19
    • 1970-01-01
    • 1970-01-01
    • 2017-05-24
    • 2016-11-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多