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);
}
}
2 Answers 2
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).
-
\$\begingroup\$ I think I agree. Something somewhere must know about the different strategies. Depends on if this something could be altered accordingly \$\endgroup\$dreza– dreza2012年11月12日 22:18:54 +00:00Commented Nov 12, 2012 at 22:18
Inject the file information into the constructor. You are going to end up with something (a factory, an IoC container, etc) that knows which strategy to use; it can also know the file information.
I like to wrap file access in a mockable class too, but that might be a problem that is specific to C#.
FileLocator
/FileNamer
/WriterFactory
or similar strategy. But do you really need that kind of flexibility? You're going to have to stop and test the writes at some point, and I would have expected it to be at theFilePersistenceStrategy
level (that is, you're mockingPersistenceStrategy
objects for testing the rest of your code, right?). \$\endgroup\$