Интерфейс позволяет двум объектам на его концах изменяться независимо. Например, если свойства объекта слева на рис. 13.3 изменятся, интерфейс должен быть изменен, а объект справа — нет.
В этой парадигме процедуры, используемые двумя объектами, поддерживаются разработчиками, которые отвечают за эти объекты. Разработчик, который реализует левый объект, также занимается реализацией процедур интерфейса, которые взаимодействуют с левым объектом.
Фасад (Facade) аналогичен интерфейсу, но он обеспечивает простой интерфейс для сложного объекта или группы объектов. Фасад также иногда называется упаковщиком (wrapper). На рис. 13.4. показана схема работы фасада.
Разница между фасадом и интерфейсом в основном умозрительная. Основная задача интерфейса — обеспечение косвенного взаимодействия между объектами, чтобы они могли развиваться независимо. Основная задача фасада — облегчение использования каких‑то сложных вещей за счет скрытия деталей.
Порождающий объект (Factory) — это объект, который создает другие объекты. Порождающий метод — это процедура или функция, которая создает объект.
Порождающие объекты наиболее полезны, если два класса должны тесно работать вместе. Например, составной класс может содержать порождающий метод, который создает итераторы для него. Порождающий метод может инициализировать итератор таким образом, чтобы он был готов к работе с экземпляром класса, который его создал.
@Рис. 13.3 Интерфейс
========366
@Рис. 13.4. Фасад
Программа IterTree создает полное двоичное дерево, записанное в массиве. После нажатия на одну из кнопок, задающих направление обхода, программа создает объект Traverser (Обходчик). Она также использует один из порождающих методов дерева для создания соответствующего итератора. Объект Traverser использует итератор для обхода дерева и вывода списка узлов в правильном порядке. На рис. 13.5 приведено окно программы IterTree, показывающее обратный обход дерева.
Единственный объект (singleton object) — это объект, который существует в приложении в единственном экземпляре. Например, в Visual Basic определен класс Printer (Принтер). Он также определяет единственный объект с тем же названием. Этот объект представляет принтер, выбранный в системе по умолчанию. Так как в каждый момент времени может быть выбран только один принтер, то имеет смысл определить объект Printer как единственный объект.
Один из способов создания единственного объекта заключается в использовании процедуры, работающей со свойствами в модуле BAS. Эта процедура возвращает ссылку на объект, определенный внутри модуля как закрытый. Для других частей программы эта процедура выглядит как просто еще один объект.
@Рис. 13.5. Программа IterTree, демонстрирующая обратный обход
=======367
Программа WinList использует этот подход для создания единственного объекта класса WinListerClass. Объект класса WinListerClass представляет окна в системе. Так как операционная система одна, то нужен только один объект класса WinListerClass. Модуль WinList.BAS использует следующий код для создания единственного объекта с названием WindowLister.
Private m_WindowLister As New WindowListerClass
Property Get WindowLister() As WindowListerClass
Set WindowLister = m_WindowLister
End Property
Единственный объект WindowLister доступен во всем проекте. Следующий код демонстрирует, как основная программа использует свойство WindowList этого объекта для вывода на экран списка окон.
WindowListText.Text = WindowLister.WindowList
Многие приложения сохраняют объекты и восстанавливают их позднее. Например, приложение может сохранять копию своих объектов в текстовом файле. При следующем запуске программы, она считывает это файл и загружает объекты.
Объект может содержать процедуры, которые считывают и записывают его в файл. Общий подход может заключаться в том, чтобы создать процедуры, которые сохраняют и восстанавливают данные объекта, используя строку. Поскольку запись данных объекта в одной строке преобразует объект в последовательность символов, этот процесс иногда называется преобразованием в последовательную форму (serialization).
Преобразование объекта в строку обеспечивает большую гибкость основной программы. При этом она может сохранять и считывать объекты, используя текстовые файлы, базу данных или область памяти. Она может переслать представленный таким образом объект по сети или сделать его доступным на Web‑странице. Программа или элемент ActiveX на другом конце может использовать преобразование объекта в строку для воссоздания объекта. Программа также может дополнительно обработать строку, например, зашифровать ее после преобразования объекта в строку и расшифровать перед обратным преобразованием.
Один из подходов к преобразованию объекта в последовательную форму заключается в том, чтобы объект записал все свои данные в строку заданного формата. Например, предположим, что класс Rectangle (Прямоугольник) имеет свойства X1, Y1, X2 и Y2. Следующий код демонстрирует, как класс может определять процедуры свойства Serialization:
Property Get Serialization() As String
Serialization = _
Format$(X1) & ";" & Format$(Y1) & ";" & _
Format$(X2) & ";" & Format$(Y2) & ";"
End Property
Property Let Serialization(txt As String)
Dim pos1 As Integer
Dim pos2 As Integer
pos1 = InStr(txt, ";")
X1 = CSng(Left$(txt, pos1 - 1))
pos2 = InStr(pos1 + 1, txt, ";")
Y1 = CSng(Mid$(txt, pos1 + 1, pos2 – pos1 - 1))
pos1 = InStr(pos2 + 1, txt, ";")
X2 = CSng(Mid$(txt, pos2 + 1, pos1 - pos2 - 1))
pos2 = InStr(pos1 + 1, txt, ";")
Y2 = CSng(Mid$(txt, pos1 + 1, pos2 – pos1 - 1))
End Property
Этот метод довольно простой, но не очень гибкий. По мере развития программы, изменения в структуре объектов могут заставить вас перетранслировать все сохраненные ранее преобразованные в последовательную форму объекты. Если они находятся в файлах или базах данных, для загрузки старых данных и записи их в новом формате может потребоваться написание программ‑конверторов.
Более гибкий подход заключается в том, чтобы сохранять вместе со значениями элементов данных объекта их имена. Когда объект считывает данные, преобразованные в последовательную форму, он использует имена элементов для определения значений, который необходимо установить. Если позднее в определение элемента будут добавлены какие‑либо элементы, или удалены из него, то не придется преобразовывать старые данные. Если новый объект загрузит старые данные, то он просто проигнорирует не поддерживаемые более значения.
Определяя значения данных по умолчанию, иногда можно уменьшить размер преобразованных в последовательную форму объектов. Процедура get свойства Serialization сохраняет только значения, которые отличаются от значений по умолчанию. Перед тем, как процедура let свойства начнет выполнение преобразования в последовательную форму, она инициализирует все элементы объекта значениями по умолчанию. Значения, не равные значениям по умолчанию, обновляются по мере обработки данных процедурой.
Программа Shapes использует этот подход для сохранения и загрузки с диска рисунков, содержащих эллипсы, линии, и прямоугольники. Объект ShapePicture представляет весь рисунок целиком. Он содержит коллекцию управляющих объектов, которые представляют различные фигуры.
Следующий код демонстрирует процедуры свойства Serialization объекта ShapePicture. Объект ShapePicture сохраняет имя типа для каждого из типов объектов, а затем в скобках — представление объекта в последовательной форме.
Property Get Serialization() As String
Dim txt As String
Dim i As Integer
For i = 1 To LastCmd
txt = txt & _
TypeName(CmdObjects(i)) & "(" & _
CmdObjects(i).Serialization & ")"
Next I
Serialization = txt
End Property
==========369
Процедура let свойства Serialization использует подпрограмму GetSerialization для чтения имени объекта и списка данных в скобках. Например, если объект ShapePicture содержит команду рисования прямоугольника, то его представление в последовательной форме будет включать строку “RectangleCMD”, за которой будут следовать данные, представленные в последовательной форме.
Процедура использует подпрограмму CommandFactory для создания объекта соответствующего типа, а затем заставляет новый объект преобразовать себя из последовательной формы представления.
Property Let Serialization(txt As String) Dim pos As Integer Dim token_name As String Dim token_value As String Dim and As Object
' Start a new picture.
NewPicture
' Read values until there are no more.
GetSerialization txt, pos, token_name, token_value Do While token_name <> ""
' Make the object and make it unserialize itself.
Set and = ConiniandFactory(token_name)
If Not (and Is Nothing) Then _
and.Serialization = token_value
GetSerialization txt, pos, token_name, tokerL-value Loop
LastCmd = CmdObjects.Count End Property
Парадигма Модель/Вид/Контроллер (МВК) (Model/View/Controller) позволяет программе управлять сложными соотношениями между объектами, которые сохраняют данные, объектами, которые отображают их на экране, и объектами, которые оперируют данными. Например, приложение работы с финансами может выводить данные о расходах в виде таблицы, секторной диаграммы, или графика. Если пользователь изменяет значение в таблице, приложение должно автоматически обновить изображение на экране. Может также понадобиться записать измененные данные на диск.
Для сложных систем управление взаимодействием между объектами, которые хранят, отображают и оперируют данными, может быть достаточно запутанным. Парадигма Модель/Вид/Контроллер разбивает взаимодействия, так что можно работать с ними по отдельности, при этом используются три типа объектов: модели, виды, и контроллеры.
Модель (Model) представляет данные, обеспечивая методы, которые другие объекты могут использовать для проверки и изменения данных. В приложении работы с финансовыми данными, модель содержит данные о расходах. Она обеспечивает процедуры для просмотра и изменения значений расходов и ввода новых значений. Она также может обеспечивать функции для вычисления суммарных величин, таких как полные издержки, расходы по подразделениям, средние расходы за месяц, и так далее