I have a bit of a design issue. I created a rate calculator as follows :
Public Interface ICalculator
Property PaymentTerm As Double
Function Calculate() As CommissionValues
ReadOnly Property CalculationRule As CalculationRuleEnum
End Interface
Public Interface IFlexibleRateCalculator
Inherits ICalculator
Property TransferRate As Decimal
End Interface
Public Interface IFixedRateCalculator
Inherits ICalculator
Property ContractRate As Decimal
End Interface
Public Interface IRateSettingBase
Property RateType As RateTypeEnum
ReadOnly Property Calculator As ICalculator
End Interface
Public MustInherit Class RateSetting
Implements IRateSettingBase
Public MustOverride ReadOnly Property Calculator() As ICalculator Implements IRateSettingBase.Calculator
I can do something like this:
dim ratevalues as RateValues = RateSetting().Calculator.Calculate()
Pretty simple. The problem is that each type of calculator has their own set of properties that need to be set in order for their Calculate() methods to work properly. So I end up having to implement as follows
FlexibleRateCalculator
Implements IFlexibleRateCalculator
Private mRequestedRate As Decimal
Public Function Calculate() As RateValues Implements ICalculator.Calculate
FixedRateCalculator
Implements IFixedRateCalculator
Private mTransferRate As Decimal
Public Function Calculate() As RateValues Implements ICalculator.Calculate
What is the best way using generics and abstract classes to create a factory pattern that will generate a calculator of a specific type dynamically??
I need a very generic solution as many calculation rates will be added and modified all with their own parameters needed for the calculation logic. I want to be able to do this quickly and possibly control these rate calculation via db. FYI answers in C# or VB.Net are welcome :) Thanks in advance!
Keep only the ICalculator
interface and convert the more specific interfaces to classes. I can't think of a good reason why you would create a class just to store a variable, so I'm going to get rid of the RateSetting
entirely.
Public Interface ICalculator
Property Rate As Double
Property PaymentTerm As Double
Function Calculate() As CommissionValues
ReadOnly Property CalculationRule As CalculationRuleEnum
End Interface
Public Class FlexibleRateCalculator : Implements ICalculator
Public Sub New(rate As Double)
Me.Rate = rate
End Sub
'
' ICalculator implementation goes here
'
End Class
Public Class FixedRateCalculator : Implements ICalculator
Public Sub New(rate As Double)
Me.Rate = rate
End Sub
'
' ICalculator implementation goes here
'
End Class
Public Enum RateType
Flexible = 1
Fixed = 2
End Enum
Public Class CalculatorFactory
Public Shared Function GetCalculator(rate As Double, type As RateType) As ICalculator
Select Case type
Case RateType.Flexible
Return New FlexibleRateCalculator(rate)
Case RateType.Fixed
Return New FixedRateCalculator(rate)
Case Else
Throw New ArgumentException
End Select
End Function
End Class
You create object instances by passing a rate and a rate type to the GetCalculator
method. I don't know what you mean by CalculationRule
, but if it's important to the end user then you should add it as an additional parameter.
You can easily add more calculator types that implement ICalculator, as long as you don't forget to update the select statement in the factory method.
EDIT: of course you can also set additional properties before returning an object instance. The point of this pattern however is to make sure that the end-user does not need to know about how Calculate()
is implemented. If you want to make more specific factory methods for every calculator, it kind of defeats the purpose.