Лабораторная работа №2
Цель:
1. Получить представление о создании классов и наследовании
2. Получить представление о шаблоне проектирования «Наблюдатель».
Содержание:
Для начала немного о шаблоне проектирования «Наблюдатель». Для начала мы будем рассматривать шаблоны проектирования, как некие приемы, позволяющие наиболее эффективно разрешить необходимую проблему реализации. Сегодня мы рассмотрим простейшую проблему синхронизации данных в разных представлениях одних и тех же данных. Представьте, что у вас есть три числовых показателя, каждый из которых нужно отобразить в трех видах – числом, горизонтальным и вертикальным ползунками. Причем при попытке изменить число или положение на ползунках, остальные представления, соответствующие данному показателю также должны измениться. Для реализации решения данной проблемы воспользуемся шаблоном проектирования «Наблюдатель». Для этих целей, создадим по классу для каждого представления данных, наследуемых от общего абстрактного класса. А также создадим класс наблюдателя, который будет регистрировать объекты классов, отвечающих за представления данных и синхронизировать значения данных в представлениях.
Создаем проект Windows Forms Application. В дизайнере создаем вот такой вот диалог:

Диалог содержит три контейнера типа Group Box. В первом контейнере находятся три объекта класса NumericUpDown, во втором контейнере три объекта класса TrackBar и в третьем три объекта класса VScrollBar. Компонент NumericUpDown, также называемый иногда SpinBox, предназначен для ввода числовых данных, компонент TrackBar используется в основном для установки настроек (громкости например или эквалайзера), а VScrollBar – это вертикальный скролл, его использование мы можем увидеть в любом приложении. Для каждого объекта класса NumericUpDown, TrackBar и VScrollBar выставим свойство Maximum на 35.
Затем создаем новый класс, который назовем AbstractDependent. Его код должен выглядеть следующим образом:
using System;
using System. Collections. Generic;
using System. Collections;
using System. Linq;
using System. Text;
using System. Windows. Forms;
namespace ObserverExample
{
class AbstractDependent
{
public AbstractDependent()
{
componentList = new ArrayList();
}
public virtual void SetValue(int index, int value)
{
}
protected ArrayList componentList;
}
}
Обратите внимание на подключенное пространство имен коллекций, а также на клчевое слово virtual, которое означает что функция отмеченная этим словом может быть перегружена в наследуемом классе.
И в соответствие с каждым типом объектов, используемых в нашем примере, мы создадим три класса, которые будут наследоваться от класса AbstractDependent и перегружать функцию SetValue.
Первый будет содержать объекты класса NumericUpDown:
using System;
using System. Collections. Generic;
using System. Collections;
using System. Linq;
using System. Text;
using System. Windows. Forms;
namespace ObserverExample
{
class NumericUpDownDependent : AbstractDependent
{
public NumericUpDownDependent(params NumericUpDown[] objects) : base()
{
foreach (NumericUpDown component in objects)
{
componentList. Add(component);
}
}
public override void SetValue(int index, int value)
{
if (index >= 0 && index < componentList. Count)
{
((NumericUpDown) componentList[index]).Value = value;
}
}
}
}
Второй объекты класса TrackBar:
using System;
using System. Collections. Generic;
using System. Collections;
using System. Linq;
using System. Text;
using System. Windows. Forms;
namespace ObserverExample
{
class TrackBarDependent : AbstractDependent
{
public TrackBarDependent(params TrackBar[] objects)
: base()
{
foreach (TrackBar component in objects)
{
componentList. Add(component);
}
}
public override void SetValue(int index, int value)
{
if (index >= 0 && index < componentList. Count)
{
((TrackBar)componentList[index]).Value = value;
}
}
}
}
И третий объекты класса VScrollBar:
using System;
using System. Collections. Generic;
using System. Collections;
using System. Linq;
using System. Text;
using System. Windows. Forms;
namespace ObserverExample
{
class VScrollBarDependent : AbstractDependent
{
public VScrollBarDependent(params VScrollBar[] objects)
: base()
{
foreach (VScrollBar component in objects)
{
componentList. Add(component);
}
}
public override void SetValue(int index, int value)
{
if (index >= 0 && index < componentList. Count)
{
((VScrollBar)componentList[index]).Value = value;
}
}
}
}
Все три класса наследуются от класса AbstractDependent. Наследование обозначается оператором «:» при объявлении класса. Обратим внимание также на конструкторы всех трех классов. В каждый из конструкторов передается массив соответствующих объектов и в каждом конструкторе присутствует модификатор params. Этот модификатор позволяет создавать методы, которым можно направить множество однотипных аргументов в виде одного параметра. Мы увидим как это происходит при создании объектов этих классов. Ключевое слово base в C# позволяет работать с родительским классом. В нашем случае мы вызываем конструктор базового класса, перед инициализацией наследуемого.
Теперь нам необходимо создать непосредственно класс Наблюдателя:
using System;
using System. Collections. Generic;
using System. Collections;
using System. Linq;
using System. Text;
namespace ObserverExample
{
class Observer
{
public Observer() {
dependentsList = new ArrayList();
}
public void Registrate(AbstractDependent obj)
{
dependentsList. Add(obj);
}
public void UpdateValues(int index, int value)
{
foreach (AbstractDependent obj in dependentsList)
{
obj. SetValue(index, value);
}
}
protected ArrayList dependentsList;
}
}
Теперь проведем инициализацию всех компонентов в файле Form1.cs:
Observer observer;
public Form1()
{
InitializeComponent();
NumericUpDownDependent numDependent = new NumericUpDownDependent(numericUpDown1, numericUpDown2, numericUpDown3);
TrackBarDependent tbDependent = new TrackBarDependent(trackBar1, trackBar2, trackBar3);
VScrollBarDependent vsbDependent = new VScrollBarDependent(vScrollBar1, vScrollBar2, vScrollBar3);
observer = new Observer();
observer. Registrate(numDependent);
observer. Registrate(tbDependent);
observer. Registrate(vsbDependent);
observer. UpdateValues(0, 15);
observer. UpdateValues(1, 20);
observer. UpdateValues(2, 5);
}
Обратите внимание на то, как передаются объекты в конструкторы наших зависимых классов – это и есть работа модификатора params. После инициализации компонентов, создаются объекты наших зависимых классов, создается объект класса наблюдателя. Зависимые классы регистрируются. И уже с помощьюю объекта класса наблюдателя инициализируются первые значения.
Далее перейдем к нашему дизайну и создадим обработчики события изменения значения компонента для каждого нашего элемента. Для этого достаточно дважды щелкнуть по нашему компоненту и в файле Form1.cs будут создаваться функции обработчиков события, которые мы отредактируем следующим образом:
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
observer. UpdateValues(0, (int) numericUpDown1.Value);
}
private void numericUpDown2_ValueChanged(object sender, EventArgs e)
{
observer. UpdateValues(1, (int) numericUpDown2.Value);
}
private void numericUpDown3_ValueChanged(object sender, EventArgs e)
{
observer. UpdateValues(2, (int) numericUpDown3.Value);
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
observer. UpdateValues(0, trackBar1.Value);
}
private void trackBar2_Scroll(object sender, EventArgs e)
{
observer. UpdateValues(1, trackBar2.Value);
}
private void trackBar3_Scroll(object sender, EventArgs e)
{
observer. UpdateValues(2, trackBar3.Value);
}
private void vScrollBar1_Scroll(object sender, ScrollEventArgs e)
{
observer. UpdateValues(0, vScrollBar1.Value);
}
private void vScrollBar2_Scroll(object sender, ScrollEventArgs e)
{
observer. UpdateValues(1, vScrollBar2.Value);
}
private void vScrollBar3_Scroll(object sender, ScrollEventArgs e)
{
observer. UpdateValues(2, vScrollBar3.Value);
}
Теперь если все сделано правильно, запустим нашу программу и посмотрим на динамическое изменение всех трех компонентов при изменении любого из них.


