Пример: две версии класса Account
Проиллюстрируем рассмотренные выше вопросы на примере проектирования классов Account и Account1, описывающих такую абстракцию данных, как банковский счет. Определим на этих данных две основные операции - занесение денег на счет и снятие денег. В первом варианте - классе Account - будем активно использовать поля класса. Помимо двух основных полей credit и debit, хранящих приход и расход счета, введем поле balance, которое задает текущее состояние счета, и два поля, связанных с последней выполняемой операцией. Поле sum будет хранить сумму денег текущей
операции, а поле result - результат выполнения операции. Полей у класса много, и как следствие, у методов класса аргументов будет немного. Вот описание нашего класса:
/// <summary>
/// Класс Account определяет банковский счет. Простейший
/// вариант с возможностью трех операций: положить деньги
/// на счет, снять со счета, узнать баланс.Вариант с полями
/// </summary>
public class Account
{
//закрытые поля класса
int debit=0, credit=0, balance =0;
int sum =0, result=0;
/// <summary>
/// Зачисление на счет с проверкой
/// </summary>
/// <param name="sum">зачисляемая сумма</param>
public void putMoney(int sum)
{
this.sum = sum;
if (sum >0)
{
credit += sum; balance = credit - debit; result =1;
}
else result = -1;
Mes();
}//putMoney
/// <summary>
/// Снятие со счета с проверкой
/// </summary>
/// <param name="sum"> снимаемая сумма</param>
public void getMoney(int sum)
{
this.sum = sum;
if(sum <= balance)
{
debit += sum; balance = credit - debit; result =2;
}
else result = -2;
Mes();
}//getMoney
/// <summary>
/// Уведомление о выполнении операции
/// </summary>
void Mes()
{
switch (result)
{
case 1:
Console.WriteLine("Операция зачисления денег
прошла успешно!");
Console.WriteLine("Cумма={0},
Ваш текущий баланс={1}",sum, balance);
break;
case 2:
Console.WriteLine("Операция снятия денег
прошла успешно!");
Console.WriteLine("Cумма={0},
Ваш текущий баланс={1}", sum,balance);
break;
case -1:
Console.WriteLine("Операция зачисления денег
не выполнена!");
Console.WriteLine("Сумма должна быть больше нуля!");
Console.WriteLine("Cумма={0},
Ваш текущий баланс={1}", sum,balance);
break;
case -2:
Console.WriteLine("Операция снятия денег
не выполнена!");
Console.WriteLine("Сумма должна быть
не больше баланса!");
Console.WriteLine("Cумма={0},
Ваш текущий баланс={1}", sum,balance);
break;
default:
Console.WriteLine("Неизвестная операция!");
break;
}
}
}//Account
Как можно видеть, только у методов getMoney и putMoney имеется один входной аргумент. Это тот аргумент, который нужен по сути дела, поскольку только клиент может решить, какую сумму он хочет снять или положить на счет. Других аргументов у методов класса нет - вся информация передается через поля класса. Уменьшение числа аргументов приводит к повышению эффективности работы с методами, так как исчезают затраты на передачу фактических аргументов. Но за все надо платить. В данном случае, усложняются сами операции работы со вкладом, поскольку нужно в момент выполнения операции обновлять значения многих полей класса. Закрытый метод Mes вызывается после выполнения каждой операции, сообщая о том, как прошла операция, и информируя клиента о текущем состоянии его баланса.
А теперь спроектируем аналогичный класс Account1, отличающийся только тем, что у него будет меньше полей. Вместо поля balance в классе появится соответствующая функция с этим же именем, вместо полей sum и result появятся аргументы у методов, обеспечивающие необходимую передачу информации. Вот как выглядит этот класс:
/// <summary>
/// Класс Account1 определяет банковский счет.
/// Вариант с аргументами и функциями
/// </summary>
public class Account1
{
//закрытые поля класса
int debit=0, credit=0;
/// <summary>
/// Зачисление на счет с проверкой
/// </summary>
/// <param name="sum">зачисляемая сумма</param>
public void putMoney(int sum)
{
int res =1;
if (sum >0)credit += sum;
else res = -1;
Mes(res,sum);
}//putMoney
/// <summary>
/// Снятие со счета с проверкой
/// </summary>
/// <param name="sum"> снимаемая сумма</param>
public void getMoney(int sum)
{
int res=2;
if(sum <= balance())debit += sum;
else res = -2;
balance();
Mes(res, sum);
}//getMoney
/// <summary>
/// вычисление баланса
/// </summary>
/// <returns>текущий баланс</returns>
int balance()
{
return(credit - debit);
}
/// <summary>
/// Уведомление о выполнении операции
/// </summary>
void Mes(int result, int sum)
{
switch (result)
{
case 1:
Console.WriteLine("Операция зачисления денег прошла успешно!");
Console.WriteLine("Cумма={0},
Ваш текущий баланс={1}", sum,balance());
break;
case 2:
Console.WriteLine("Операция снятия денег
прошла успешно!");
Console.WriteLine("Cумма={0},
Ваш текущий баланс={1}", sum,balance());
break;
case -1:
Console.WriteLine("Операция зачисления денег
не выполнена!");
Console.WriteLine("Сумма должна быть больше нуля!");
Console.WriteLine("Cумма={0},
Ваш текущий баланс={1}", sum,balance());
break;
case -2:
Console.WriteLine("Операция снятия денег не выполнена!");
Console.WriteLine("Сумма должна быть
не больше баланса!");
Console.WriteLine("Cумма={0},
Ваш текущий баланс={1}", sum,balance());
break;
default:
Console.WriteLine("Неизвестная операция!");
break;
}
}
}//Account1
Сравнивая этот класс с классом Account, можно видеть, что число полей сократилось с пяти до двух, упростились основные методы getMoney и putMoney. Но, в качестве платы, у класса появился дополнительный метод balance(), многократно вызываемый, и у метода Mes теперь появились два аргумента. Какой класс лучше? Однозначно сказать нельзя, все зависит от контекста, от приоритетов, заданных при создании конкретной системы.
Приведу процедуру класса Testing, тестирующую работу с классами Account и Account1:
public void TestAccounts()
{
Account myAccount = new Account();
myAccount.putMoney(6000);
myAccount.getMoney(2500);
myAccount.putMoney(1000);
myAccount.getMoney(4000);
myAccount.getMoney(1000);
//Аналогичная работа с классом Account1
Console.WriteLine("Новый класс и новый счет!");
Account1 myAccount1 = new Account1();
myAccount1.putMoney(6000);
myAccount1.getMoney(2500);
myAccount1.putMoney(1000);
myAccount1.getMoney(4000);
myAccount1.getMoney(1000);
}
На рис. 9.1 показаны результаты работы этой процедуры.
Рис. 9.1. Тестирование классов Account и Account1
- 1. Лекция: Visual Studio .Net, Framework .Net
- Открытость
- Модульность
- Виртуальная машина
- Дизассемблер и ассемблер
- Метаданные
- Сборщик мусора - Garbage Collector - и управление памятью
- Исключительные ситуации
- События
- Общие спецификации и совместимые модули
- 2. Лекция: Язык c# и первые проекты
- Создание c#
- Виды проектов
- Консольный проект
- Windows-проект
- Начало начал - точка "большого взрыва"
- Выполнение проекта по умолчанию после "большого взрыва"
- Проект WindowsHello
- Общий взгляд
- Система типов
- Типы или классы? и типы, и классы
- Семантика присваивания
- Преобразование к типу object
- Примеры преобразований
- Семантика присваивания. Преобразования между ссылочными и значимыми типами
- Операции "упаковать" и "распаковать" (boxing и unboxing).
- 4. Лекция: Преобразования типов
- Где, как и когда выполняются преобразования типов?
- Преобразования ссылочных типов
- Преобразования типов в выражениях
- Преобразования внутри арифметического типа
- Преобразования и класс Convert
- Проверяемые преобразования
- Исключения и охраняемые блоки. Первое знакомство
- Опасные вычисления в охраняемых проверяемых блоках
- Опасные вычисления в охраняемых непроверяемых блоках
- Опасные преобразования и методы класса Convert
- 5. Лекция: Переменные и выражения
- Объявление переменных
- Время жизни и область видимости переменных
- Глобальные переменные уровня модуля. Существуют ли они в c#?
- Int X,y; //координаты точки
- Локальные переменные
- Глобальные переменные уровня процедуры. Существуют ли?
- Константы
- Выражения
- Приоритет и порядок выполнения операций
- Перегрузка операций
- Операции sizeof и typeof
- Как получить подробную информацию о классе?
- Статические поля и методы арифметических классов
- Логические операции
- Условное выражение
- Операция приведения к типу
- Присваивание
- Специальные случаи присваивания
- Определенное присваивание
- Еще раз о семантике присваивания
- Рассмотрим объявления:
- Класс Math и его функции
- Класс Random и его функции
- Блок или составной оператор
- If(выражение_1) оператор_1
- If(выражение1) if(выражение2) if(выражение3) ...
- Оператор switch
- Операторы break и continue
- Циклы While
- Цикл foreach
- Процедуры и функции - функциональные модули
- Процедуры и функции - методы класса
- Процедуры и функции. Отличия
- Описание методов (процедур и функций). Синтаксис
- Список формальных аргументов
- Тело метода
- Вызов метода. Синтаксис
- О соответствии списков формальных и фактических аргументов
- Вызов метода. Семантика
- Поля класса или функции без аргументов?
- Пример: две версии класса Account
- Функции с побочным эффектом
- Методы. Перегрузка
- 10. Лекция: Корректность методов. Рекурсия
- Корректность методов
- Инварианты и варианты цикла
- Рекурсия
- Рекурсивное решение задачи "Ханойские башни"
- Быстрая сортировка Хоара
- 11. Лекция: Массивы языка c#
- Общий взгляд
- Динамические массивы
- Многомерные массивы
- Массивы массивов
- Процедуры и массивы
- Класс Array
- Массивы как коллекции
- Сортировка и поиск. Статические методы класса Array
- Сводка свойств и методов класса Array
- Класс Object и массивы
- Массивы объектов
- Массивы. Семантика присваивания
- Общий взгляд
- Класс char[] - массив символов
- Операции над строками
- Строковые константы
- Неизменяемый класс string
- Статические свойства и методы класса String
- Метод Format
- Методы Join и Split
- Динамические методы класса String
- Операции над строками
- Основные методы
- Емкость буфера
- Пространство имен RegularExpression и классы регулярных выражений
- Немного теории
- Синтаксис регулярных выражений
- Классы Match и MatchCollection
- Классы Group и GroupCollection
- Пример "чет и нечет"
- Пример "око и рококо"
- Пример "кок и кук"
- Пример "обратные ссылки"
- Пример "Дом Джека"
- Пример "Атрибуты"
- 16. Лекция: Классы
- Синтаксис класса
- Поля класса
- Доступ к полям
- Методы-свойства
- Индексаторы
- Константы
- Конструкторы класса
- Деструкторы класса
- Проектирование класса Rational
- Методы класса Rational
- Закрытый метод нод
- Операции над рациональными числами
- Константы класса Rational
- Развернутые и ссылочные типы
- Классы и структуры
- Класс Rational или структура Rational
- Встроенные структуры
- Еще раз о двух семантиках присваивания
- Перечисления
- Персоны и профессии
- 18. Лекция: Отношения между классами. Клиенты и наследники
- Отношения между классами
- Отношения "является" и "имеет"
- Отношение вложенности
- Расширение определения клиента класса
- Отношения между клиентами и поставщиками
- Сам себе клиент
- Наследование
- Добавление полей потомком
- Конструкторы родителей и потомков
- Добавление методов и изменение методов родителя
- Статический контроль типов и динамическое связывание
- Три механизма, обеспечивающие полиморфизм
- Пример работы с полиморфным семейством классов
- Абстрактные классы
- Классы без потомков
- Преобразование к классу интерфейса
- Наследование от общего предка
- Клонирование и интерфейс iCloneable
- Сериализация объектов
- Класс с атрибутом сериализации
- Интерфейс iSerializable
- 20. Лекция: Функциональный тип в c#. Делегаты
- Как определяется функциональный тип и как появляются его экземпляры
- Функции высших порядков
- Вычисление интеграла
- Построение программных систем методом "раскрутки". Функции обратного вызова
- Наследование и полиморфизм - альтернатива обратному вызову
- Делегаты как свойства
- Операции над делегатами. Класс Delegate
- Пример "Плохая служба"
- 21. Лекция: События
- Классы с событиями
- Класс sender. Как объявляются события?
- Делегаты и события
- Как зажигаются события
- Классы receiver. Как обрабатываются события
- Классы с событиями, допустимые в каркасе .Net Framework
- Пример "Списки с событиями"
- Класс sender
- Классы receiver
- Переопределение значений аргументов события
- Классы с большим числом событий
- Проект "Город и его службы"
- 22. Лекция: Универсальность. Классы с родовыми параметрами
- Наследование и универсальность
- Синтаксис универсального класса
- Класс с универсальными методами
- Два основных механизма объектной технологии
- Стек. От абстрактного, универсального класса к конкретным версиям
- Ограниченная универсальность
- Синтаксис ограничений
- Список с возможностью поиска элементов по ключу
- Как справиться с арифметикой
- Родовое порождение класса. Предложение using
- Универсальные делегаты
- Framework .Net и универсальность
- 23. Лекция: Отладка и обработка исключительных ситуаций
- Корректность и устойчивость программных систем
- Жизненный цикл программной системы
- Искусство отладки
- Отладочная печать и условная компиляция
- Классы Debug и Trace
- Метод Флойда и утверждения Assert
- Выбрасывание исключений. Создание объектов Exception
- If !MyMethod(){// обработка ошибки}
- Параллельная работа обработчиков исключений
- Блок finally
- Класс Exception
- Организация интерфейса
- Форма и элементы управления
- Взаимодействие форм
- Модальные и немодальные формы
- Передача информации между формами
- Шаблон формы для работы с классом
- Наследование форм
- Два наследника формы TwoLists
- Огранизация меню в формах
- Создание меню в режиме проектирования
- Классы меню
- Создание инструментальной панели с командными кнопками
- Методы класса Graphics
- Класс Pen
- Класс Brush
- Событие Paint
- Кисти и краски
- 25. Лекция: Финальный проект
- Абстрактный класс Figure
- Класс Circle
- Список с курсором. Динамические структуры данных
- Классы элементов списка
- Организация интерфейса