Forse qualcosa sulla falsariga di:
public class CalculatorItem
{
}
public class CaptiveManagementFee : CalculatorItem
{
}
public class InterestMargin : CalculatorItem
{
}
public class ServiceMarkup : CalculatorItem
{
}
public interface ICalculator
{
void Calculate(CalculatorItem item);
}
public interface ICalculator<TCalculatorItem> : ICalculator
where TCalculatorItem : CalculatorItem
{
void Calculate(TCalculatorItem item);
}
public class CalculatorBase : ICalculator
{
protected void Witness(string method, object item)
{
Console.WriteLine($"{GetType()}.{method}({item.GetType()})");
}
#region ICalculator
public void Calculate(CalculatorItem item)
{
if (item == null)
{
throw new ArgumentNullException("undecidable item type for null", nameof(item));
}
var itemType = item.GetType();
var specialized = GetType().GetMethod(nameof(Calculate), new[] { itemType });
if (specialized == null)
{
throw new ArgumentException($"unsupported item type: {itemType}", nameof(item));
}
specialized.Invoke(this, new object[] { item });
}
#endregion
}
public class CaptiveCalculator : CalculatorBase,
ICalculator<CaptiveManagementFee>,
ICalculator<ServiceMarkup>
{
protected virtual void CalculateFee(CaptiveManagementFee item)
{
Witness(nameof(CalculateFee), item);
}
protected virtual void CalculateMarkup(ServiceMarkup item)
{
Witness(nameof(CalculateMarkup), item);
}
#region ICalculator<CaptiveManagementFee>
public void Calculate(CaptiveManagementFee item)
{
CalculateFee(item);
}
#endregion
#region ICalculator<ServiceMarkup>
public void Calculate(ServiceMarkup item)
{
CalculateMarkup(item);
}
#endregion
}
public class HeadquarterCalculator : CalculatorBase,
ICalculator<InterestMargin>,
ICalculator<ServiceMarkup>
{
protected virtual void CalculateMargin(InterestMargin item)
{
Witness(nameof(CalculateMargin), item);
}
protected virtual void CalculateMarkup(ServiceMarkup item)
{
Witness(nameof(CalculateMarkup), item);
}
#region ICalculator<InterestMargin>
public void Calculate(InterestMargin item)
{
CalculateMargin(item);
}
#endregion
#region ICalculator<ServiceMarkup>
public void Calculate(ServiceMarkup item)
{
CalculateMarkup(item);
}
#endregion
}
public class CalculatorFactory
{
private static Type[] CalculatorTypes =
new[] { typeof(CaptiveCalculator), typeof(HeadquarterCalculator) };
public ICalculator<TCalculatorItem> CreateCalculator<TCalculatorItem>()
where TCalculatorItem : CalculatorItem
{
var found =
CalculatorTypes.
Single // (throws if we don't find exactly *one* matching type)
(
type =>
typeof(ICalculator<>).MakeGenericType(typeof(TCalculatorItem)).IsAssignableFrom(type)
);
return (ICalculator<TCalculatorItem>)Activator.CreateInstance(found);
}
}
class Program
{
static void Main(string[] args)
{
var calcFactory = new CalculatorFactory();
var headQuarterCalc = calcFactory.CreateCalculator<InterestMargin>();
headQuarterCalc.Calculate(new InterestMargin());
//headQuarterCalc.Calculate(new CaptiveManagementFee()); // throws an exception (unsupported item type)
headQuarterCalc.Calculate(new ServiceMarkup()); // late-bound, dispatched via ICalculator.Calculate(CalculatorItem)
var captiveCalc = calcFactory.CreateCalculator<CaptiveManagementFee>();
captiveCalc.Calculate(new CaptiveManagementFee());
//captiveCalc.Calculate(new InterestMargin()); // throws an exception (unsupported item type)
captiveCalc.Calculate(new ServiceMarkup()); // late-bound, dispatched via ICalculator.Calculate(CalculatorItem)
//var ambiguousCalc = calcFactory.CreateCalculator<ServiceMarkup>(); // throws an exception
Console.ReadKey();
}
}
Questo è un po 'troppo di norma a mio gusto (YMMV), ma non posso davvero pensare a nulla di più semplice considerando le tue esigenze, prese così com'è.
'Spero che questo aiuti.