简体   繁体   中英

best practice to avoid static fields

I've already read some questions about the disadvantages of static variables (for example why are static variables considered evil , but i find some difficulties in choosing the best solution to avoid to use them.

In my simple chess application, for example, i have an abstract class, Piece, with many subclasses. Every Piece must have a BufferedImage variable, image , but i want to load every image just one time for each Piece.

This would be very easy by using static variables, something like that :

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

Of course this code doesn't follow OOP, and inheritance is not used for the image variable.

A solution would be to load all the images in an external class, and let all the subclasses instances to keep the reference to the same object:

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

But what if i need to create the subclasses instances from n different classes ? The images would be loaded at least n times, assuming i create just one instance of those n classes.

I've considered to keep the images reference in a Singleton pattern designed class, but i also want to keep the image variable in the Piece class to simply use the getImage() method for every Piece.

Is it a good design to use the second solution combined with the Singleton pattern, so i would pass to every Piece's subclass constructor the reference to the same BufferedImage obtained with a method like SingletonClass.getXxxImage()?

Or is there any better approach? Of course it would be not so evil to load a BufferedImage more than one time, but i would like to have a general solution to avoid useless repetitions of code.

Thanks for your help !

There are a few cases where static is the way to go. But generally, the need for static stuff signals a flaw in the OO design.

In your particular case, I think you're missing an abstraction for the PieceType . If you model each piece type as a class, you're mixing the need for instantiating piecing with the general description of some particular kind of piece.

Namely, your two bishops need to share both data (the image) and behavior (the moving pattern) that are not specific to the white or black bishop, but are common for bishops in general.

While the shared behavior fits quite nicely into a class as a method definition (that will be shared by all instances), the shared data will need to be static or copied redundantly for each instance.

What may be missing here is some PieceType abstraction encapsulating both the data and the behavior of each kind of piece.

This can be modeled as an enum (since you have a fixed number of types) listing each type of piece as an instance:

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

}

Now you only need a single Piece type that will delegate all the type-related stuff to its PieceType:

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

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

    // behavior that delegates to the pieceType whenever necessary
}

Note that the enum defined above inherently creates static variables (its constants). But this is one of the acceptable/encouraged usages for static (as I mentioned in the introduction).

Note that static variables are not bad in and of themselves. They just tend to get overused by new programmers who do not understand OOP. This situation actually seems like a reasonable use of a static field. Every Bishop has the exact same visual, so it seems wasteful to load the same image multiple times.

On the other hand, passing in an image to the constructor is more flexible. It leaves the responsibility of image loading to someone else. It also allows you to change the image used more easily.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM