Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Design Patterns via C#.pdf
Скачиваний:
154
Добавлен:
17.03.2016
Размер:
13.25 Mб
Скачать

99

объект класса Adapter невозможно использовать там, где может использоваться объект класса

Adaptee.

Имеется разновидность адаптеров, которые называются двусторонними адаптерами (twoway adapters). Двусторонние адаптеры способны обеспечить возможность использовать адаптер TwoWayAdapter там, где мог использоваться Adaptee.

Интерфейс двустороннего адаптера (класса TwoWayAdapter) представляет собой объединение интерфейсов адаптируемых друг к другу классов AdapteeNew и AdapteeOld.

См. Пример к главе: \006_Adapter\005_TwoWayAdapter

Реализация

Имеется три подхода к реализации сменных адаптеров. Эти способы проще рассмотреть на примере использования пользовательского элемента управления TreeDisplay, для автоматического отображения иерархических древовидных структур (TreeDisplay уже был описан выше).

Первый подход – использование абстрактных операций.

При таком подходе в теле абстрактного класса-цели TreeDisplay необходимо задать абстрактный метод Display, с аргументом типа object, значение аргумента должно представлять собой ссылку на иерархическую структуру. Метод Display, в данном случае, формирует «узкий» интерфейс для адаптируемых классов со встроенными адаптерами: AssemblyTreeDisplay и DirectoryTreeDisplay. Другими словами, метод Display представляет собой наименьшее подмножество операций, позволяющих выполнить адаптацию, и его реализация фактически является оберткой над специфическими методами адаптируемых классов AssemblyTreeDisplay и DirectoryTreeDisplay, которые позволяют осуществлять доступ к иерархической структуре сборки проекта и структуре каталогов файловой системы.

См. Пример к главе: \006_Adapter\006_3_AdaptersForTreeDisplay

Второй подход – использование объектов-уполномоченных.

Для того чтобы реализовать данный подход потребуется создать специальные объекты – «объекты-уполномоченные». Тогда класс TreeDisplay сможет переадресовывать запросы для доступа к иерархической структуре этим объектам-уполномоченным, при этом, реализовывая различные стратегии адаптации путем использования необходимых конкретных уполномоченных. Например, одним из таких объектов-уполномоченных для класса TreeDisplay мог бы быть класс DirectoryBrowser. В статически типизированных языках, которым и является C#, потребуется явно задать интерфейс, который необходим классу TreeDisplay, например, interface ITreeAccessorDelegate, и реализовать этот интерфейс в выбранном классе-уполномоченном

DirectoryBrowser.

Третий подход – адаптеры, конфигурируемые при помощи параметров.

Этот подход предоставляет возможность использования конструкции switch или нескольких условных конструкций if-else в теле адаптера, как альтернативу наследованию при реализации

100

сменных адаптеров. Такой подход позволяет реализовывать механизмы адаптации без порождения подклассов. Применимо, к рассматриваемому примеру это означает, что в классе TreeDisplay может быть два условных блока: первый блок, для преобразования узла в тип GraficNode, а второй блок – для доступа к потомкам узла.

Пример кода

Предлагается рассмотреть реализацию адаптера уровня класса и адаптера уровня объекта на примере, из раздела «Мотивация»:

В классе Shape создается абстрактный фабричный метод CreateManipulator, с возвращаемым значением типа Manipulator. Manipulator обладает функциональностью по анимированию объектов класса Shape в ответ на действия пользователя. В классе Shape также создается абстрактный метод BoundingBox, который отвечает за отображение элементов типа

Shape на форме (TextShape и Line).

public abstract class Shape

{

public abstract void BoundingBox();

public abstract Manipulator CreateManipulator();

}

Абстрактный класс Manipulator реализует взаимодействие и контролирует состояние объекта-манипулятора. Манипуляторы представлены как наследники базового класса Control.

public abstract class Manipulator : Control

