I've come to the conclusion that CreateDataFile
is doing one thing by making a quick measurement and then storing the data, and doing both in the same method is more intuitive for someone else using this code then having to make a measurement and write to a file as separate method calls.
I think this is your problem, actually. The method is not doing one thing. It's performing two, distinct operations that involve I/O to different devices, both of which it's off-loading to other objects:
- Fetch a measurement
- Save that result to a file somewhere
These are two different I/O operations. Notably, the first one does not mutate the file system in any way.
In fact, we should note that there's an implied middle step:
- Fetch a measurement
- Serialize the measurement into a known format
- Save the serialized measurement to a file
Your API should provide each of these separately in some form. How do you know a caller will not want to take a measurement without storing it anywhere? How do you know they won't want to obtain a measurement from another source? How do you know they won't want to store it somewhere other than the device? There is good reason to decouple the operations. At a bare minimum, each individual piece should be available to any caller. I should not be forced to write the measurement to a file if my use case does not call for it.
As an example, you might separate the operations like this.
IMeasurer
has a way to fetch the measurement:
public interface IMeasurer
{
IMeasurement Measure(int someInput);
}
Your measurement type might just be something simple, like a string
or decimal
. I'm not insisting you need an interface or class for it, but it makes the example here more general.
IFileAccess
has some method for saving files:
interface IFileAccess
{
void SaveFile(string fileContents);
}
Then you need a way of serializing a measurement. Build that into the class or interface representing a measurement, or have a utility method:
interface IMeasurement
{
// As part of the type
string Serialize();
}
// Utility method. Makes more sense if the measurement is not a custom type.
public static string SerializeMeasurement(IMeasurement m)
{
return ...
}
It's not clear whether you have this serialization operation separated out yet.
This sort of separation improves your API. It lets the caller decide what they need and when, rather than forcing your preconceived ideas about what I/O to perform. Callers should have the control to perform any valid operation, whether you think it's useful or not.
Once you have separate implementations for each operation, your CreateDataFile
method becomes just a shorthand for
fileAccess.SaveFile(SerializeMeasurement(measurer.Measure()));
Notably, your method adds very little value once you've done all this. The above line of code is not difficult for your callers to use directly, and your method is purely for convenience at most. It should be and is something optional. And that is the correct way for the API to behave.
Once all the relevant parts are factored out and we've acknowledged that the method is just a convenience, we need to rephrase your question:
What would be the most common use case for your callers?
If the entire point is to make the typical use case of measuring from and writing to the same board slightly more convenient, then it makes perfect sense to just make it available on the Board
class directly:
public class Board : IMeasurer, IFileAccess
{
// Interface methods...
/// <summary>
/// Convenience method to measure and immediate record measurement in
/// default location.
/// </summary>
public void ReadAndSaveMeasurement()
{
this.SaveFile(SerializeMeasurement(this.Measure()));
}
}
If this doesn't improve convenience, then I wouldn't bother with the method at all.
This being a convenience method brings up one other question.
Should the IFileAccess
interface know about the measurement type and how to serialize it? If so, you could add a method to IFileAccess
:
interface IFileAccess
{
void SaveFile(string fileContents);
void SaveMeasurement(IMeasurement m);
}
Now callers just do this:
fileAccess.SaveFile(measurer.Measure());
which is just as short and probably more clear than your convenience method as conceived in the question.