FactoryPattern с производными классами generi c - PullRequest
0 голосов
/ 29 мая 2020

Это короткий отрывок по сути, я скрыл детали, которые не имеют отношения к делу. Цель состоит в том, чтобы заставить мою фабрику работать с типами, разработанными мною обобщенными типами. В конце поста у меня есть два наблюдения.

Интерфейс

public interface IWorker<T> where T : IWorkload 
{
    T CraftWorkload(string plainWorkload);
    Task DoWork(T workload);
}

IWorkload

public interface IWorkload
{
    // some properties
}

Базовый класс

internal abstract class Worker<T> : IWorker<T> where T : IWorkload
{
    public T CraftWorkload(string workAsJsonString)
    {
        return JsonConvert.DeserializeObject<T>(workAsJsonString, new JsonSerializerSettings
        {
            MissingMemberHandling = MissingMemberHandling.Error
        });
    }

    public abstract Task DoWork(T workload);
}

Другой производный базовый класс

internal abstract class RestWorker<T> : Worker<T> where T : IWorkload
{ 
  // this is a worker that interact with http requests
}

Бетонщики

internal class WorkerA: RestWorker<WorkerA_Workload>
{
    public override Task DoWork(WorkerA_Workload workload)
    {
    }
}

internal abstract class WorkerB: Worker<WorkerB_Workload>
{
    public override Task DoWork(WorkerB_Workload workload)
    {
    }
}

WorkersFactory

MessageType - это перечисление, которое идентифицирует воркера с типом

internal interface IWorkerFactory
{
    IWorker<IWorkload> GetWorker(MessageType type);
}

internal class WorkerFactory : IWorkerFactory
{
    public IWorker<IWorkload> GetWorker(MessageType type)
    {
        switch (type)
        {
            case MessageType.WorkerA:
                return new WorkerA() as IWorker<IWorkload>;
            case MessageType.WorkerB:
                return new WorkerB() as IWorker<IWorkload>;
           .
           .
           .
           .
           .
            default:
                throw new ArgumentException("The type must be a type that maps to an available worker");
        }
    }
}

Два наблюдения:

  1. Если я изменю Factory для возврата без неявного приведения к IWorker<IWorkload>, он запутается
  2. Если я приведу (как показано в коде), он вернет null

Является ли мое наследование смысл?

1 Ответ

2 голосов
/ 29 мая 2020

Ваш интерфейс IWorker<T> представляет воркера, который может создавать объекты типа T, и , потребляя объекты типа T (поскольку он использует T как параметр для метод и возвращаемый тип метода). Это означает, что IWorker<IWorkload> - это объект, который может создавать объекты типа IWorkload, что могут делать WorkerA и WorkerB, поскольку создаваемые ими объекты WorkerA_Workload и WorkerB_Workload являются объектами IWorkload. Но это также означает, что IWorker<IWorkload> должен иметь возможность принимать * любые IWorkload и работать с ним. Ни WorkerA, ни WorkerB не могут этого сделать. Они могут принимать только входные данные WorkerA_Workload и WorkerB_Workload соответственно, но для реализации интерфейса IWorker<IWorkload> они должны иметь возможность принять любой другой тип нагрузки .

Если ваши воркеры действительно могут принять любую IWorkload, и им на самом деле не нужно принимать специфику c типа рабочей нагрузки, то вам необходимо скорректировать свой код, чтобы в основном удалить все общие c типы, ограниченные IWorkload, и вместо этого просто использовать не-общий тип c IWorkload во всех этих местах. (Вам также необходимо сделать IWorker<T> ковариантным по отношению к T.)

Если ваши рабочие не могут на самом деле принять любое IWorkload и нуждаются в спецификации c типа рабочей нагрузки, тогда вы не можете рассматривать всех своих работников как IWorker<IWorkload>, вам нужно всегда рассматривать их как IWorker<T>, где T - фактический конкретный c тип рабочей нагрузки, который они могут принять.

...