Смекни!
smekni.com

Разработка корпоративной информационной системы на основе объектно-ориентированного подхода (стр. 10 из 11)

Если операции имеют одинаковую семантику, но разное число формальных параметров, можно добавить отсутствующие параметры, но игнорировать их при выполнении операции; например, операция обрисовки изображения на монохромный монитор не требует параметра цвет, но его можно добавить и не принимать во внимание при выполнении операции.

Некоторые операции имеют меньше параметров потому, что они являются частными случаями более общих операций; такие операции можно не реализовывать, сведя их к более общим операциям с соответствующими значениями параметров; например, добавление элемента в конец списка есть частный случай вставки элемента в список.

Одинаковые по смыслу атрибуты или операции разных классов могут иметь разные имена; такие атрибуты (операции) можно переименовать и перенести в класс, являющийся общим предком рассматриваемых классов.

Операция может быть определена не во всех классах некоторой группы классов; можно, тем не менее, вынести ее в их общий суперкласс, переопределив ее в подклассах, как пустую там, где она не нужна.

Использование делегирования операций можно пояснить на следующем примере (рисунок 2.9). Класс стек близок классу список, причем операциям стека push и pop соответствуют очевидные частные случаи операций списка add и remove. Если реализовать класс стек как подкласс класса список, то придется применять вместо операций push и pop более общие операции add и remove, следя за их параметрами, чтобы избежать записи или чтения из середины стека; это неудобно и чревато ошибками. Гораздо лучше объявить класс список телом класса стек (делегирование), обращаясь к операциям списка через операции стека. При этом, не меняя класса список, мы заменяем его интерфейс интерфейсом класса стек.

Рисунок 2.9 - Реализация стека с использованием наследования (а) и делегирования (б)

2.2.6. Разработка зависимостей

Зависимости - это "клей" объектной модели: именно они позволяют рассматривать модель как нечто целое, а не просто как множество классов.

Односторонние зависимости можно реализовать с помощью ссылок (указателей) (см. рисунок 2.10). При этом, если кратность зависимости равна единице, ей соответствует один указатель, если кратность больше единицы, то множество указателей.

Рисунок 2.10 - Реализация односторонней зависимости

На рисунке 2.11 показан способ реализации двусторонней зависимости с помощью указателей.

Рисунок 2.11 - Реализация двусторонней зависимости

На рисунке 2.12 показан способ реализации зависимости с помощью таблицы (как в реляционных базах данных).

Рис. 2.12. Реализация зависимости с помощью таблицы

При реализации зависимостей с помощью указателей атрибуты зависимостей (связей) переносятся в один из классов, участвующих в зависимости.

3. Реализация объектно-ориентированного проекта

Каждый язык программирования имеет средства для выражения трех сторон спецификации разрабатываемой прикладной системы: структур данных, потоков управления и функциональных преобразований.

Все три модели методологии OMT, разработанные на этапе анализа требований к системе и уточненные на этапе ее проектирования, используются на этапе реализации программного обеспечения системы. Объектная модель определяет классы, атрибуты, иерархию наследования, зависимости. Динамическая модель определяет стратегию управления, которая будет принята в системе (процедурно-управляемая, событийно-управляемая, или многозадачная). Функциональная модель содержит функциональность объектов, которая должна быть воплощена в их методах.

В объектно-ориентированной программе действия выполняются путем создания объектов и вызова методов объектов. Однако какое-то действие должно начать этот процесс. В языке Си++ программа начинает выполняться со специальной функции main. Эта функция может вызвать другие функции и создать какие-либо объекты, которым она может передать сообщения.

При определении поведения программы важную роль играет тот факт, что язык Си++ поддерживает не только объектно-ориентированный, но и процедурный стиль программирования. Действия, в том числе методы классов, - это функции или процедуры.

Данный фрагмент программы реализует два вида счетов - расчетный счет (класс CheckingAccount) и депозитный счет (класс DepositAccount) - с помощью наследования из базового класса Account.

Класс Account - это абстрактный класс с чисто виртуальным методом GetAccountTypc. Кроме того, он задает общие для всех порожденных из него классов методы- Deposit, Withdraw и Balance. Вызывая эти методы, программы, работающие с базовым классом, будут использовать ограниченный полиморфизм.

Атрибут amount - остаток на счету - помещен в защищенную часть класса для того, чтобы порожденные классы могли с ним работать. Атрибут accountNumber - номер счета - помещен во внутреннюю часть класса, поскольку базовый класс полностью контролирует создание уникальных номеров счетов.

//

// Класс Account - базовый класс для всех типов счетов

//

class Account

