Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
449 views
in Technique[技术] by (71.8m points)

c# - Breaking SOLID Principles in multiple implementation of an Interface

I am facing a problem with dependency inversion in a factory method and it is also breaking Open Closed principle. My code looks like below codes

    public interface IWriter
    {
        void WriteToStorage(string data);
    }

    public class FileWriter : IWriter
    {
        public void WriteToStorage(string data)
        {
            //write to file
        }
    }

    public class DBWriter : IWriter
    {
        public void WriteToStorage(string data)
        {
            //write to DB
        }
    }

Now I an using a factory class to solve the object creation. It look like below code

public interface IFactory
{
    IWriter GetType(string outputType);
}

public class Factory : IFactory
{
    public IWriter GetType(string outputType)
    {
        IWriter writer = null;
        if (outputType.Equels("db"))
        {
            writer = new FileWriter();
        }
        else if (outputType.Equels("db"))
        {
            writer = new DBWriter();
        }
    }
}

Now the problem is the Factory class is breaking Open closed principle so it also breakes Dependency Inversion Principle

And then

public interface ISaveDataFlow
{
    void SaveData(string data, string outputType);
}

public class SaveDataFlow : ISaveDataFlow
{
    private IFactory _writerFactory = null;
    public SaveDataFlow(IFactory writerFactory)
    {
        _writerFactory = writerFactory;
    }
    public void SaveData(string data, string outputType)
    {
        IWriter writer = _writerFactory.GetType(outputType);
        writer.WriteToStorage(data);
    }
}

As the above factory class is breaking the dependency inversion I remove the Factory class and change the SaveDataFlow class like below

public class SaveDataFlow : ISaveDataFlow
{
    private IWriter _dbWriter = null;
    private IWriter _fileWriter = null;
    public SaveDataFlow([Dependency("DB")]IWriter dbWriter,
                        [Dependency("FILE")]IWriter fileWriter)
    {
        _dbWriter = dbWriter;
        _fileWriter = fileWriter;
    }
    public void SaveData(string data, string outputType)
    {
        if (outputType.Equals("DB"))
        {
            _dbWriter.WriteToStorage(data);
        }
        else if (outputType.Equals("FILE"))
        {
            _fileWriter.WriteToStorage(data);
        }
    }
}

And resolved those dependencies using Unity Framework

container.RegisterType<IWriter, DBWriter>("DB");
container.RegisterType<IWriter, FileWriter>("FILE");

Yet eventually I am ending up breaking Open Closed Principle. I need a better design/solution to solve such a problem yet I must follow SOLID Principles.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I would simply turn it into a strategy pattern:

namespace UnityMutliTest
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    using Microsoft.Practices.Unity;

    class Program
    {
        static void Main(string[] args)
        {
            IUnityContainer container = new UnityContainer();

            container.RegisterType<IWriter, FileWriter>("file");
            container.RegisterType<IWriter, DbWriter>("db");

            container.RegisterType<IWriterSelector, WriterSelector>();

            var writerSelector = container.Resolve<IWriterSelector>();

            var writer = writerSelector.SelectWriter("FILE");

            writer.Write("Write me data");

            Console.WriteLine("Success");

            Console.ReadKey();
        }
    }

    interface IWriterSelector
    {
        IWriter SelectWriter(string output);
    }

    class WriterSelector : IWriterSelector
    {
        private readonly IEnumerable<IWriter> writers;

        public WriterSelector(IWriter[] writers)
        {
            this.writers = writers;
        }

        public IWriter SelectWriter(string output)
        {
            var writer = this.writers.FirstOrDefault(x => x.CanWrite(output));

            if (writer == null)
            {
                throw new NotImplementedException($"Couldn't find a writer for {output}");
            }

            return writer;
        }
    }

    interface IWriter
    {
        bool CanWrite(string output);

        void Write(string data);
    }

    class FileWriter : IWriter
    {
        public bool CanWrite(string output)
        {
            return output == "FILE";
        }

        public void Write(string data)
        {
        }
    }

    class DbWriter : IWriter
    {
        public bool CanWrite(string output)
        {
            return output == "DB";
        }

        public void Write(string data)
        {
        }
    }
}

You can have as many IWriters as you want, just register them:

container.RegisterType<IWriter, LogWriter>("log");

You can even implement decorators over the writers if you want as well.

You use the (badly named) IWriterSelector as the implementation on how to select your writer, this should be concerned with only getting a writer! The throw exception here is really useful, it will fail fast if there is no implementation that suits your needs!!

If you ever have Open Closed problems, either use Strategy or Template patterns to overcome.

I use this pattern all the time, to great effect.

I've created a little extension method to prevent you having to name your instances:

static class UnityExtensions
{
    public static void RegisterMultipleType<TInterface, TConcrete>(this IUnityContainer container)
    {
        var typeToBind = typeof(TConcrete);
        container.RegisterType(typeof(TInterface), typeToBind, typeToBind.Name);
    }
}

container.RegisterMultipleType<IWriter, FileWriter>();

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...