How to use inversion of control within a strategy when using the strategy pattern I am creating a skeleton application to demonstrate use of the Strategy design pattern and inversion of control inside a Strategy.

The application is the backend of a simple board game, which also has the capability to save and load the board. Since we can have different types of data stores for saving, I have used an interface PersistenceStrategy and concrete implementations of it like the FilePersistenceStrategy.

I have listed the code below. However, I am stuck in one place. Inside the FilePersistenceStrategy class, the save() as well as load() methods need to communicate with a file. I do not want these methods to create a file handle (Reader/Writer) themselves, since this will be bad for testability.

I know there are several ways for these methods to get hold of FileReader and FileWriter objects - like getting them from a factory, or getting the FQN from a properties file and instantiating them using reflection. 

The most common method of solving this problem - Dependency Injection, cannot be used here because the load() and save() methods come from an interface and we cannot add Strategy specific parameters to the interface.

How would you solve this problem, and what in your opinion are the pros and cons of different approaches ?

Code follows:

Board.java

package com.diycomputerscience.example.sanddi;

public class Board {

    private PersistenceStrategy persistenceStrategy;
    private BoardState state;

    public Board(PersistenceStrategy persistenceStrategy) {
        this.persistenceStrategy = persistenceStrategy;
    }

    //various methods of playing the board game

    public void load() throws PersistenceException {
        this.state = this.persistenceStrategy.load();
    }

    public void save() throws PersistenceException {
        this.persistenceStrategy.save(this.state);
    }
}


BoardState.java

package com.diycomputerscience.example.sanddi;

public class BoardState {

}


PersistenceStrategy

package com.diycomputerscience.example.sanddi;

public interface PersistenceStrategy {
    public void save(BoardState state) throws PersistenceException;
    public BoardState load()  throws PersistenceException;
}


FilePersistenceStrategy.java

package com.diycomputerscience.example.sanddi;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class FilePersistenceStrategy implements PersistenceStrategy {

    @Override
    public void save(BoardState state) throws PersistenceException {
        FileWriter writer = getFileWriter();
        //logic to save the state 
        if(writer != null) {
            try {
                writer.close();
            } catch(IOException ioe) {
                String msg = "Could not close FileWriter";
                throw new PersistenceException(msg, ioe);
            }
        }
    }

    @Override
    public BoardState load() throws PersistenceException {
        FileReader reader = getFileReader();
        BoardState state = parseFileForBoardState(reader);
        return state;
    }

    private BoardState parseFileForBoardState(FileReader reader) {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * HOW DO I USE INVERSION OF CONTROL HERE FOR BETTER TESTABILITY ?
     * @return
     */
    private FileReader getFileReader() {
        // Create a FileReader from a well known file name... 
        // perhaps a hardcoded name or read name from a config file
        return null;
    }

    /**
     * HOW DO I USE INVERSION OF CONTROL HERE FOR BETTER TESTABILITY ?
     * @return
     */
    private FileWriter getFileWriter() {
        // Create a FileWriter from a well known file name... 
        // perhaps a hardcoded name or read name from a config file
        return null;
    }
}


PersistenceException.java

package com.diycomputerscience.example.sanddi;

public class PersistenceException extends Exception {
    //Override all constructors from the superclass

    public PersistenceException(String msg, Throwable cause) {
        super(msg, cause);
    }
}
For me the question is how the strategy is created. Is it possible to inject the file name during construction of the strategy? That would be the way I would do it.

Else you can create callbacks that are created and injected. When you need the file call this callback and let this decide (eg asking the user).