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

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

Добавим в проект BankLibrary новый класс Account, который будет иметь следующее определение:

namespace BankLibrary
{
    public abstract class Account : IAccount
    {
        //Событие, возникающее при выводе денег
        protected internal event AccountStateHandler? Withdrawed;
        // Событие возникающее при добавление на счет
        protected internal event AccountStateHandler? Added;
        // Событие возникающее при открытии счета
        protected internal event AccountStateHandler? Opened;
        // Событие возникающее при закрытии счета
        protected internal event AccountStateHandler? Closed;
        // Событие возникающее при начислении процентов
        protected internal event AccountStateHandler? Calculated;

        static int counter = 0;
        protected int _days = 0; // время с момента открытия счета

        public Account(decimal sum, int percentage)
        {
            Sum = sum;
            Percentage = percentage;
            Id = ++counter;
        }

        // Текущая сумма на счету
        public decimal Sum { get; private set; }
        // Процент начислений
        public int Percentage { get; }
        // Уникальный идентификатор счета
        public int Id { get; }
        // вызов событий
        private void CallEvent(AccountEventArgs e, AccountStateHandler? handler)
        {
            if (e != null)
                handler?.Invoke(this, e);
        }
        // вызов отдельных событий. Для каждого события определяется свой витуальный метод
        protected virtual void OnOpened(AccountEventArgs e)
        {
            CallEvent(e, Opened);
        }
        protected virtual void OnWithdrawed(AccountEventArgs e)
        {
            CallEvent(e, Withdrawed);
        }
        protected virtual void OnAdded(AccountEventArgs e)
        {
            CallEvent(e, Added);
        }
        protected virtual void OnClosed(AccountEventArgs e)
        {
            CallEvent(e, Closed);
        }
        protected virtual void OnCalculated(AccountEventArgs e)
        {
            CallEvent(e, Calculated);
        }

        public virtual void Put(decimal sum)
        {
            Sum += sum;
            OnAdded(new AccountEventArgs("На счет поступило " + sum, sum));
        }
        // метод снятия со счета, возвращает сколько снято со счета
        public virtual decimal Withdraw(decimal sum)
        {
            decimal result = 0;
            if (Sum >= sum)
            {
                Sum -= sum;
                result = sum;
                OnWithdrawed(new AccountEventArgs($"Сумма {sum} снята со счета {Id}", sum));
            }
            else
            {
                OnWithdrawed(new AccountEventArgs($"Недостаточно денег на счете {Id}", 0));
            }
            return result;
        }
        // открытие счета
        protected internal virtual void Open()
        {
            OnOpened(new AccountEventArgs($"Открыт новый счет! Id счета: {Id}", Sum));
        }
        // закрытие счета
        protected internal virtual void Close()
        {
            OnClosed(new AccountEventArgs($"Счет {Id} закрыт.  Итоговая сумма: {Sum}", Sum));
        }

        protected internal void IncrementDays()
        {
            _days++;
        }
        // начисление процентов
        protected internal virtual void Calculate()
        {
            decimal increment = Sum * Percentage / 100;
            Sum = Sum + increment;
            OnCalculated(new AccountEventArgs($"Начислены проценты в размере: {increment}", increment));
        }
    }
}

Поскольку как такого банковского счета нет, а есть конкретные счета - депозит, до востребования и т.д., то данный класс является абстрактным. В то же время он реализует интерфейс IAccount.

Он определяет ряд событий, которые вызываются при изменении состояния. Для определения событий применяется ранее созданный делегат AccountStateHandler.

Каждый счет имеет уникальный идентификатор Id - некий уникальный номер. Для его получения применяется статический счетчик counter.

Почти все методы являются виртуальными. Прежде всего определяются методы генерации. Метод CallEvent() вызывает событие, которое представляет делегат AccountStateHandler и передает ему аргументы события - объект AccountEventArgs. Для вызова отдельных событий с помощью метода CallEvent предусмотрены методы OnAdded, OnCalculated, OnClosed, OnOpened, OnWithdrawed. Подобная организация вызова событий позволит проще переопределить модель вызова в классах-наследниках и передать различные для каждого класса-наследника объекты AccountEventArgs.

Обращение к этим методам идет внутри методов, которые отвечают за функциональность счета. Например, в методе Put хранится логика по зачислению средств на счет. И в этом методе через вызов OnAdded() будет генерироваться событие Added.

Также надо отметить, что большинство членов класса имеют модификатор protected internal, то есть они будут видны только внутри проекта BankLibrary.

Абстрактный класс Account определяет полиморфный интерфейс, который наследуется или переопределяется производными классами. Теперь добавим в проект новый класс, который будет представлять счет до востребования и который будет называться DemandAccount:

public class DemandAccount : Account
{
    public DemandAccount(decimal sum, int percentage) : base(sum, percentage)
    {
    }

    protected internal override void Open()
    {
        base.OnOpened(new AccountEventArgs($"Открыт новый счет до востребования! Id счета: {this.Id}", this.Sum));
    }
}

В данном классе переопределен один метод Open. При желании можно переопределить и больше функционала, но ограничимся в данном случае этим.

И также добавим второй класс-наследник DepositAccount:

public class DepositAccount : Account
{
    public DepositAccount(decimal sum, int percentage) : base(sum, percentage)
    {
    }
    protected internal override void Open()
    {
        base.OnOpened(new AccountEventArgs($"Открыт новый депозитный счет! Id счета: {this.Id}", this.Sum));
    }

    public override void Put(decimal sum)
    {
        if (_days % 30 == 0)
            base.Put(sum);
		else
            base.OnAdded(new AccountEventArgs("На счет можно положить только после 30-ти дневного периода", 0));
    }

    public override decimal Withdraw(decimal sum)
    {
        if (_days % 30 == 0)
            return base.Withdraw(sum);
        else
            base.OnWithdrawed(new AccountEventArgs("Вывести средства можно только после 30-ти дневного периода", 0));
        return 0;
    }

    protected internal override void Calculate()
    {
        if (_days % 30 == 0)
            base.Calculate();
    }
}

Депозитные счета имеют особенность: они оформляются на продолжительный период, что накладывает некоторые ограничения. Поэтому здесь переопределяются еще три метода. Допустим, что депозитный счет имеет срок в 30 дней, в пределах которого, клиент не может ни добавить на счет, ни вывести часть средств со счета, кроме закрытия всего счета. Поэтому при всех операциях проверяем количество прошедших дней для данного счета:

if (_days % 30 == 0)

В данном случае сравниваем остаток деления количества дней на 30 дней. Если остаток от деления равен 0, то значит прошел очередной 30-дневный период, по окончанию которого происходит начисление процентов, возможен вывод средств или их добавление.

Собственно это все классы банковских счетов, которые мы будем использовать в программе.

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