views:

149

answers:

7

I think I'm going mad, someone please reassure me.

public class MyFile
{   
    public static byte[] ReadBinaryFile(string fileName)
    {
        return File.ReadAllBytes(fileName);
    }

    public static void WriteBinaryFile(string fileName, byte[] fileContents)
    {
        File.WriteAllBytes(fileName, fileContents);
    }
}

People keep on adding code like the above in to our code base, surely this is wrong and horrid and I am doing the world a favour by deleting it and replacing all (or both in this case...) references to it with the internal code.

Is there any real justification for this kind of thing? Could I be missing the bigger picture? We are quite YAGNI-centric in our team and this seems to fly in the face of that. I could understand if this was the beginnings of something more, however this code has lay dormant for many many months until I tripped over it today. The more I search the more I find.

+2  A: 

No. It's silly. Please make it stop.

David Morton
+4  A: 

The existence of those methods is a disgusting aberration that should be corrected immediately.

They were probably written by someone who didn't know about the File class, then rewritten by someone who did, but wasn't as daring as you.

SLaks
If I could you'd get two votes, one for "disgusting aberration" and one for "daring" :)
runrunraygun
Why was this downvoted?
SLaks
A: 

I guess the answer to this does depend on your team, practices and what the codes ultiamte purpose is for (eg. The piece of code you've found currently writes to a file but will be writing to a web service/database/morse-code machine once it's finished - although that "excuse" is kinda defeated by the class/method names). I think you've answered the question yourself with "We are quite YAGNI-centric in our team and this seems to fly in the face of that".

The ultimate answer though would be to ask the person who wrote it why they wrote it that way.

Rob
+9  A: 

As written, the class/methods are garbage. However, I can see a situation in which a similar pattern might be used legitimately:

public interface IFileStorage
{
    byte[] ReadBinaryFile(string fileName);
    void WriteBinaryFile(string fileName, byte[] fileContents);
}

public class LocalFileStorage : IFileStorage { ... }

public class IsolatedFileStorage : IFileStorage { ... }

public class DatabaseFileStorage : IFileStorage { ... }

In other words, if you wanted to support different kinds of storage, then you might actually wrap very simple methods in order to implement a generic abstraction.

As written, though, the class doesn't implement any interface, and the methods are static, so it's pretty much useless. If you're trying to support the above pattern, then refactor; otherwise, get rid of it.

Aaronaught
this is a much better way of going about it :D
Jimmy
Interesting idea, cheers.
runrunraygun
+6  A: 

It is rather silly, until you think about hiding the implementation details of those methods.

Take for example if you had code like this

File.WriteAllBytes(fileName, fileContents);

scattered throughout your code, what if some day down the line you wanted to change your application's method of writing a file out? Well in that case you would have to go throughout your code and update all of these lines of code to adopt the new method, where as with the above version you would only need to change it in one place.

I am not saying their way is right and you are wrong to correct it, I am just adding some perspective

Jimmy
Fear of unlikely changes pollutes codebases - it makes them harder to understand and maintain.
Jeff Sternal
Good point. Having a centralised point for writing to disk could reap rewards if we need to make a change across the code base. Although I don't think that was the motivation here ;)
runrunraygun
I understand, and agree, merely adding what I figure was in the minds of those developers
Jimmy
@Jeff: I agree that fear of unlikely change should not be the _only_ motivator for something like this, but that's not the only reason to hide the implementation details behind a method. `GetCustomerDataFileAsByteArray()` provides more context than `File.ReadAllBytes()` and would, IMHO, be preferable.
Seth Petry-Johnson
@Seth: there's definitely a place for contextual, domain-oriented abstractions, but I think it's at a higher level, like a `GetCustomer(string sourceFilePath)` method. Then *that* method would deal directly with the excellent, well-tested .NET APIs instead of dealing with a facade that doesn't add any behavior and could be the site of future bugs.
Jeff Sternal
+2  A: 

If all the methods do is take the same parameter list, and pass them through, then no, it is pointless, and I think actually makes the code less understandable. However, if the method also passes on default values besides the ones passed as parameters, or does some sort of common validation on the parameters, then it's a harder argument to make.

Are these methods placeholders for later logic to be added? If so, then comments or even the dreaded TODO statement should be added so that someone wraps around later and completes the thought.

Nick
+1 for default values if needed.
Chris Haas
+2  A: 

I don't think these methods make much sense as static methods of a helper class, but the pattern has merit in some cases. For instance:

  1. To decouple code from a static class. If MyFile was not static then it might serve as an abstraction over the static IO.File object. Code that directly accesses the filesystem can be very difficult to test, so abstractions like this can be useful. (For instance, I have an IFileSystem interface that all my I/O code depends on, and my FileSystem class implements this interface and wraps the basic methods of File.)

  2. To preserve consistent levels of abstraction in a method. I don't like to mix code in the problem domain (e.g. "customers" and "orders") with code in the _solution domain) (files and bytes and bits). I'll often write wrappers like this to make the code easier to read and to give me extensibility points. I'd rather read GetDataFileContents() than File.ReadAllBytes("foo.dat").

  3. To provide context. If a piece of code is being executed for its side effects, such as deleting a text file to effect the deletion of a customer's order, then I'd prefer to read DeleteCustomerOrderFile("foo.txt") than File.Delete("foo.txt"). The former provides contextual information to the code, the latter does not.

Seth Petry-Johnson
Point 1 is what I actually want to do with this class in the long term to aid testing/mocking etc however today's priority it is not.
runrunraygun