Проблема неустойчивости базовых классов и контроль версии
Проблема несовместимости компонентов хорошо известна всем, кому доводилось программировать для Windows. Обычно она выступает в форме так называемого кошмара DLL (DLL Hell) – программа использует определенную версию DLL, a потом установка новой версии компонента нарушает работу программы. Почему? Причины могут быть разными, от очевидных (случайное исключение функции, использовавшейся в программе) до весьма нетривиальных (например, изменение типа возвращаемого значения у функции). В любом случае все сводится к вариациям на одну тему – при изменении открытого интерфейса кода, от которого зависит ваша программа, программа не может использовать новую версию вместо старой, а старая версия уже стерта.
В большинстве объектно-ориентированных языков наследование сопряжено с потенциальной угрозой работоспособности вашей программы из-за несовместимости компонентов. Программисту остается лишь надеяться на то, что открытые и защищенные члены классов-предшественников в иерархии наследования не будут изменяться, таким образом, что это нарушит работоспособность их программ. Эта ситуация называется проблемой неустойчивости базовых классов. Наследование часто превращает наши программы в некое подобие карточного домика – попробуйте вытащить нижнюю карту, и все сооружение развалится.
Проблему неустойчивости базовых классов желательно рассмотреть на конкретном примере. Разместите приведенное ниже определение класса PayableEntity в отдельной библиотеке и откомпилируйте его в сборку с именем PayableEntityExample командой Build (чтобы задать имя сборки, щелкните правой кнопкой мыши на имени проекта в окне решения, выберите в контекстном меню команду Properties и введите нужные значения в диалоговом окне). Если вы не используете архив с примерами, прилагаемый к книге, запомните, в каком каталоге был построен проект:
Public Mustlnherit Class PayableEntity Private m_Name As String Public Sub New(ByVal theName As String) m_Name =theName End Sub Public Readonly Property TheName()As String Get Return m_Name End Get End Property Public MustOverride Property TaxID()As String End Class
После построения DLL закройте решение.
Допустим, вы решили включить в класс Employee новый способ получения адреса, зависящий от базового класса PayableEntity; при этом следует помнить, что класс будет использоваться только в откомпилированной форме. Для этого необходимо включить ссылку на сборку, содержащую этот проект (находится в подкаталоге \bin того каталога, в котором была построена DLL PayableEntityExample). Примерный код класса Employee приведен ниже. Обратите внимание на строку, выделенную жирным шрифтом, в которой класс объявляется производным от абстрактного класса, определенного в сборке PayableEntityExample.
Public Class Employee ' Пространство имен называется PayableEntityExample. ' поэтому полное имя класса записывается в виде PayableEntityExample.PayableEntity! Inherits PayableEntityExample.Employee Private m_Name As String Private m_Salary As Decimal Private m_Address As String Private m_TaxID As String Private Const LIMIT As Decimal = 0.1D Public Sub New(ByVal theName As String, ByVal curSalary As Decimal, ByVal TaxID As String) MyBase.New(theName) m_Name = theName m_Salary = curSalary m_TaxID = TaxID End Sub Public Property Address()As String Get Return m_Address End Get Set(ByVal Value As String) m_Address = Value End Set End Property Public Readonly Property Salary()As Decimal Get Return m_Salary End Get End Property Public Overrides Property TaxIDO As String Get Return m_TaxID End Get SetCByVal Value As String) If Value.Length <> 11 Then ' См. главу 7 Else m_TaxID = Value End If End Set End Property End Class