Интернет-журнал "Домашняя лаборатория", 2007 №9
Шрифт:
Теперь нам нужно ввести изменения в ранее созданный класс OneLinkList. Обратите внимание на важный технологический принцип работы с объектными системами. Пусть уже есть нормально работающий класс с нормально работающими клиентами класса. Не следует изменять этот класс. Класс закрыт для изменений. Используйте наследование и открывайте класс-потомок, в который и вносите изменения, учитывающие добавляемую специфику класса. Принцип "Закрыт — Открыт" является одним из важнейших принципов построения программных систем
В полном соответствии с этим принципом построим класс SumList — потомок класса OneLinkList. То, что родительский класс является универсальным, ничуть не мешает строить потомка класса, сохраняющего универсальный характер родителя.
public class SumList<K, Т>: OneLinkList<K, Т> where К:
IComparable<K>
{
Calc<T> calc;
T s um;
public SumList(Calc<T> calc)
{ this.calc = calc; sum = default(T); }
public new void add(K key, T item)
{
Node<K, T> newnode = new Node<K, T>;
if (first == null)
{
first = newnode; cursor = newnode;
newnode.key = key; newnode.item = item;
sum = calc.Add(sum, item);
}
else
{
newnode.next = cursor.next; cursor.next = newnode;
newnode.key = key; newnode.item = item;
sum = calc.Add(sum, item);
}
}
public T Sum
{return (sum); }
}//SumList
У класса добавилось поле sum, задающее сумму хранимых элементов, и поле calc — калькулятор, выполняющий вычисления. Метод add, объявленный в классе с модификатором new, скрывает родительский метод add, задавая собственную реализацию этого метода. Родительский метод можно было бы определить как виртуальный, переопределив его у потомка, но я не стал трогать код родительского класса. К классу добавился еще один запрос, возвращающий значение поля sum.
Некоторые изменения в уже существующем проекте пришлось-таки сделать, изменив статус доступа у полей. А все потому, что в целях экономии текста кода я не стал закрывать поля и вводить, как положено, открытые процедуры-свойства для закрытых полей.
Проведем теперь эксперименты с новыми вариантами списков, допускающих суммирование элементов:
public void TestSum
{
SumList<string, int> list1 =
new SumList<string, int>(new IntCalc);
list1.add("Петр", 33); list1.add("Павел", 44);
Console.WriteLine("sum= {0}", list1.Sum);
SumList<string, double> list2 =
new SumList<string, double> (new DoubleCalc);
list2.add("Петр", 33.33); list2.add("Павел", 44.44);
Console.WriteLine("sum= {0}", list2.Sum);
SumList<string, string> list3 =
new SumList<string, string> (new StringCalc);
list3.add("Мама", "
"Машу мылом!");
Console.WriteLine("sum= {0}", list3.Sum );
}
Обратите внимание на создание списков:
SumList<string, int> list1 =
new SumList<string, int>(new IntCalc);
SumList<string, double> list2 =
new SumList<string, double>(new DoubleCalc);
SumList<string, string> list3 =
new SumList<string, string>(new StringCalc);
Как видите, конструктору объекта передается калькулятор, согласованный с типами данных, которые хранятся в списке. Результаты вычислений, полученных при работе с этими списками, приведены на рис. 22.6.
Рис. 22.6. Списки с суммированием
Родовое порождение класса. Предложение using
До сих пор рассматривалась ситуация родового порождения экземпляров универсального класса. Фактические типы задавались в момент создания экземпляра. Это наглядно показывает преимущества применяемой технологии, поскольку очевидно, что не создается дублирующий код для каждого класса, порожденного универсальным классом. И все-таки остается естественный вопрос: можно ли породить класс из универсального класса путем подстановки фактических параметров, а потом спокойно использовать этот класс обычным образом? Такая вещь возможна. Это можно сделать не совсем обычным путем — не в программном коде, а в предложении using, назначение которого и состоит в выполнении подобных подстановок.
Давайте вернемся к универсальному классу OneLinkstack<T>, введенному в начале этой лекции, и породим на его основе вполне конкретный класс IntStack, заменив формальный параметр T фактическим — int. Для этого достаточно задать следующее предложение using;
using IntStack = Generic.OneLinkStack<int>;
Вот тест, в котором создаются несколько объектов этого класса:
public void TestlntStack
{
IntStack stack1 = new IntStack ;
IntStack stack2 = new IntStack ;
IntStack stack3 = new IntStack ;
stack2.put (11); stackl.put (22);
int x1 = stack1.item, x2 = stack1.item;
if ((x1 == x2) && (xl == 22)) Console.WriteLine("OK!");
stack1.remove; x2 = stack1.item;
if ((x1!= x2) && (x2 == 11)) Console.WriteLine("OK!");
stack1.remove; x2 = (stack1.empty)? 77:
stack1.item;
if ((x1!= x2) && (x2 == 77)) Console.WriteLine("OK!");