按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
to our project structure; this scope declaration implies that any reference to LibTax will see the
interface ITaxEngine and BaseTaxEngine; but not TaxEngine。 For example; the following test
code will not work。
Dim taxengine As ITaxEngine = New Surtax。TaxEngine()
The reason the test code will not work is that any type that is not declared with Public scope
is private to the solution containing the declaration。 You may be thinking; “That’s great—you
declare a type that you cannot instantiate。 So; how can I use that type?”
The scope declarations are not a mistake and illustrate a design pattern called a factory 。 A
factory is a way of abstracting the instantiation away from the caller so that the interface can
vary from its implementation。 In our restaurant analogy; it means when you want a waiter; you
…………………………………………………………Page 200……………………………………………………………
178 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
don’t want to need to know his name。 You would rather have a generic mechanism where the
restaurant presents to you the waiter。 Otherwise; to eat at a restaurant; you would need to
know the name of your waiter before being able to order something。 That would be inefficient。
The correct way of defining a factory is as follows:
Public Module EngineCreator
Public Function CreateSurtaxTaxEngine() As ITaxEngine
Return New Surtax。TaxEngine()
End Function
' Required for country…specific tax calculations
Public Function CreateCanadianTaxEngine() As ITaxEngine
Return New Canada。TaxEngine()
End Function
End Module
The factory is typically declared in a module because a module is not instantiated。 Generally
speaking; you don’t want to add a factory to an object that can be instantiated because it could
result in context…specific instantiation。 In other words; you usually want a fresh object created
from scratch each time; so it’s not affected by the current state of the application。 In the imple
mentation of CreateSurtaxTaxEngine(); the type Surtax。TaxEngine is instantiated; and the
instance is cast to the interface type ITaxEngine。
The EngineCreator module is declared with public scope; implying any code that references
the assembly can see the module。 Thus; the test code can be rewritten as follows (we need to
import the LibTax project; of course):
Dim taxengine As ITaxEngine = EngineCreator。CreateSurtaxTaxEngine()
After calling EngineCreator。CreateSurtaxTaxEngine(); the test code has a valid instance of
ITaxEngine。 It’s very important to note that the test code has no idea what type implemented
the interface。 This allows the assembly to change which type is referenced in the implementa
tion of CreateSurtaxTaxEngine() without having to inform the caller of the method。
Putting this into the context of the restaurant; it means waiters can be replaced。 So if you
repeatedly visit a restaurant and get a waiter called John; but one day John gets sick and is not
working; you can still order and receive your food from the waitress called Mary。 It would be a
bad idea for a restaurant to depend on a particular server for a particular guest。
Using Default Implementations
In some cases; base classes are not necessary。 Sometimes you can create a default implemen
tation that could span multiple subsystems。 In the case of the tax engine; an ine is an ine
in Canada; an ine in the United States; and an ine in Germany。 What varies is how the
ine is treated in each country when calculating taxes。 Another consistency across countries is
that if ine is a capital gain; not all of the ine is taxable。
In the case of ine; you can create an implementation that would be identical across
different tax engines; as follows:
…………………………………………………………Page 201……………………………………………………………
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 179
NotInheritable Class TaxIne
Implements ITaxIne
Private _amount As Double
Private _taxableRate As Double
Public Sub New(ByVal amount As Double; ByVal taxableRate As Double)
_amount = amount
_taxableRate = taxableRate
End Sub
Public ReadOnly Property RealAmount() As Double _
Implements ITaxIne。RealAmount
Get
Return _amount
End Get
End Property
Public ReadOnly Property TaxableAmount() As Double _
Implements ITaxIne。TaxableAmount
Get
Return _amount * _taxableRate
End Get
End Property
End Class
The ITaxIne interface has two properties that are implemented in TaxIne: RealAmount
and TaxableAmount。 The values for the two properties are considered read…only and are defined
by the constructor of TaxIne。 The purpose of the constructor is to assign two values and
then consider the object as immutable。 If you wanted to change the values of the ITaxIne
interface; you would need to instantiate a new instance of TaxIne。 While it sounds like a pain
to need to instantiate a new instance whenever you want to change the value of the RealAmount and
TaxableAmount properties; this approach has some advantages in terms of performance and
resource management。
In the sample code; the TaxableAmount property is the result of multip