{

// внешняя часть класса, его интерфейс

public:

// перечисление всех возможных типов счетов

enum AccountType ( CHECKING, DEPOSIT );

Account() ( amount = 0; ); // Создание пустого счета

Account(long _accountNumber); // Создание объекта для

// имеющегося счета

virtual ~Account() ;

// Хотя класс Account абстрактный, он проводит

// стандартные реализации методов "положить на счет"

// (Deposit), "снять со счета" (Withdraw) и

// "остаток на счете" (Balance), хотя они могут быть

// переопределены в порожденных классах

virtual void Deposit(float _amount); //Положить на счет

virtual bool Withdraw(float _ainount); //Снять со счета

virtual float Balance(void) const; //Остаток на счете

// Метод GetAccountType возвращает тип конкретного счета

// и определен только в порожденных классах

virtual AccountType GetAccountType(void) consfc = 0;

// Узнать номер счета

long GetAccountNumber(void) const

{ return accountNumber; } ;

// Защищенная часть класса доступна порожденным классам

protected:

float amount; // Атрибут - сумма на счете

// Внутренняя часть класса

private:

long accountNumber; // Атрибут - номер счета

};

Класс DatabaseObject - абстрактный класс, который задает интерфейс для сохранения и восстановления объектов в базе данных. Порожденные из него классы обязаны определить два метода: Save - для сохранения самого себя в базе данных, и Restore - для восстановления состояния из базы данных. В данном классе показано, что в абстрактном классе могут задаваться конкретные методы (MarkModifled, IsModifled, GetDatabaseConnection). Если первые два метода относятся к интерфейсу класса, то третий - это защищенный метод, необходимый для реализации методов Save и Restore в порожденных классах. (Мы исходим из предположения, что операции с базой данных требуют установления соединения с базой данных через объект типа DBConnection, который и возвращает метод GetDatabaseConnection.)

//

// Базовый класс для всех'объектов,

// которые могут храниться в базе данных

//

class DatabaseObject

{

public:

DatabaseObject () :niodifiedFlag( false) {}

virtual ~DatabaseObject() ;

virtual boot Save(void) = 0;

virtual boot Restore(void) = 0;

// Два дополнительных метода устанавливают или проверяют

// признак того, что объект был модифицирован и должен

// быть сохранен в базе данных

void MarkModified(void) ;

boot IsModified(void) const;

protected:

DBConnection* GetDatabaseConnection(void);

private:

boot modifiedFlag;

};

Два следующих класса: CheckingAccount и DepositAccount - это конкретные классы, реализующие два типа счетов - расчетный счет и депозит. Они используют множественное наследование. В качестве базовых классов выступают классы Account и DatabaseObject. Класс Account задает интерфейс и стандартную реализацию свойств счета, а класс DatabaseObject - необходимый интерфейс для хранения объектов в базе данных.

//

// Класс Расчетный счет - конкретный счет, который

// может храниться в базе данных

//

class CheckingAccount : public Account, DatabaseObject

{

public:

CheckingAccount() {} ;

CheckingAccount(long _accountNumber) •;

Account (_accountNuinber) {} ;

// При конструировании нового объекта данного

// класса мы вначале конструируем его

// базовую часть

virtual ~CheckingAccount ();

// Класс переопределяет метод снятия со счета, потому

// что имеется требование минимального остатка на счете,

// оставляя неизменными методы "положить на счет"

//и "проверить остаток"

virtual boot Withdraw(float _amount);

// Метод GetAccountType определен и возвращает тип

// счета - расчетный счет

virtual AccountType GetAccountType(void) const

( return CHECKING;) ;

// Поскольку объекты данного класса можно хранить

//в базе данных - класс выведен из DatabaseObject,

//в нем определяются методы Save и Restore для

// сохранения и восстановления из базы данных

virtual boot Save(void) ;

virtual boot Restore(void) ;

private:

// Класс определяет один дополнительный атрибут

//минимальный остаток на счете

float minBalance;

};

//

// Класс счет-депозит, аналогичный предыдущему

// расчетному счету

//

class DepositAccount : public Account, DatabaseObject

{

DepositAccount() {}; ,

DepositAccount (long _accountNurnber) :

Account (_accountNuinber) {};

virtual ~DespositAccount() ;

// Класс переопределяет метод снятия со счета и метод

// "положить на счет", потому что имеется ограничение,

//в какие сроки можно их производить,

// метод "проверить остаток" также переопределен,

// поскольку на сумму начисляется процент

virtual void Deposit(float _amount);

virtual boot Withdraw(float amount);

virtual float Balance(void) const;

// Метод GetAccountType определен и возвращает тип

// счета - депозит

virtual AccountType GetAccountType(void) const

( return DEPOSIT;);

// Поскольку объекты данного класса можно хранить

//в базе данных - класс выведен из DatabaseObject,

// в нем определяются методы Save и Restore для

// сохранения и восстановления из базы данных