fbpx

Каталог статей

Каталог статей для размещения статей информационного характера

Как выучить

Объектно-ориентированное программирование (C#)

Объектно-ориентированное программирование (C<#)

C#> является объектно-ориентированным языком программирования. Четыре основных принципа объектно-ориентированного программирования:

  • Абстракция Моделирование соответствующих атрибутов и взаимодействий сущностей в виде классов для определения абстрактного представления системы.
  • Инкапсуляция Сокрытие внутреннего состояния и функциональности объекта и предоставление доступа только через общедоступный набор функций.
  • Наследование Способность создавать новые абстракции на основе существующих абстракций.
  • Полиморфизм Возможность реализовать унаследованные свойства или методы различными способами в нескольких абстракциях.

В предыдущем уроке, введении в классы, вы видели и абстракцию, и инкапсуляцию. Класс BankAccount предоставлял абстракцию для концепции банковского счета. Вы могли изменять его реализацию, не затрагивая код, использующий класс BankAccount. Классы BankAccount и Transaction обеспечивают инкапсуляцию компонентов, необходимых для описания этих концепций в коде.

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

Создание различных типов счетов

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

  • Процентный счет, на который начисляются проценты в конце каждого месяца.
  • Кредитная линия, которая может иметь отрицательный баланс, но при наличии баланса ежемесячно начисляются проценты.
  • Счет предоплаченных подарочных карт, который начинается с одного депозита и может быть погашен только один раз. Его можно пополнять один раз в начале каждого месяца.

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

Вместо этого можно создать новые типы банковских счетов, которые наследуют методы и данные от класса BankAccount, созданного в предыдущем уроке. Эти новые классы могут расширять класс BankAccount с конкретным поведением, необходимым для каждого типа:

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

Хорошей практикой является создание каждого нового класса в отдельном исходном файле. В Visual Studio вы можете щелкнуть правой кнопкой мыши

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

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

Параметры этого нового конструктора соответствуют типу и именам параметров конструктора базового класса. Для указания вызова конструктора базового класса используется синтаксис : base(). Некоторые классы определяют несколько конструкторов, и этот синтаксис позволяет вам выбрать, какой конструктор базового класса вы вызываете. После обновления конструкторов можно разрабатывать код для каждого из производных классов. Требования к новым классам можно сформулировать следующим образом:

  • Счет, приносящий проценты:
    • Получает кредит в размере 2% от баланса на конец месяца.
    • Может иметь отрицательный баланс, но не больше по абсолютной величине, чем кредитный лимит.
    • Каждый месяц, когда остаток на конец месяца не равен 0, взимается процентная плата.
    • Взимается комиссия за каждое снятие средств, превышающее кредитный лимит.
    • Может быть пополнен на определенную сумму один раз в месяц, в последний день месяца.

    Вы можете видеть, что все три типа счетов имеют действие, которое происходит в конце каждого месяца. Однако каждый тип счета выполняет разные задачи. Для реализации этого кода вы используете полиморфизм. Создайте один виртуальный метод в классе BankAccount:

    Предыдущий код показывает, как вы используете ключевое слово virtual для объявления метода в базовом классе, для которого производный класс может предоставить другую реализацию. Виртуальный метод – это метод, который любой производный класс может выбрать для повторной реализации. Производные классы используют ключевое слово override для определения новой реализации. Обычно это называется “переопределение реализации базового класса”. Ключевое слово virtual указывает, что производные классы могут переопределять поведение. Вы также можете объявить абстрактные методы, в которых производные классы должны переопределить поведение. Базовый класс не предоставляет реализацию для абстрактного метода. Далее вам нужно определить реализацию для двух новых классов, которые вы создали. Начните с InterestEarningAccount :

    Добавьте следующий код в LineOfCreditAccount. Код отрицает баланс для вычисления положительного процентного начисления, которое снимается со счета:

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

    Конструктор предоставляет значение по умолчанию для параметра monthlyDeposit, чтобы вызывающие пользователи могли опустить 0 для отсутствия ежемесячного депозита. Затем переопределите метод PerformMonthEndTransactions, чтобы добавить ежемесячный депозит, если он был установлен в ненулевое значение в конструкторе:

    Переопределение применяет ежемесячный депозит, установленный в конструкторе. Добавьте следующий код в метод Main, чтобы проверить эти изменения для GiftCardAccount и InterestEarningAccount:

    Проверьте результаты. Теперь добавьте аналогичный набор тестового кода для LineOfCreditAccount :

    Когда вы добавите предыдущий код и запустите программу, вы увидите что-то вроде следующей ошибки:

    Фактический вывод включает полный путь к папке с проектом. Имена папок были опущены для краткости. Также, в зависимости от формата вашего кода, номера строк могут быть немного другими.

    Этот код не работает, потому что BankAccount предполагает, что начальный баланс должен быть больше 0. Другое предположение, заложенное в класс BankAccount, заключается в том, что баланс не может быть отрицательным. Вместо этого любое снятие средств, превышающее баланс счета, отклоняется. Оба эти предположения должны быть изменены. Счет кредитной линии начинается с 0 и обычно имеет отрицательный баланс. Кроме того, если клиент одалживает слишком много денег, он получает комиссию. Транзакция принимается, просто она стоит дороже. Первое правило можно реализовать, добавив в конструктор BankAccount необязательный аргумент, определяющий минимальный баланс. По умолчанию он равен 0. Второе правило требует наличия механизма, позволяющего производным классам изменять алгоритм по умолчанию. В некотором смысле, базовый класс “спрашивает” производный тип, что должно произойти при овердрафте. Поведение по умолчанию заключается в том, чтобы отклонить транзакцию, выбросив исключение.

    Давайте начнем с добавления второго конструктора, который включает необязательный параметр minimumBalance. Этот новый конструктор выполняет все действия, выполняемые существующим конструктором. Кроме того, он устанавливает свойство минимального баланса. Можно скопировать тело существующего конструктора, но это означает два места, которые придется менять в будущем. Вместо этого можно использовать цепочку конструкторов, чтобы один конструктор вызывал другой. В следующем коде показаны два конструктора и новое дополнительное поле:

    В предыдущем коде показаны два новых приема. Во-первых, поле minimumBalance помечено как readonly. Это означает, что его значение не может быть изменено после создания объекта. После создания BankAccount значение minimumBalance не может измениться. Во-вторых, конструктор, принимающий два параметра, использует : this(name, initialBalance, 0) < >в качестве его реализации. Выражение : this() вызывает другой конструктор, тот, который принимает три параметра. Эта техника позволяет вам иметь одну реализацию для инициализации объекта, даже если клиентский код может выбрать один из многих конструкторов.

    Эта реализация вызывает MakeDeposit только в том случае, если начальный баланс больше 0. Это сохраняет правило, что депозиты должны быть положительными, но позволяет открыть кредитный счет с балансом 0.

    Теперь, когда класс BankAccount имеет поле только для чтения для минимального баланса, последнее изменение заключается в изменении жесткого кода 0 на minimumBalance в методе MakeWithdrawal:

    После расширения класса BankAccount можно изменить конструктор LineOfCreditAccount, чтобы он вызывал новый базовый конструктор, как показано в следующем коде:

    Обратите внимание, что конструктор LineOfCreditAccount изменяет знак параметра CreditLimit, чтобы он соответствовал значению параметра m

    Один из методов – определить виртуальную функцию, в которой реализуется требуемое поведение. Класс BankAccount рефакторит метод MakeWithdrawal на два метода. Новый метод выполняет указанное действие, когда при снятии средств баланс становится ниже минимального. Существующий метод MakeWithdrawal имеет следующий код:

    Замените его следующим кодом:

    Добавленный метод является защищенным, что означает, что он может быть вызван только из производных классов. Это объявление не позволяет другим клиентам вызывать этот метод. Он также является виртуальным, чтобы производные классы могли изменять его поведение. Возвращаемый тип – Transaction? . Аннотация ? указывает, что метод может возвращать null. Добавьте следующую реализацию в LineOfCreditAccount, чтобы взимать комиссию при превышении лимита снятия средств:

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

    Запустите программу и проверьте результаты.

    Резюме

    Если вы застряли, вы можете посмотреть исходный текст этого руководства в нашем репозитории GitHub.

    Этот учебник продемонстрировал многие из приемов, используемых в объектно-ориентированном программировании:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *