Создание классов. Часть 2

Последнее обновление: 23.01.2024

Как правило, банковские счета существуют не сами по себе, а внутри банка, который выступает некоторым контейнером счетов и выполняет функции по управлению ими. Поэтому добавим в проект BankLibrary новый класс Bank:

using BankLibrary;

// тип счета
public enum AccountType
{
    Ordinary,
    Deposit
}
public class Bank<T> where T : Account
{
    T[] accounts = { };

    public string Name { get; }

    public Bank(string name)
    {
        Name = name;
    }
    // метод создания счета
    public void Open(AccountType accountType, decimal sum,
        AccountStateHandler addSumHandler, AccountStateHandler withdrawSumHandler,
        AccountStateHandler calculationHandler, AccountStateHandler closeAccountHandler,
        AccountStateHandler openAccountHandler)
    {
        T? newAccount = null;

        switch (accountType)
        {
            case AccountType.Ordinary:
                newAccount = new DemandAccount(sum, 1) as T;
                break;
            case AccountType.Deposit:
                newAccount = new DepositAccount(sum, 40) as T;
                break;
        }

        if (newAccount == null)
            throw new Exception("Ошибка создания счета");
        // добавляем новый счет в массив счетов      
        T[] tempAccounts = new T[accounts.Length + 1];
        for (int i = 0; i < accounts.Length; i++)
            tempAccounts[i] = accounts[i];
        tempAccounts[tempAccounts.Length - 1] = newAccount;
        accounts = tempAccounts;

        // установка обработчиков событий счета
        newAccount.Added += addSumHandler;
        newAccount.Withdrawed += withdrawSumHandler;
        newAccount.Closed += closeAccountHandler;
        newAccount.Opened += openAccountHandler;
        newAccount.Calculated += calculationHandler;

        newAccount.Open();
    }
    //добавление средств на счет
    public void Put(decimal sum, int id)
    {
        T? account = FindAccount(id);
        if (account == null)
            throw new Exception("Счет не найден");
        account.Put(sum);
    }
    // вывод средств
    public void Withdraw(decimal sum, int id)
    {
        T? account = FindAccount(id);
        if (account == null)
            throw new Exception("Счет не найден");
        account.Withdraw(sum);
    }
    // закрытие счета
    public void Close(int id)
    {
        int index;
        T? account = FindAccount(id, out index);
        if (account == null)
            throw new Exception("Счет не найден");

        account.Close();

        if ( accounts.Length <= 1)
            accounts = new T[]{ };
        else
        {
            // уменьшаем массив счетов, удаляя из него закрытый счет
            T[] tempAccounts = new T[accounts.Length - 1];
            for (int i = 0, j = 0; i < accounts.Length; i++)
            {
                if (i != index)
                    tempAccounts[j++] = accounts[i];
            }
            accounts = tempAccounts;
        }
    }

    // начисление процентов по счетам
    public void CalculatePercentage()
    {
        for (int i = 0; i < accounts.Length; i++)
        {
            accounts[i].IncrementDays();
            accounts[i].Calculate();
        }
    }

    // поиск счета по id
    public T? FindAccount(int id)
    {
        for (int i = 0; i < accounts.Length; i++)
        {
            if (accounts[i].Id == id)
                return accounts[i];
        }
        return null;
    }
    // перегруженная версия поиска счета
    public T? FindAccount(int id, out int index)
    {
        index = -1;
        for (int i = 0; i < accounts.Length; i++)
        {
            if (accounts[i].Id == id)
            {
                index = i;
                return accounts[i];
            }
        }
        return null;
    }
}

Класс банка является обобщенным. При этом параметр T имеет ограничение: он обязательно должен представлять класс Account или его наследников. Поэтому у любого объекта T нам будут доступны методы и свойства класса Account.

Все счета в классе хранятся в массиве accounts. На момент проектирования класса мы можем не знать, какими именно счетами будет управлять банк. Возможно, это будут любые счета, а может быть только депозитные, то есть объекты DepositAccount. Поэтому использование обобщений позволяет добавить больше гибкости.

При создании нового счета в методе Open в этот метод передается ряд параметров, в частности, тип счета, который описывается перечислением:

public enum AccountType
{
    Ordinary,
    Deposit
}

Данное перечисление можно определить после класса Bank. В зависимости от типа счета создается объект DemandAccount или DepositAccount и затем добавляется в массив accounts. Поскольку массивы не расширяются автоматически, то фактически мы создаем новый массив с увеличением элементов на единицу и в конец нового массива добавляем новый элемент.

При этом параметризация, то есть создание обобщенных классов имеет ограничение в том, что созданный объект еще необходимо привести к типу T:

newAccount = new DemandAccount(sum, 1) as T;

Такое приведение позволит нам избежать ошибок, например, если мы типизируем класс Bank не Account, а типом DepositAccount, то преобразование newAccount = new DemandAccount(sum, 1) as T вернет нам значение null. Дальше мы можем проверить полученное значение на null:

if (newAccount == null)

Также в метод Open передаются обработчики для всех событий класса Account, которые устанавливаются после создания объекта Account.

В конце вызывается у нового объекта Account метод OnOpened(), который генерирует событие Account.Opened, благодаря чему извне можно получить уведомление о событии.

Для поиска счета в массиве по id определяется метод FindAccount(). Его перегруженная версия позволяет также получать индекс найденного элемента через выходной параметр index:

 public T? FindAccount(int id, out int index)

В методах Put, Withdraw и Close используются метод FindAccount() для получения счета для добавления или вывода средств, а также закрытия. При закрытии счета в методе Close() создается новый массив без одного элемента - счета, который надо удалить. Таким образом, происходит удаление счета.

В методе CalculatePercentage() пробегаемся по всем элементам массива счетов, увеличиваем у каждого счета счетчик дней и производим начисление процентов.

В общем класс Bank является оберткой, через которую из главного проекта мы будем взаимодействовать с объектами Account.

В тоге проект BankLibrary должен выглядеть следующим образом:

Проект библиотеки классов в C#

И теперь построим проект. Для этого нажмем на название проекта в окне Solution Explorer (Обозреватель решений) правой кнопкой мыши и в появившемся контекстном меню выберем пункт Build. После этого в проекте в папке bin/Debug будет создан файл библиотеки классов с расширением dll.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850