Как избежать приведения к указанному c интерфейсу - PullRequest
2 голосов
/ 20 февраля 2020

Справочная информация

У меня есть набор интерфейсов / классов следующим образом. Для простоты представьте больше свойств, коллекций и т. Д. c.

interface IMaster
{
//Some properties
}

interface IB : IMaster
{
    string PropOnA { get; set }
}
interface IC : IMaster
{
    string PropOnB { get; set }
}

class B : IB
class C : IC
...

Эти контракты были разработаны для хранения данных (которые в каждом конкретном случае хранятся в несколько ином формате). Существует много кода, который использует эти контракты для получения, форматирования, обработки, записи и т. Д. c. Мы разработали целую библиотеку, которая не видит конкретные реализации (B, C) любого из этих контрактов, инвертируя управление и позволяя пользователю использовать наши «реализации по умолчанию» для каждого контракта или просто загружая их самостоятельно. У нас есть реестр, в котором пользователь может зарегистрировать другую реализацию.

С этой целью я реализовал своего рода шаблон стратегии, в котором существует стратегия для каждого типа контракта, основанная на выполняемой задаче. Для простоты, скажем, задача состоит в том, чтобы написать , в действительности это намного сложнее.

interface IWriteStrategy
{
    public Write(IMaster thing);
}

class WriterA : IWriteStrategy

class WriterB : IWriteStrategy

etc

Вышеупомянутые конкретные стратегии также никогда не "видны" в нашей библиотеке, Клиент должен зарегистрировать свою собственную реализацию или нашу версию по умолчанию.

Недостаток дизайна ??

Мне не нравятся актеры в каждой стратегии, которая сейчас необходима.

public classWriterA : IWriteStrategy
{
    public void Write(IMaster thing)
    {
        if(thing is IA thingA)
        //do some work
    }
}

public classWriterB : IWriteStrategy
{
    public void Write(IMaster thing)
    {
        if(thing is IB thingB)
        //do some work
    }
}

То, что мы хотим сделать, это иметь возможность oop через список IMaster объектов и выполнение некоторых операций.

foreach(var thing in Things)
{
    var strategy = GetStrategy(thing.GetType());  //this gets the strategy object from our `registry` if one exists
    strategy.Execute(thing);
}

Приведенная выше схема позволяет это сделать, но, похоже, есть недостаток, который я не могу найти в жизни для меня. Мы должны привести к указанному c интерфейсу в каждой реализации стратегии.

Я пробовал с дженериками, но, похоже, просто не могу это прибить.

Вопрос

Какой лучший способ придумать это, чтобы избежать броска, но все же быть в состоянии l oop через список IMaster вещей и относиться к ним одинаково? Или здесь абсолютно необходим актерский состав?

Я пытаюсь следовать дизайну SOLID, но чувствую, что актерский состав портится с этим, поскольку клиент, реализующий стратегии, должен будет выполнить актерский состав, чтобы получить что-либо для работать по методу Write.

[Edit] Я обновил классы, реализующие IWriteStrategy.

Ответы [ 2 ]

1 голос
/ 20 февраля 2020

что об этом, у вас будут все касты в одном методе фабрики стратегии:

 public interface IWriterStrategy
{
    void Execute();
}

public class WriterA : IWriterStrategy
{
    private readonly IA _thing;

    public WriterA(IA thing)
    {
        _thing = thing;
    }
    public void Execute()
    {
        Console.WriteLine(_thing.PropOnA);
    }
}

public class WriterB : IWriterStrategy
{
    private readonly IB _thing;
    public WriterB(IB thing)
    {
        _thing = thing;
    }
    public void Execute()
    {
        Console.WriteLine(_thing.PropOnB);
    }
}



public static class WriterFactory
{
    public static List<(Type Master, Type Writer)> RegisteredWriters = new List<(Type Master, Type Writer)>
        {
           (typeof(IA),  typeof(WriterA)),
           (typeof(IB),  typeof(WriterB))
        };
    public static IWriterStrategy GetStrategy(IMaster thing)
    {
        (Type Master, Type Writer) writerTypeItem = RegisteredWriters.Find(x => x.Master.IsAssignableFrom(thing.GetType()));
        if (writerTypeItem.Master != null)
        {
            return (IWriterStrategy)Activator.CreateInstance(writerTypeItem.Writer, thing);
        }
        throw new Exception("Strategy not found!");
    }
}

public interface IMaster
{
    //Some properties
}

public interface IA : IMaster
{
    string PropOnA { get; set; }
}

public interface IB : IMaster
{
    string PropOnB { get; set; }
}

public interface IC : IMaster
{
    string PropOnC { get; set; }
}

public class ThingB : IB
{
    public string PropOnB { get => "IB"; set => throw new NotImplementedException(); }
}

public class ThingA : IA
{
    public string PropOnA { get => "IA"; set => throw new NotImplementedException(); }
}

public class ThingC : IC
{
    public string PropOnC { get => "IC"; set => throw new NotImplementedException(); }
}

internal static class Program
{
    private static void Main(string[] args)
    {
        var things = new List<IMaster> { 
            new ThingA(),
            new ThingB()//,
            //new ThingC()
        };



        foreach (var thing in things)
        { 
            var strategy = WriterFactory.GetStrategy(thing);  //this gets the strategy object from our `registry` if one exists
            strategy.Execute();
        }
    }
}
1 голос
/ 20 февраля 2020

Может быть, уместно использовать шаблон Стратегии, просто дать реализацию и выполнить ее. Позвольте мне показать пример.

interface IMaster
{
    void ExecuteMaster();
}


class MasterOne : IMaster
{
    public void ExecuteMaster()
    {
        Console.WriteLine("Master One");
    }
}


class MasterTwo : IMaster
{
    public void ExecuteMaster()
    {
        Console.WriteLine("Master Two");
    }
}

и

interface IWriteStrategy
{
    void Write(IMaster thing);
}

class WriterA : IWriteStrategy
{
    public void Write(IMaster thing)
    {
        Console.WriteLine("Writer A");
        thing.ExecuteMaster();
    }
}

class WriterB : IWriteStrategy
{
    public void Write(IMaster thing)
    {
        Console.WriteLine("Writer B");
        thing.ExecuteMaster();
    }
}

и код для выполнения:

static void Main(string[] args)
{
    List<IWriteStrategy> writeStrategies = new List<IWriteStrategy>()
    {
        new WriterA(),
        new WriterB()
    };
    List<IMaster> executes = new List<IMaster>()
    {
         new MasterOne(),
         new MasterTwo()
    };

    for (int i = 0; i < writeStrategies.Count(); i++)
    {
         writeStrategies[i].Write(executes[i]);
    }
}
...