Search code examples
c#solid-principles

My first SOLID re-factoring


(Cross-post programmers.stackexchange) I am trying to make use of SOLID principles for the first time. I am re-factoring a File class that stores file information and makes file operations available. This class is then inherited to specific file types to make available methods for that type.

I have started with SRP and have tried to make a FileWriter and FileReader abstract class to read and write from different resources. Some include:

  • File System
  • Streams
  • Database
  • Other undefined services (SOAP, REST??).

I am happy to say that the file data is handled by a byte array, but I am not sure how to handle the file path/uid for a resource.

Here is what I have...

public abstract class EFileReader
{
    event EventHandler<IEFileEventArgs> ReadThreadedComplete;
    public abstract byte[] Read(object source);
    public abstract async Task<byte[]> ReadAsync(object source);
    public abstract void ReadThreaded(object source);
    protected virtual void OnFileRead(IEFileEventArgs e)
    {
        EventHandler<IEFileEventArgs> handler = this.ReadThreadedComplete;
        if (handler != null)
        {
            handler(this, e);
        }
    }
}

I could cast the object as the required type in implementation. Alternatively it could be a generic type somehow specified in implementation.

public abstract class EFileReader<T>
{
    event EventHandler<IEFileEventArgs> ReadThreadedComplete;
    public abstract byte[] Read(T source);
    public abstract async Task<byte[]> ReadAsync(T source);
    public abstract void ReadThreaded(T source);
    protected virtual void OnFileRead(IEFileEventArgs e)
    {
        EventHandler<IEFileEventArgs> handler = this.ReadThreadedComplete;
        if (handler != null)
        {
            handler(this, e);
        }
    }
}

I could define or constrain T in the implementation.

...Or maybe there is some abstraction of the source I can write to accommodate the various possible use cases. I guess I could make it take an abstract FileResource class that exposes a stream.

What is the best way to approach this?


Solution

  • I think you're on the right track with creating a new type, but I'd call it FileIdentifier. It would not necessarily "expose a stream" itself, instead it would be used with a FileStorage class, like

    abstract class FileStorage
    {
        public abstract Stream GetStream(FileIdentifier id);
    }
    

    The FileReader class might not need to be abstract anymore - instead its constructor would require an instance of a FileStorage-derived class, which would own the details of getting a stream from a file:

    class FileReader
    {
        FileReader(DiskFileStorage storage)..
    
        override byte[] Read(FileIdentifier id)
        {
            Stream stream = this.storage.GetStream(id);
            return stream.Read....
        }
    }
    

    You could have DiskFileStorage, NetworkFileStorage, DatabaseFileStorage, etc, with corresponding FileIdentifer-derived classes. DiskFileIdentifier would just wrap a file path string, NetworkFileIdentifier might wrap a URL, DatabaseFileIdentifier might wrap a username, password, table and primary key, and so on.