Классы, как упаковка
К классу Rational, создание которого еще не закончено, мы еще вернемся. Пока же продолжим разговор о процедурах - свойствах, поскольку они являются важными компонентами класса. В этом разделе пойдет речь об одном из их довольно неожиданных применений, когда они позволяют выступать классу в роли привлекательной упаковки внутренних и служебных функций. Но обо всем по порядку.
Построим класс Plan, в котором по существу есть только одно свойство, хранящее текущий месяц. Хранится это свойство, как целое, но для внешнего мира оно выглядит как обычное имя месяца. Вот как это реализовано.
Пример 4.3.
(html, txt)
Обратите внимание, работу с закрытым свойством CurMonth обеспечивают процедуры - свойства Property Let ТекущийМесяц и Property Get ТекущийМесяц. С их помощью можно читать и изменять значение свойства CurMonth. Одновременно Let и Get занимаются преобразованием данных, что является распространенной практикой. Пример этот интересен и с позиций построения конструктора по умолчанию, который выполняет все необходимые внутренние инициализации, в данном случае, задавая значение массиву Месяцы.
Приведем еще процедуру, которая работает с данными этого класса:
Public Sub WorkWithPlan() Dim MyPlan As New Plan Debug.Print MyPlan.ТекущийМесяц MyPlan.ТекущийМесяц = "Апрель" Debug.Print MyPlan.ТекущийМесяц MyPlan.ТекущийМесяц = "Янв." Debug.Print MyPlan.ТекущийМесяц
End Sub
В результате ее работы, (процедура запускалась в марте месяце) напечатаны значения:
Март Апрель Март
Задумаемся над сутью решаемой задачи, - нам хочется при обращении к свойству класса получать значение текущего месяца. Но ведь текущий месяц, он и есть текущий, и пользователь не должен изменять его значение. Так что первое, что следовало бы сделать, это воспользоваться стратегией Read- only и не разрешать изменять значение свойства. Поэтому исключим Property Let из нашего класса. Но в этом примере есть куда более интересная особенность. Исключив Property Let, мы понимаем, что теперь теряет смысл хранение самого свойства CurMonth.
Его значение мы можем ( и должны по смыслу свойства) получать динамически, обращаясь к системной процедуре Month(Now). Удалим из класса и переменную CurMonth. Наш новый класс PlanNew стал проще:
'Класс PlanNew 'Закрытый массив, играющий служебную роль Private Месяцы(1 To 12) As String
Private Sub Class_Initialize() Месяцы(1) = "Январь": Месяцы(2) = "Февраль": Месяцы(3) = "Март" Месяцы(4) = "Апрель": Месяцы(5) = "Май": Месяцы(6) = "Июнь" Месяцы(7) = "Июль": Месяцы(8) = "Август": Месяцы(9) = "Сентябрь" Месяцы(10) = "Октябрь": Месяцы(11) = "Ноябрь": Месяцы(12) = "Декабрь" End Sub
Public Property Get ТекущийМесяц() As String ТекущийМесяц = Месяцы(Month(Now)) End Property
Свойство ТекущийМесяц теперь имеет статус Read- only. Но главная суть не в этом. Теперь отчетливо видна настоящая роль класса PlanNew, - он является красивой упаковкой вызова служебных функций Month(Now). Стоило ли создавать класс для этой цели? Не проще ли просто написать стандартную процедуру или вообще все делать самому, обращаясь к служебной функции и затем преобразуя номер месяца в его название? Для одной функции, как в нашем примере, вероятно, не стоит создавать класс, но для десятка функций это вполне целесообразно. Эта техника с особым успехом используется, когда нужно упаковать обращение к Win API функциям. Так что, подводя итог, отметим, что иногда классы используются как упаковка, расширяющая стандартные возможности и особую роль в этом процессе играют процедуры - свойства.