Абстрактные базовые классы
Абстрактные базовые классы
На стадии проектирования наследственных связей в программе часто выясняется, что многие классы обладают целым рядом сходных черт. Например, внештатные сотрудники не относятся к постоянным работникам, но и те и другие обладают рядом общих атрибутов — именем, адресом, кодом налогоплательщика и т. д. Было бы логично выделить все общие атрибуты в базовый класс Payabl eEnt i ty. Этот прием, называемый факторингом, часто используется при проектировании классов и позволяет довести абстракцию до ее логического завершения.
В классах, полученных в результате факторинга, некоторые методы и свойства невозможно реализовать, поскольку они являются общими для всех классов в иерархии наследования. Например, класс Payabl eEnt i ty, от которого создаются производные классы штатных и внештатных работников, может содержать свойство с именем TaxID. Обычно в процедуре этого свойства следовало бы организовать проверку кода налогоплательщика, но для некоторых категорий внештатных работников эти коды имеют особый формат. Следовательно, проверка этого свойства должна быть реализована не в базовом классе Payabl eEntity, а в производных классах, поскольку лишь они знают, как должен выглядеть правильный код.
В таких ситуациях обычно определяется абстрактный базовый класс. Абстрактным называется класс, содержащий хотя бы одну функцию с ключевым словом MustOverride; при этом сам класс помечается ключевым словом Mustlnherit. Ниже показано, как может выглядеть абстрактный класс Payabl eEntity:
Public Mustlnherit Class PayableEntity
Private m_Name As String
Public Sub New(ByVal itsName As String)
m_Name = itsName
End Sub
Readonly Property TheName()As String
Get
Return m_Name
End Get
End Property
Public MustOverride Property TaxID()As String
End Class
Обратите внимание: свойство TaxID, помеченное ключевым словом MustOverride, только объявляется без фактической реализации. Члены классов, помеченные ключевым словом MustOverride, состоят из одних заголовков и не содержат команд End Property, End Sub и End Function.
Доступное только для чтения свойство TheName при этом реализовано; из этого следует, что абстрактные классы могут содержать как абстрактные, так и реализованные члены. Ниже приведен пример класса Егор! оуее, производного от абстрактного класса PayableEntity (ключевые строки выделены жирным шрифтом):
Public Class Employee
Inherits PayableEntity
Private m_Salary As Decimal
Private m_TaxID As String
Private Const LIMIT As Decimal = 0.1D
Public Sub NewCByVal theName As String, ByVal curSalary As Decimal.
ByVal TaxID As String) MyBase.New(theName)
m_Salary = curSalary
m_TaxID = TaxID End Sub
Public Overrides Property TaxID() As String Get
Return m_TaxID
End Get
Set(ByVal Value As String)
If Value.Length <> 11 then
' См. главу 7 Else
m_TaxID = Value
End If
End Set
End Property
Readonly Property Salary() As Decimal Get
Return MyClass.m_Salary
End Get
End Property
Public Overridable Overloads Sub RaiseSalary(ByVal Percent As Decimal)
If Percent > LIMIT Then
' Операция запрещена - необходим пароль
Console.WriteLineC'NEED PASSWORD TO RAISE SALARY MORE " & _
"THAN LIMIT!!!!") Else
m_Salary =(1D + Percent) * m_Salary
End If
End Sub
Public Overridable Overloads Sub RaiseSalary(ByVal Percent As
Decimal. ByVal Password As String) If Password ="special" Then
m_Salary MID + Percent) * m_Salary
End If
End Sub
End Class
Первая ключевая строка расположена внутри конструктора, который теперь должен вызывать конструктор абстрактного базового класса для того, чтобы правильно задать имя. Во втором выделенном фрагменте определяется элементарная реализация для свойства Taxld, объявленного с ключевым словом MustOverride (в приведенном примере новое значение свойства не проверяется, как следовало бы сделать в практическом примере).
Ниже приведена процедура Sub Mai n, предназначенная для тестирования этой программы:
Sub Main()
Dim tom As New Employee("Tom". 50000. "111-11-1234")
Dim sally As New Programmed "Sally", 150000. "111-11-2234".)
Console.Wri teLi ne(sa1ly.TheName)
Dim ourEmployees(l) As Employee
ourEmployees(0) = tom
ourEmployees(l) = sally
Dim anEmployee As Employee
For Each anEmployee In ourEmployees anEmployee.RaiseSalary(0.lD)
Console.WriteLine(anEmployee.TheName & "has tax id " & _
anEmployee.TaxID & ".salary now is " & anEmployee.Salary())
Next
Consol e.ReadLine() End Sub
В программе невозможно создать экземпляр класса, объявленного с ключевым словом Mustlnherit. Например, при попытке выполнения следующей команды:
Dim NoGood As New PayableEntity("can't do")
компилятор выводит сообщение об ошибке:
Class 'PayableEntity' is not creatable because it contains at least one member marked as 'MustOverride' that hasn't been overridden.
Тем не менее объект производного класса можно присвоить переменной или контейнеру абстрактного базового класса, что дает возможность использовать в программе полиморфные вызовы:
Dim torn As New Employee("Tom". 50000, "123-45-6789")
Dim whoToPay(13) As PayableEntity whoToPay(0) = tom