I need help with an abstract factory pattern design.I have and calculation engine which calculates for different channels like captive, captiveTemplate or Headquarter and in the future different channel can be added. Attached is the drawing on how I am setting up the pattern:
The problem is some items are calculated only for specific channels and some items calculated for all type of channels.For example, CaptiveManagementFee only calculates in CaptiveCalculatorFactory and InterestMargin calculated only in HeadQuarterFactory but ServiceMarkup should be calculated in all type of calculatorFactories.How should I design my classes to solve this problem?
-
3The point of having a factory is usually to decouple client code from having to distinguish between different concrete types. But in your diagram you have a separate factory type for every calculator type. Are you sure you are actually implementing a factory behaviour?Kilian Foth– Kilian Foth05/11/2017 09:31:57Commented May 11, 2017 at 9:31
-
2It would help me to understand if you could provide a code example demonstrating how you intend to use these factories, and why you need so many.John Wu– John Wu05/11/2017 19:42:59Commented May 11, 2017 at 19:42
1 Answer 1
Maybe something along the lines of:
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();
}
}
That's a bit too much of boilerplate to my taste (YMMV), but I can't really think of anything simpler considering your requirements, taken as-is.
'Hope this helps.
-
Thank you so much i will use builder pattern instead of abstract factory beacause i have different calulation items per calculation chanels. But thank you a lot for your recomendations and solutions.I can use some part of your solution.Fikret Asma– Fikret Asma05/15/2017 05:26:33Commented May 15, 2017 at 5:26
Explore related questions
See similar questions with these tags.