梓囚徒貧圭�鮗� ○ 賜 ★ 辛酔堀貧和鍬匈��梓囚徒貧議 Enter 囚辛指欺云慕朕村匈��梓囚徒貧圭�鮗� ● 辛指欺云匈競何��
!!!!隆堋響頼��紗秘慕禰厮宴和肝写偬堋響��
Implementing a Tax Engine and Tax Account
Implementing a Canadian tax engine means deriving a class from BaseTaxEngine�察�and that
means implementing the CreateTaxAccount�┌� method。 The implementation of the Canadian
tax engine will be in a new namespace�察�and a good name would be Canada ��as this is in the
LibTax project�察�it will have a root namespace of LibTax�察�so LibTax。Canada is the full namespace��。
The code will not show the namespace details�察�as they are implied。
The implementation is as follows�此�
Friend Class TaxEngine
Inherits BaseTaxEngine
Public Overrides Function CreateTaxAccount�┌� As ITaxAccount
Return New TaxAccount�┌�
End Function
´´´´´´´´´´´´´´´´´´´´´´Page 205´´´´´´´´´´´´´´´´´´´´´´´
CH AP T E R 7 * L E AR N IN G AB O U T CO M P O N E N TS AN D C L AS S H I E R AR C HI E S 183
End Class
In the implementation of the CreateTaxAccount�┌� method�察�the TaxAccount class is instan
tiated。 TaxAccount is a class that derives from BaseTaxAccount and thus implements the ITaxAccount
interface。 The implementation of TaxAccount is as follows�此�
Friend Class TaxAccount
Inherits BaseTaxAccount
Private _province As Province
Private _year As Integer
Public Overrides Function GetTaxRate��ByVal ine As Double�� As Double
If _year = 2008 Then
If _province = Province。Ontario Then
Return OntarioTax2008。TaxRate��ine��
End If
End If
Throw New NotSupportedException�─�Year ; & _year & ; Province ; & _
_province & ; not supported;��
End Function
End Class
The purpose of the GetTaxRate�┌� method is to return the applicable tax rate for the given
amount。 In Canada�察�the tax rate is determined by which province you live in and the year。 The
calculation in GetTaxRate�┌� has the ability to calculate the taxes for the year 2008 in the Ontario
province。 Here¨s the skeleton of the OntarioTax2008 class�此�
Public Class OntarioTax2008
Public Shared Function TaxRate��ByVal ine As Double�� As Double
' Return the appropriate rate
End Function
End Class
Yet there is a problem�察�and it involves the data members _province and _year。 The data
members are used in the calculation GetTaxRate�┌��察�but they are not assigned。 This is a problem。
Assigning State When the Interface Cannot
The problem of the Canadian tax account is a mon one that you will encounter in many
situations。 The essence of the problem is the need to assign state that is specific to an imple
mentation without violating the intention of the general interface。
To illustrate the problem�察�let¨s say that the method GetTaxRate�┌� will include a reference
to the province and year。 The rewritten ITaxAccount interface would be as follows�此�
Public Interface ITaxAccount
Sub AddDeduction��ByVal deduction As ITaxDeduction��
Sub AddIne��ByVal ine As ITaxIne��
Function GetTaxRate��ByVal ine As Double�察�ByVal province As Province�察�_
ByVal year As Integer�� As Double
´´´´´´´´´´´´´´´´´´´´´´Page 206´´´´´´´´´´´´´´´´´´´´´´´
184 CH AP T E R 7 * L E A R N IN G AB OU T CO M P O N E N TS AN D C L AS S H I E R AR C H IE S
ReadOnly Property Deductions�┌� As ITaxDeduction�┌�
ReadOnly Property Ine�┌� As ITaxIne�┌�
End Interface
The bolded code illustrates the added parameters that are used to calculate Canadian taxes。
But is this a good solution�拭�No�察�it¨s a particularly bad solution。 The parameters are specific to
an implementation�察�and particularly to a Canadian implementation。
The parameter year could be justified because most countries do have specific tax rates
and implementations that are dependent on a year。 Yet the parameter province has no justifi
cation。 Imagine trying to implement a British tax system and needing to specify a province�察 �
when Britain does not collect ine tax at a local level。
A solution might be to redefine the interface as follows�此�
Public Class Specifics
Public CanadianProvince As Province
Public AmericanState As State
End Class
Public Interface ITaxAccount
Sub AddDeduction��ByVal deduction As ITaxDeduction��
Sub AddIne��ByVal ine As ITaxIne��
Function GetTaxRate��ByVal ine As Double�察�ByVal year As Integer�察�_
ByVal specifics As Specifics�� As Double
ReadOnly Property Deductions�┌� As ITaxDeduction�┌�
ReadOnly Property Ine�┌� As ITaxIne�┌�
End Interface
This new implementation has a specifics parameter�察�which is of type Specifics。 The
purpose of Specifics is to define a class that is a hodgepodge of information that is needed to
determine the correct tax rate。 However�察�the Specifics approach is wrong�察�for the following
reasons�此�
o It requires knowing the implementation�察�which in the case of the interface is a bad idea。
It is like going to a restaurant and saying you would like a waitress with blond hair。
o Even if the type Specifics were acceptable�察�you would be adding and removing data
members depending on how many tax systems you have implemented。 That is a bad
idea and introduces maintenance issues。
The proposed solutions are not acceptable。 Additionally�察�there is still the problem of
having to figure out which tax rate to use。
Implementing Ideas with Specifics
To implement a solution�察�let¨s first start by fixing the TaxAccount class。 The modified version
will have some type of functionality that has data members that reference the year and prov
ince。 Here is the modified and correct implementation of TaxAccount�此�
´´´´´´´´´´´´´´´´´´´´´´Page 207´´´´´´´´´´´´´´´´´´´´´´´
CH AP T E R 7 * L E AR N IN G AB O U T CO M P O N E N TS AN D C L AS S H I E R AR C HI E S 185
Friend Class TaxAccount
Inherits BaseTaxAccount
Private _province As Province
Private _year As Integer
Public Sub New ��ByVal province As Province�察�ByVal year As Integer��
_province = province
_year = year
End Sub
Public Overrides Function GetTaxRate��ByVal ine As Double�� As Double
If _year = 2008 Then
If _province = Province。Ontario Then
Return OntarioTax2008。TaxRate��ine��
End If
End If
Throw New NotSupportedException�─�Year ; & _year & ; Province ; & _
_province & ; not supported;��
End Function
End Class
The fix is to add a constructor that has province and year as parameters。 This sort of fix is
quite mon�察�in that you don¨t change the interfaces�察�rather you change how the implemen
tations are instantiated。 Remember that when you instantiate a specific implementation�察�you
know what functionality you want�察�and thus can give the additional parameters。 Once you are
at the interface level�察�you should need to use only general ideas。
Now the TaxEngine class needs to be fixed。 TaxEngine is responsible for instantiating
TaxAccount�察�and thus to instantiate a Canadian TaxAccount�察�TaxEngine needs additional
parameters�察�as follows�此�
Friend Class TaxEngine
Inherits BaseTaxEngine
Public Overrides Function CreateTaxAccount�┌� As ITaxAccount