Организация структуры фабрики
Есть структура продуктов (упрощённо):
public class ParentProduct { }
public sealed class ChildProduct1 : ParentProduct { }
public sealed class ChildProduct2 : ParentProduct { }
Отдельно от этих типов есть enum
для каждого типа. Нужно так же учесть, что этот enum
и все ParentProduct
наследники не связаны напрямую
public enum ProductType
{
Parent = 1,
Child1 = 2,
Child2 = 3,
}
Есть примерно такая же структура обработчиков как ниже:
public interface IProductHandler<T> where T : ParentProduct
{
public Task Add(T product);
}
public sealed class ParentHandler : IProductHandler<ParentProduct>
{
public async Task Add(ParentProduct product)
{
// ...
}
}
public sealed class Child1Handler : IProductHandler<ChildProduct1>
{
public async Task Add(ChildProduct1 product)
{
// ...
}
}
public sealed class Child2Handler : IProductHandler<ChildProduct2>
{
public async Task Add(ChildProduct2 product)
{
// ...
}
}
Теперь появилась необходимость создания фабрики, которая по ProductType
будет делать соответствующий обработчик. Но конкретно на этом этапе я не понимаю, как правильно реорганизовать структуру, что бы работало примерно следующим образом:
public sealed class ProductFactory
{
public IProductHandler<T> GetHandler<T>(ProductType type) where T : ParentProduct =>
type switch
{
ProductType.Parent => new ParentHandler()
};
}
Так же стоит учесть, что с требований нужно обойтись БЕЗ использования явного приведения типов через as
и сравнение через typeof
и т.п. Иными словами, вот такой формат не подходит:
public interface IProductHandler
{
public Task Add<T>(T product) where T : ParentProduct;
}
Или такой
public interface IProductHandler
{
public Task Add(ParentProduct product);
}
Так как внутри реализации потребуется явно преобразовывать типы.
UPD:
В комментариях спросили, как я собираюсь это использовать. В проекте есть несколько таких мест, в основном это экшен контроллера и ViewComponent
. Код на примере использования в контроллере:
public sealed class ProductController(ProductFactory factory) : ControllerBase
{
[HttpGet("{type}/{id:int}")]
public async Task<IActionResult> DetDetails([FromRoute] ProductType type, [FromRoute] int id)
{
IProductHandler handler = factory.GetHandler(type)
return Ok(await handler.GetDetails(id));
}
}
Ответы (1 шт):
проблема в том, что не работает, т.к. нельзя напрямую конвертировать
ParentHandler
вIProductHandler<T>
как сообщает мне ошибка
Ну это легко, контрвариантый интерфейс.
public interface IProductHandler<in T> where T : ParentProduct
{
public Task Add(T product);
}
И всё, ошибка в фабрике ушла.
public sealed class ProductFactory
{
public IProductHandler<T> GetHandler<T>(ProductType type) where T : ParentProduct =>
type switch
{
ProductType.Parent => new ParentHandler() // OK
};
}
Но я бы на вашем месте избавился бы от перечисления.
Например, если учесть, что все продукты имеют уникальный ID, то передача типа - избыточная информация, так как по ID вы получите конкретный экземпляр продукта, который уже имеет свой конкретный тип, по нему и можно будет определить тип хэндлера. Ну там уже насколько фантазии хватит. Хотя мне очень сильно кажется, что вся задумка с этими хэндлерами под сомнением, и продукт должен уметь сам себя сериализовать product.GetDetails()
. То есть получать я бы стал некий IProduct
, а не IProductHandler
и вызывал бы метод сериализации у него.