{

//состояние манипулятора на форме.

public Point StartPoint { get; protected set; } public Point EndPoint { get; protected set; }

//задание базовых параметров контрола public Manipulator()

{

SetStyle(ControlStyles.UserPaint, true); SetStyle(ControlStyles.SupportsTransparentBackColor, true); SetStyle(ControlStyles.Opaque, true);

this.BackColor = Color.Transparent; this.DoubleBuffered = false;

}

//определяет параметры необходимые при создании контрола protected override CreateParams CreateParams

{

get

{

const int WS_EX_TRANSPARENT = 0x00000020; CreateParams createParams = base.CreateParams; createParams.ExStyle |= WS_EX_TRANSPARENT; return createParams;

}

}

}

Класс TextView содержит метод GetExtend, который отвечает за отображение объектов на форме, этот метод является аналогом метода BoundingBox, а аналог фабричного метода для создания манипулятора в классе TextView отсутствует. Таким образом для работы с классом TextView в проекте необходимо использовать класс-адаптер TextShape, который адаптирует интерфейс класса TextView к интерфейсу класса Shape.

101

class TextView

{

Label label;

public TextView()

{

label = new Label();

}

public Label GetExtend()

{

label.ForeColor = Color.DarkMagenta;

label.Font = new Font(label.Font, label.Font.Style | FontStyle.Bold); label.BorderStyle = BorderStyle.None;

label.TextAlign = ContentAlignment.TopLeft; label.FlatStyle = FlatStyle.Standard; label.AutoSize = true;

return label;

}

}

class TextShape : Shape

{

Point startPoint; string text; TextView textView; Label label;

public TextShape(Point startPoint, string text)

{

textView = new TextView(); this.startPoint = startPoint; this.text = text;

}

//Преобразование запроса BoundingBox в запрос GetExtend. public override void BoundingBox()

{

label = textView.GetExtend(); label.Text = text;

}

//Фабричный метод, возвращающий манипулятор соответствующий

//конкретной фигуре

public override Manipulator CreateManipulator()

{

return new TextManipulator(startPoint, label);

}

}

Поскольку в классе TextView не предусмотрен метод для создания манипулятора, который будет работать с текстом, то необходимо такой метод реализовать самостоятельно. В классе TextShape реализация метода CreateManipulator не использует повторно никакой функциональности из класса TextView.

class TextManipulator : Manipulator

{

102

Label textInside;

private int deltaX, deltaY; Point currentMouse;

bool mousePressed = false;

//Метод, возвращающий глобальные координаты курсора на

//контроле относительно формы

Point GetMouseLocation(Point curMouse)

{

return new Point(curMouse.X + this.Location.X, curMouse.Y + this.Location.Y);

}

// Перерисовка манипулятора

void ReDrawControl(Point startPoint)

{

Size = new Size(textInside.Text.Length * 8, textInside.Size.Height); Location = startPoint;

RecreateHandle();

Refresh();

}

public TextManipulator(Point startPoint, Label label)

{

textInside = label; StartPoint = startPoint; ReDrawControl(startPoint); Controls.Add(textInside);

textInside.MouseDown += TextInside_MouseDown; textInside.MouseUp += TextInside_MouseUp;

}

// Методы обработчики базового класса Manipulator

void TextInside_MouseDown(object sender, MouseEventArgs e)

{

OnMouseDown(e);

}

void TextInside_MouseUp(object sender, MouseEventArgs e)

{

OnMouseUp(e);

}

// Метод, определяющий новые координаты манипулятора на форме protected override void OnMouseUp(MouseEventArgs e)

{

if (mousePressed)

{

base.OnMouseUp(e);

deltaX = GetMouseLocation(e.Location).X - currentMouse.X; deltaY = GetMouseLocation(e.Location).Y - currentMouse.Y;

StartPoint = new Point(StartPoint.X + deltaX, StartPoint.Y + deltaY); ReDrawControl(StartPoint);

currentMouse = GetMouseLocation(e.Location);

103

}

else

mousePressed = false; Cursor = Cursors.Arrow;

}

// Определение координат текущего манипулятора по клику protected override void OnMouseDown(MouseEventArgs e)

{

base.OnMouseDown(e);

currentMouse = GetMouseLocation(e.Location); mousePressed = true;

Cursor = Cursors.Hand;

}

}

См. Пример к главе: \006_Adapter\002_DrawingEditor

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]