I have a chain of responsibility that applies filters to a collection. I am trying to make a factory to build that chain of responsibility from a configuration. My concrete types for the chain arent generic but their abstraction are, and the genericity makes me struggle to put them in a collection for a mapping between config and correct chain node implementation.
Here is the implementation of the chain :
public interface IFilter<T> where T : IFilterable
{
IFilter<T> SetNext(IFilter<T> next);
IEnumerable<T> Filter(IEnumerable<T> data);
}
public class BaseFilter<T> : IFilter<T> where T : IFilterable
{
protected IFilter<T> Next { get; set; }
public IFilter<T> SetNext(IFilter<T> next)
{
Next = next;
return Next;
}
public virtual IEnumerable<T> Filter(IEnumerable<T> data)
{
return Next == null ? data : Next.Filter(data);
}
}
Here is an example of concrete implementation of the nodes of the chain :
public interface IFilterable {}
public interface ICanFly: IFilterable
{
bool CanFly { get; }
}
public interface ITransport : IFilterable
{
int Passengers { get; }
}
public class Duck : ICanFly
{
public bool CanFly => true;
}
public class Plane : ICanFly, ITransport
{
public bool CanFly => true;
public int Passengers => 5;
}
public class FlyerFilter : BaseFilter<ICanFly>
{
public override IEnumerable<ICanFly> Filter(IEnumerable<ICanFly> data)
{
return base.Filter(data.Where(x => x.CanFly));
}
}
public class SmallTransportFilter : BaseFilter<ITransport>
{
public override IEnumerable<ITransport> Filter(IEnumerable<ITransport> data)
{
return base.Filter(data.Where(x => x.Passengers < 8));
}
}
My problems start when I want to make a factory that map the configuration to my concrete types (FlyerFilter
and SmallTransportFilter
in my example)
public interface IFilterChainBuilder<T> where T : IFilterable
{
IFilter<T> GenerateFilterResponsabilityChain(IEnumerable<string> filtersParam);
}
public class FilterChainBuilder<T> : IFilterChainBuilder<T> where T : IFilterable
{
private readonly Dictionary<string, IFilter<T>> _paramToFiltersMap;
public FilterChainBuilder()
{
_paramToFiltersMap = new Dictionary<string, IFilter<T>>(StringComparer.OrdinalIgnoreCase)
{
{"Flyers", new FlyerFilter()}, // Compile error, cannot convert from FlyerFilter to IFilter<T>
{"SmallTransport", new SmallTransportFilter()} // Compile error, cannot convert from SmallTransportFilter to IFilter<T>
};
}
public IFilter<T> GenerateFilterResponsabilityChain(IEnumerable<string> filtersParam)
{
IFilter<T> filterResponsabilityChain = null;
foreach (var parameter in filtersParam)
if (_paramToFiltersMap.TryGetValue(parameter, out var filter))
{
if (filterResponsabilityChain == null)
filterResponsabilityChain = filter;
else
filterResponsabilityChain.SetNext(filter);
}
else
{
throw new ArgumentException(
$"config parameter {parameter} has no associated IFilter");
}
return filterResponsabilityChain ?? new BaseFilter<T>();
}
}
I can understand why it doesnt compile. Since FlyerFilter
is a BaseFilter<ICanFly>
(so a IFilter<ICanFly>
), it would be bad if I declared a new FilterChainBuilder<PlaceholderType>
. And actually since SmallTransportFilter
inherit from a different T type, the only possible IFilterable
implementation would have to implement both ITransport
and ICanFly
.
I tried to remove the generic T type entirely but the consummer of this chain of responsability relies on that IEnumerable<T> Filter(IEnumerable<T> data)
signature and wants an enumeration of concrete types rather than IFilterable
.
I am not sure how could I fix this problem, I am currently stuck here.
Aucun commentaire:
Enregistrer un commentaire