Автор Анна Евкова
Преподаватель который помогает студентам и школьникам в учёбе.

История и развитие методологии объектно-ориентированного программирования. Сферы применения (Развитие методологии объектно-ориентированного программирования)

Содержание:

ВВЕДЕНИЕ

Технология программирования представляет собой совокупность средств и методов, необходимых для разработки программного обеспечения. Решающее значение для специалиста в области информационных технологий и программирования имеет выбор парадигмы программирования, определяющей успешность реализации программного продукта, и соответствующего ей языка программирования. За последние 50 лет появились сотни языков, поддерживающих различные парадигмы, причем некоторые из них используют несколько парадигм (такие языки называют мультипарадигменными). Однако, несмотря на большое количество языков программирования, существует несколько действительно важных концепций программирования, и не так много языков, которые были бы актуальны на протяжении более десяти лет [1.]. Именно парадигмы программирования определяют общий способ проектирования прикладных программ. Парадигмой программирования называют используемый различными языками подход к программированию, то есть, проще говоря, набор идей и понятий, определяющих стиль написания программ. Среди основных парадигм программирования выделяют объектно-ориентированную, императивную, декларативную, структурную, функциональную и логическую. Большинство языков программирования, активно используемых в современной разработке прикладных программ, являются мультипарадигменными.

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

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

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

  • изучить принципы объектно-ориентированного подхода к проектированию программного обеспечения, а также проследить за развитием методологии, изучив основы предшествовавшего ей структурного подхода;
  • сравнить реализацию объектно-ориентированной парадигмы программирования на примере языков C++ и Objective-C, выделить плюсы и минусы этих языков;
  • применить принципы объектно-ориентированной методологии к проектированию многофункционального программного обеспечения.

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

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

1. Развитие методологии объектно-ориентированного программирования

1.1 Предыстория появления

Инструкции первых языков программирования, появившихся в начале 50-х годов XX века и ориентированных на конкретный компьютер, записывались в исходном коде и выполнялись последовательно. Данные, полученные при выполнении предыдущих инструкций, могли быть считаны из памяти или записаны в нее. Таким образом, программы представляли собой последовательность команд, которые должен был выполнить компьютер. Языки программирования, использующие этот подход (прежде всего, это машинные инструкции и язык ассемблера), образовали императивную парадигму программирования. В отличие от декларативного подхода, при котором задается спецификация решения задачи, в императивном широко используются операторы присваивания. Ранние императивные языки сложны для понимания и решения прикладных задач.

Развитие вычислительных устройств и компьютерной техники привело к появлению процедурных языков. Процедурные языки программирования, первые реализации которых относятся к концу 50-х годов, позволили разбивать задачи на шаги и решать их за шагом, причем возможность определять каждый шаг в процессе решения задачи была предоставлена программисту. Эти языки использовали императивную парадигму, но последовательно выполняемые операторы могли быть собраны в подпрограммы, то есть более крупные целостные единицы кода, с помощью самого языка. Согласно некоторым классификациям, такой подход выделяется в самостоятельную парадигму, получившую название процедурной. К процедурным языкам программирования относятся, например, Паскаль, Си, Алгол, КОБОЛ и Фортран.

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

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

1.2 Структурное программирование

Структурное программирование – это способ организации программ, который облегчает их понимание, упрощает тестирование и делает более доступным изменение. Результаты показали, что использование структурного подхода вместе с другими улучшенными технологиями программирования может привести к увеличению производительности программиста и, соответственно, к снижению частоты появления ошибок в результирующем коде [6.]. Методология структурного программирования пытается решить проблемы, связанные с безусловными переходами (то есть переходами в заданную точку программы без проверки выполнения любых условий), чтобы позволить программистам следовать логике программ.

Потенциальный рост сложности программы проистекает из того факта, что она содержит много переходов к другим участкам кода как в прямую, так и в обратную сторону. Кроме того, поскольку программа претерпевает изменения в течение периода разработки, по мере ее дальнейшей отладки на протяжении жизненного цикла и по мере изменения в последующих новых проектах, сложность программы растет большими темпами. В некоторых случаях добавляется новый код, потому что программист не может найти существующий код, который выполняет желаемую функцию, или не уверен, как работает существующий код, или боится править существующий код из-за боязни испортить другую желаемую функцию и/или итоговый результат после нескольких модификаций [8.].

Концепции структурного программирования препятствуют использованию операторов «goto» и поощряют блочность и модульность программ, нисходящий подход и возможность многократного использования кода. Программы, написанные с использованием структурного подхода, более читабельны и более надежны. Кроме того, затраты и время на разработку программного обеспечения меньше, когда применяется структурное программирование, поскольку меньшие блоки программ могут быть написаны независимо (иногда разными, отдельными программистами или группами) и затем объединены для достижения желаемого конечного результата. Программы могут быть легко изменены и обновлены в соответствии с внешними обстоятельствами. В структурированных программах любая функция может выполняться с использованием одной или нескольких базовых алгоритмических конструкций: следование, цикл или ветвление, как показано на рисунке 1.

Рисунок 1 – Основные алгоритмические конструкции

Многие программы могут быть разбиты на ряд однозначно идентифицируемых подзадач. Хорошей практикой программирования является реализация каждой из этих подзадач в виде отдельного программного модуля. Идея модульного программирования состоит в том, чтобы разбить программу на более мелкие блоки, которые можно отладить и протестировать независимо друг от друга, и которые могут быть затем объединены для достижения общей цели [9.]. Использование модульной структуры программирования повышает точность и читабельность программы, а также облегчает ее будущие изменения.

Одним из мотивов модульности программы в методы является подход «разделяй и властвуй», который делает разработку программ более управляемой за счет создания независимым программ из небольших простых частей. Другой мотив – повторное использование кода – использование существующих методов в качестве строительных блоков для создания новых программ. Создавать программы из готовых методов приходится чаще, чем создавать собственный код [4.]. Третья причина – избежание повторения кода. Разделение программы на осмысленные методы облегчает отладку и сопровождение программы [3.].

При разработке новой программы общая стратегия должна быть полностью спланирована до начала непосредственного написания кода. Это позволяет целиком сосредоточиться логике программы, не заботясь о синтаксических деталях отдельных инструкций. Как только общая программная стратегия четко определена, можно рассмотреть детали, связанные с отдельными программными операциями. Этот подход обычно называют «нисходящим» проектированием программы. В больших программах весь этот процесс может повторяться несколько раз с добавлением дополнительных деталей на каждом этапе [7.].

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

Как следует из рисунка 2, основная программа находится на уровне цели 1. Эта основная программа делится на два модуля (подцели 11 и 1S) по мере роста требований. Это главное преимущество модульного программирования. Нисходящая методология проектирования часто использует процесс, называемый поэтапным усовершенствованием, или «разделяй и властвуй», посредством которого ситуация постепенно улучшается до достижения самого низкого уровня в структурной схеме. Этот процесс также очень часто применяется к спецификации процедур более низкого уровня. Нисходящий подход часто называют другими именами – структурированный подход, составной подход, программирование с поэтапным усовершенствованием и так далее. Хотя имена различаются, в целом образуется единый подход: сначала определяется основная функция, которая должна быть выполнена, затем определяются ее подфункции, затем подфункции этих функций и т.д. Нисходящее проектирование состоит из серии шагов, определяющих функции, необходимые для решения задачи, с точки зрения самой задачи [9.].

Рисунок 2 – Функциональная структура программы, спроектированной с использованием нисходящего подхода

Эта методика может быть полезной для программ, использующих независимые программные модули (то есть определяемые пользователем процедуры и функции). Восходящий подход предполагает детальную разработку этих программных модулей на ранних этапах общего процесса проектирования. Общая разработка программы основывается на известных характеристиках этих отдельных модулей [8.]. Восходящий подход противоположен нисходящему – он включает в себя написание модульной программы от частного к общему. То есть модули строятся от своего «скелета» в сторону увеличения кода, пока не будет получено общее решение. Это не очень приемлемая методология в модульном программировании, но, тем не менее, это полезный метод проектирования, когда в задачу входит только модификация и обновление уже существующей программы для получения необходимого результата.

Таким образом, структурное программирование позволяет разделить алгоритм на небольшие блоки. Эта стратегия была очень популярной в начале 1980-х годов. Однако с ростом сложности задач структурное программирование приводило к снижению качества кода, вследствие чего разработчикам требовалось писать все больше функций, которые очень редко могли использоваться в других программах. Многие программисты увидели в объектно-ориентированном программировании потенциальное решение возникшей проблемы. С одной стороны, Smalltalk использовали почти все более-менее сложные системы. С другой – использование виртуальных машин повышало требования к ресурсам. Smalltalk был первым широко распространенным объектно-ориентированным языком программирования. Здесь понятие класса стало основообразующей идеей для всех остальных конструкций языка (то есть класс в Смолтоке является примитивом, посредством которого описаны более сложные конструкции).

В 1983-м году появились сразу два языка, на которые повлияли языки Си и Smalltalk, а также структурный подход. Это были Objective-C и C++, которые пытались избавиться от необходимости повторного использования кода. Оба языка активно задействовали новую, объектно-ориентированную парадигму, быстро ставшую популярной.

1.3 Объектно-ориентированное программирование

Выбор методологии для создания программы до появления языков четвертого поколения учитывал размеры программ, которые обычно были небольшими. В небольших программах наиболее распространенным оператором обычно является оператор присваивания. Однако в больших программах (более 10000 строк) наиболее распространенным оператором обычно является вызов процедуры подпрограммы. Обеспечение правильной передачи параметров в правильную подпрограмму становится серьезной проблемой. Концепция объектно-ориентированного проектирования заключалась в определении всех классов (и связанных с ними отношений и поведения), которые имеют отношение к решаемой проблеме.

Несмотря на то что структурирование программы и использование иерархической модели может помочь в проектировании некоторого программного обеспечения, для ряда больших программ объективно-ориентированный подход, как оказалось, является более гибким, поскольку разделяет программу на набор модулей, каждый из которых управляет своими собственными данными, алгоритмами или устройствами, но доступ к ним возможен только при первом указании именованного доступа к объекту класса модуля. Вместо того, чтобы полагаться на схему иерархии структурного программирования, объектно-ориентированному программированию необходим набор ссылок, чтобы проследить, какие модули или классы доступны из других мест [5.].

Состояние объекта в объектно-ориентированном языке является в основном внутренним для самого объекта. Каждый объект включает в себя набор функций и процедур, с помощью которых можно получить доступ и изменить локальное состояние. Они называются методами, но похожи на обычные процедуры и функции, за исключением того, что они могут автоматически получать доступ к данным объекта (в отличие от «внешнего мира») и, следовательно, могут рассматриваться как содержащие неявный параметр, представляющий сам объект.

Объекты могут быть объявлены путем создания шаблона для локального состояния и методов. Этот шаблон называется классом, и по сути он похож на тип данных. Действительно, во многих объектно-ориентированных языках класс является типом и включается в систему типов языка более или менее стандартными способами. Затем объявляются объекты определенного класса точно так же, как переменные определенного типа объявляются в языке, таком как Си или Паскаль. Говорят, что объект является экземпляром класса.

Центральным понятием объектно-ориентированного программирования является объект, который становится своего рода модулем, содержащим данные и подпрограмму. Объект – это самодостаточная сущность, которая имеет внутреннее состояние (содержащиеся в нем данные) и может отвечать на сообщения (обращения к его методам). Например, объект записей студента имеет состояние, состоящее из сведений обо всех зарегистрированных студентах. Если ему отправляется сообщение, в котором говорится о добавлении сведений о новом ученике, он отвечает, изменяя свое состояние. Если будет отправлено сообщение о том, что оно должно быть напечатано, оно ответит распечаткой списка сведений обо всех зарегистрированных студентах.

Подход объектно-ориентированного программирования к разработке программного обеспечения должен начинаться с определения объектов, вовлеченных в проблему, и определения сообщений, на которые эти объекты должны отвечать. В результате получается набор объектов, каждый со своими данными и собственным набором обязанностей. Объекты взаимодействуют, посылая сообщения друг другу [7.].

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

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

Инкапсуляция – это возможность объединять код и данные в одном месте и скрывать эти данные от внешнего воздействия. Иными словами, это такое объединение внутри класса полей и методов, при котором доступ к полю возможен только путем вызова соответствующего метода. При идеальном выполнении принципа инкапсуляции поля класса могут быть только личными (использоваться только методами данного класса).

Наследование – это возможность определения для базового класса (предка) иерархии производных классов (наследников), в каждом из которых доступны элементы базового класса (их описание становится частью описания производного класса). Иначе говоря, наследование – механизм, посредством которого класс может наследовать элементы другого класса и добавлять к ним свои элементы.

Как правило, базовый класс является общим, производные – более специальными, конкретными. Естественно, у класса-наследника обычно больше полей и методов, чем у класса-предка, так как при наследовании обычно добавляются новые элементы.

Если имеется иерархия классов, то можно рассматривать защищенные (protected) элементы класса, которые доступны для методов своего класса и его наследников.

Одиночное наследование – это случай, когда каждый производный класс может наследовать только от одного базового класса, тогда как множественное наследование – это случай, когда класс может наследовать от двух или более базовых классов. В современных объектно-ориентированных языках, таких как Java и C++, предусмотрено множественное наследование.

Полиморфизм – это механизм, который позволяет объектам разных типов по-разному реагировать на один и тот же вызов функции. Перегрузка и шаблоны функций могут рассматриваться как примитивные полиморфизмы, поскольку решение о вызове конкретной функции принимается во время компиляции, а не во время выполнения. Во время компиляции точная природа некоторых объектов не может быть определена. Полиморфизм – это способность идентифицировать определенные аспекты, общие для нескольких типов данных, и писать код, который одинаково хорошо работает со всеми из них, игнорируя различия в ситуациях, не имеющих значения [2.].

Как структурное программирование, так и ООП требуют элементарного понимания концепций программирования и порядка выполнения инструкций программы. Циклы, условные операторы и переменные являются понятиями, которые необходимо знать вне зависимости от того, какой из подходов используется. В таблице 1 приведены основные различия между объектно-ориентированным и структурным программированием.

Таблица 1

Различия между структурной и ООП-парадигмой

Структурное программирование

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

Нисходящий подход

Восходящий подход

Фокус на алгоритме и инструкциях

Фокус на объектной модели

Программа делится на подмодули, функции, процедуры

Программа организована с помощью ряда классов и объектов

Функции не зависят друг от друга

Классы могут образовать иерархию

Данные и функции рассматриваются как отдельные объекты

Данные и функции – единое целое

Повторное использование методов невозможно

Возможно повторное использование методов

Используется вызов функции

Сообщения передаются объектам

Абстракция функций

Абстракция данных

Главное – алгоритм

Главное – данные

Инкапсуляция отсутствует

Данные и функциональные возможности – единая сущность

Ключевое отношение – между программистом и программой

Ключевое отношение – между программистом и пользователем

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

2.1 Объектно-ориентированное программирование в языке C++

C++ был написан Бьерном Страуструпом в Bell Labs в 1983-1985 гг. C++ является расширением Си. До 1983 года Бьерн Страуструп добавил функции в Си и сформировал то, что он назвал «Си с классами». Он объединил использование классов и объектно-ориентированных функций из языка Simula с мощью и эффективностью языка Си.

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

В листинге 1 дается простейшее описание класса на языке С++.

Листинг 1. Описание класса в языке C++

class имя_класса {

private:

описание личных элементов класса

public:

описание общих элементов класса

};

Под личными элементами класса (private) понимаются такие элементы, которые могут использоваться только методами своего класса. К общим элементам класса (public) доступ разрешен в любом месте программы. Существуют и другие способы доступа к элементам класса.

Поля класса описываются внутри класса как обычные переменные. Методы – это функции, которые можно применять к полям. Считается хорошим стилем программирования, что внутри класса методы задаются своими прототипами. Вне класса (ниже его описания) приводится полное описание метода. Очень короткие методы можно полностью описывать внутри класса.

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

Если внутри класса метод описан прототипом:

тип_результата имя_метода (список типов параметров),

то при полном описании вне класса метод имеет заголовок:

тип_результата имя_класса::имя_метода (список параметров),

т. е. в заголовке перед именем метода указывается имя класса (через знак «::»).

Существуют особые методы класса: конструкторы и деструкторы [10.]. Назначение конструктора заключается в создании экземпляра класса и инициализации его полей. Имя конструктора совпадает с именем класса. Конструктор никогда не вызывается явно, его вызов осуществляется компилятором в момент создания экземпляра класса.

Деструктор вызывается для уничтожения экземпляра класса. Имя деструктора образуется как ~имя_класса. Деструктор может вызываться в программе явно или (что происходит обычно) его вызов обеспечивается компилятором в момент уничтожения экземпляра класса.

Наличие конструктора и деструктора для любого класса обязательно; при их отсутствии компилятор автоматически создает стандартные варианты конструктора и деструктора.

Экземпляры класса могут создаваться автоматически или динамически. Уничтожение автоматически созданных экземпляров классов происходит также автоматически при завершении выполнения блока функции, в котором они были определены. Определение (описание) автоматического экземпляра класса может встречаться в любом месте функции и имеет вид:

имя_класса имя_экземпляра(параметры конструктора);

Перед созданием динамического экземпляра класса (по аналогии с любыми динамическими переменными) необходимо объявить указатель на экземпляр:

имя_класса* указатель_на_экземпляр;

Динамический экземпляр класса создается с помощью оператора new, а уничтожается с помощью оператора delete:

указатель_на_экземпляр= new имя_класса (параметры конструктора);

delete указатель_на_экземпляр;

Метод класса (по аналогии с полем структуры) вызывается одним из следующих способов:

имя_экземпляра.имя_метода или имя_экземпляра->имя_метода

Инкапсуляция – такое объединение внутри класса полей и методов, при котором доступ к полю возможен только путем вызова соответствующего метода.

При идеальном выполнении принципа инкапсуляции поля класса могут быть только личными (private).

Ниже перечислены уровни инкапсуляции, т. е. уровни доступа к элементам класса:

  • private (личный). Этот уровень накладывает самые жесткие ограничения на доступ к элементам класса. Именно эти элементы могут быть использованы только методами данного класса. Как правило, поля класса объявляются private.
  • public (общий). Элементы класса данного уровня доступны из любой точки программы (самый «широкий» доступ).
  • protected (защищенный). Элементы класса данного класса доступны методам данного класса и его наследников.

Наследование – это возможность определения для базового класса (предка) иерархии производных классов (наследников), в каждом из которых доступны элементы базового класса (их описание становится частью описания производного класса). Иначе говоря, наследование – механизм, посредством которого класс может наследовать элементы другого класса и добавлять к ним свои элементы.

Как правило, базовый класс является общим, производные – более специальными, конкретными. Естественно, у класса-наследника обычно больше полей и методов, чем у класса-предка, так как при наследовании обычно добавляются новые элементы. Если имеется иерархия классов, то можно рассматривать защищенные (protected) элементы класса, которые доступны для методов своего класса и его наследников. Наследование может быть единичным (наследник имеет одного предка) и множественным (количество предков больше 1). В C++ допустимо множественное наследование [10.]. Наследование может быть общим, личным и защищенным. Видимость полей и функций базового класса из производного определяется секцией, в которой находится объявление компонента, и видом наследования (см. таблицу 2).

Как видно из таблицы 2, при общем наследовании общие и защищенные элементы класса-предка сохраняют свой уровень инкапсуляции и в классе-наследнике. При личном наследовании общие и защищенные элементы класса-предка становятся личными для класса-наследника. При защищенном наследовании защищенные элементы сохраняют свой уровень в классе-наследнике, а общие становятся защищенными. Независимо от вида наследования, личные элементы базового класса недоступны в производных классах. Таким образом, чтобы «достать» их из производного класса, надо использовать унаследованные общие и защищенные методы класса-предка.

Таблица 2

Видимость компонентов базового класса в производном

Вид наследования

Объявление компонентов в базовом классе

Видимость компонентов в производном классе

private

не доступны

private

protected

private

public

private

private

не доступны

protected

protected

protected

public

protected

private

не доступны

public

protected

protected

public

public

Описание производного класса в общем виде приведено в листинге 2.

Листинг 2. Описание производного класса в языке C++

class Имя_производного_класса:

Вид_наследования_1 Имя_базового_класса_1,

Вид_наследования_2 Имя_базового_класса_2,

...

Вид_наследования_n Имя_базового_класса_n,

{

Описание_добавляемых_элементов_производного_класса

};

Если Вид_наследования не указан, то по правилу умолчания принимается public.

Самая простая ситуация – это единичное общее наследование. Заголовок производного класса в этом случае имеет вид:

class имя_наследника: public имя предка;//можно без public

Преимущества C++ [11.]:

  1. Портативность

C++ предлагает функцию переносимости или независимости от платформы, которая позволяет пользователю легко запускать одну и ту же программу в разных операционных системах или интерфейсах

  1. Объектно-ориентированность и мультпарадигменность

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

C++ является языком программирования с несколькими парадигмами. Термин «парадигма» относится к стилю программирования. Парадигмой программирования называют используемый различными языками подход к программированию, то есть, проще говоря, набор идей и понятий, определяющих стиль написания программ. Среди основных парадигм программирования выделяют объектно-ориентированную, императивную, декларативную, структурную, функциональную и логическую. Большинство языков программирования, активно используемых в современной разработке прикладных программ, являются мультипарадигменными.

  1. Манипуляции низкого уровня

Поскольку C++ тесно связан с Си, который является процедурным языком, тесно связанным с машинным языком, C++ допускает низкоуровневое манипулирование данными на определенном уровне. Встроенные системы и компилятор создаются с помощью C++.

  1. Управление памятью

C++ дает программисту полный контроль над управлением памятью. Это можно рассматривать как ценное качество, так и как повышенную ответственность, поскольку такая возможность увеличивает ответственность пользователя за управление памятью, а не за управление сборщиком мусора. Эта концепция реализована с помощью динамического распределения памяти с использованием указателей.

  1. Поддержка большого сообщества

C++ имеет большое сообщество, которое поддерживает его, предоставляя онлайн-курсы и лекции, как платные, так и бесплатные. С точки зрения статистики, C++ является 6-м наиболее часто используемым тегом на StackOverflow и GitHub.

  1. Совместимость с Си

C++ в значительной степени совместим с C. Практически каждая скомпилированная и отлаженная программа на языке Си является допустимой программой на C++. В зависимости от используемого компилятора каждая программа C++ может запускаться в файле с расширением .cpp.

  1. Масштабируемость

Масштабируемость относится к способности программы масштабироваться. Это означает, что программа на C++ способна работать как в небольших масштабах, так и на больших объемах данных. Также можно создавать приложения, которые являются ресурсоемкими.

  1. Производительность

Как язык со статической типизацией, C++, как правило, более производительный, чем языки с динамической типизацией, потому что код проверяется перед выполнением. Java набирает обороты с точки зрения скорости, но, в конце концов, в зависимости от того, насколько талантлив разработчик C++, C++ все еще может быть быстрее, чем Java.

  1. Карьерные перспективы

Для разработки популярных видеоигр, скорее всего, придется использовать C++. Многие мобильные бестселлеры также были созданы с Cocos2d-x, который является свободно распространяемым кроссплатформенным игровым движком с открытым исходным кодом. Игры Cocos2d-x разрабатываются на C++, хотя Cocos2d теперь также поддерживает другие языки.

Недостатки языка C++ [11.]:

  1. Использование указателей

Указатели в C / C++ довольно сложны для понимания и занимают много памяти. Неправильное использование указателей, известное как «висячие указатели», может привести к сбою системы или неправильному поведению системы.

  1. Проблема безопасности

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

  1. Отсутствие сборщика мусора

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

2.2 Объектно-ориентированное программирование в языке Objective-C

Objective-C – это язык общего назначения, который разработан поверх языка программирования Си путем добавления возможностей языка программирования Smalltalk, что делает его объектно-ориентированным языком. Он в основном используется при разработке операционных систем iOS и Mac OS X, а также его приложений. Язык был создан Брэдом Коксом в начале 1980-х в его компании Stepstone, причем первоначально Objective-C был разработан NeXT для ее ОС NeXTSTEP, от которой Apple перешла к его iOS и Mac OS X. Язык полностью поддерживает объектно-ориентированное программирование, включая полиморфизм, наследование и инкапсуляцию.

Основная цель языка программирования Objective-C состоит в том, чтобы добавить объектную ориентацию к языку программирования Си. Классы являются центральным понятием в Objective-C и часто называются пользовательскими типами.

Класс используется для определения объекта, и он объединяет представление данных и методы для манипулирования этими данными в одном месте. Данные и методы класса называются членами класса.

Характеристики Objective-C:

  1. Класс определяется в двух разных разделах, а именно: @interface и @implementation.
  2. Почти все структурные единицы языка представлены в виде объектов.
  3. Объекты получают сообщения, и они часто называются получателями.
  4. Объекты содержат переменные экземпляра класса.
  5. Объекты и переменные экземпляра имеют область видимости.
  6. Классы скрывают реализацию объекта.
  7. Свойства используются для предоставления доступа к переменным экземпляра класса в других классах.

Когда определен класс, то определена «схема» для типа данных. На самом деле это определение не определяет какие-либо данные, но объясняет, что означает имя класса, то есть то, из чего будет состоять объект класса и какие операции могут быть выполнены с таким объектом.

Определение класса начинается с ключевого слова @interface, за которым следует имя интерфейса (класса); и тело класса, заключенное в пару фигурных скобок. В Objective-C все классы являются производными от базового класса, называемого NSObject. Это суперкласс для всех классов Objective-C. Он предоставляет основные методы, такие как выделение памяти и инициализация. Например, тип данных Box может быть определен с использованием ключевого слова class так, как показано в листинге 3.

Листинг 3. Описание класса в языке Objective-C

@interface Box:NSObject

{

double length;

double breadth;

}

@property(nonatomic, readwrite) double height;

@end

Переменные экземпляра класса являются частными (private) и доступны только внутри реализации класса.

Свойства (properties) введены в Objective-C для обеспечения доступа к переменной экземпляра класса вне класса.

Различные части объявления свойства заключаются в следующем.

  1. Свойства начинаются с @property, который является ключевым словом.
  2. За ним следуют спецификаторы доступа, которые являются неатомарными или атомарными, доступными для чтения или записи и только для чтения, сильными или слабыми (в случае указателей) и т.д. Для других типов можно использовать модификаторы readwrite или readonly.
  3. Далее следует тип данных переменной.
  4. Наконец, имя свойства, оканчивающееся точкой с запятой.

Класс предоставляет «схемы» для объектов, поэтому в основном объект создается при помощи класса. Объекты классов объявляются точно так же, как и переменные базовых типов. Операторы из листинга 4 объявляют два объекта класса Box.

Листинг 4. Создание двух экземпляров классов в Objective-C

Box box1 = [[Box alloc] init];

Box box2 = [[Box alloc] init];

Оба объекта box1 и box2 будут иметь собственную копию элементов данных.

Objective-C не допускает множественного наследования. Все классы в Objective-C являются производными от суперкласса NSObject. Наследование имеет следующий вид:

@interface имя класса-наследника: имя базового класса

Основной заботой создателя языка была ремонтопригодность больших файлов кода. Опыт мира структурированного программирования показал, что одним из основных способов улучшения кода было разбиение его на более мелкие части. Objective-C добавил концепцию категорий, чтобы помочь с этим процессом.

Категория собрала реализации метода в отдельные файлы. Таким образом, программист мог бы поместить группы связанных методов в свою собственную «категорию», чтобы сделать ее более читаемой. Например, можно создать категорию «SpellChecking» для объекта String, собрав все методы, связанные с проверкой орфографии, в одном месте.

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

Объединение категорий с динамической типизацией (Objective-C имеет динамическую типизацию, как и его предшественник Smalltalk) дает синергию, которая приводит к невероятной гибкости, и это стало чем-то новым даже для популярных языков программирования. К примеру, в языке Java просто позволить String добавить метод checkSpelling сам по себе недостаточно, поскольку остальная часть программы не позволит вызывать этот новый метод – все методы должны быть известны во время компиляции как побочный эффект сильной типизации. Вместо этого необходимо использовать вспомогательные объекты, специальные классы или паттерны, и все это увеличивает сложность и громоздкость реализации.

Листинг 5 иллюстрирует пример описания и реализации категории в языке Objective-C.

Листинг 5. Пример описания и реализации категории в Objective-C

#import "ClassName.h"

@interface ClassName ( CategoryName )

methods declarations

@end

#import "CategoryName.h"

@implementation ClassName ( CategoryName )

methods bodies

@end

Преимущества Objective-C [12.]:

  1. Objective-C – очень «практичный» язык. Он использует в качестве среды разработки легковесную систему реального времени, написанную на Си, которая практически не увеличивает размер приложения. И это было значительным преимуществом в годы появления языка, поскольку большинство объектно-ориентированных систем того времени использовали большие виртуальные машины. Программы, написанные на Objective-C, имели тенденцию быть ненамного больше, чем размер их кода и библиотек (которые часто не нужно было включать в дистрибутив программного обеспечения), в отличие от систем Smalltalk, где требовались огромные объемы памяти только для того, чтобы открыть окно.
  2. Точно так же язык был реализован поверх существующих компиляторов Си (сначала как препроцессор, затем как модуль GCC), а не как новый компилятор. Это позволило Objective-C использовать огромную существующую коллекцию кода на Си, библиотек, инструментов и идей. Можно легко обернуть существующие библиотеки C в обертки Objective-C, чтобы придать им ООП-стиль и легко использовать их в своих программах. Иными словами, любая программа, написанная на Си, может быть улучшена с использованием возможностей Objective-C.
  3. Все эти практические изменения снизили барьер для входа, что было, вероятно, самой большой проблемой для широкого распространения Smalltalk в 1980-х годах. В целом Objective-C можно описать как обеспечивающий большую гибкость более поздних систем Smalltalk на языке, который развертывается так же легко, как и C.

Недостатки Objective-C [12.]:

  1. Первые версии Objective-C не поддерживали сборку мусора. В то время это было предметом некоторых дискуссий, и многие разработчики считали, что долгие язык избежал многих проблем Smalltalk, не включив эту функцию. Однако, оглядываясь назад, стоит заметить, что это серьезный недосмотр. Хотя некоторые сторонние реализации добавили эту функцию (особенно GNUStep), она остается вне стандарта.
  2. Другая проблема заключается в том, что Objective-C не включает механизм пространства имен. Вместо этого программисты вынуждены добавлять префиксы к именам классов, и даже в самом лучшем случае это плохое решение. Добавление системы пространств имен было бы простым и естественным в Objective-C (где они довольно четко вписываются в систему библиотек), поэтому несколько удивительно, что этого не произошло.
  3. Objective-C не поддерживает перегрузку операторов (хотя и поддерживает специальный полиморфизм), в отличие от некоторых других объектно-ориентированных языков программирования, таких как C++.
  4. Objective-C позволяет объекту наследовать только от одного класса (запрещая множественное наследование).
  5. Наконец, поскольку язык был основан на компиляторах языка Си, он по-прежнему сильно зависит от заголовочного файла для правильной работы. Это дополнительная сложность, которая больше не нужна и отсутствует в большинстве современных языков, таких как Java или C#.

Различия в реализации объектно-ориентированной парадигмы для языков C++ и Objective-C приведены в таблице 3. Несмотря на ряд трудностей, связанных с реализацией принципов ООП в ранних версиях языка C++, реализация в его более поздних версиях обладает рядом существенных преимуществ, которые делают этот язык (а также более современные си-подобные языки, такие как C#) лучшим выбором для реализации прикладных программ.

Таблица 3

Различия в реализации ООП

C++

Objective-C

Добавляет ООП, метапрограммирование и обобщенное программирование к языку Си

Добавляет к Си только ООП

Имеет встроенную стандартную библиотеку

Зависит от сторонних библиотек

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

В основном используется для создания прикладных пользовательских приложений

Для реализации всех принципов ООП в ранних версиях языка необходимо большое количество кода

Простое управление классами и объектами

В следующем разделе выполняется проектирование и разработка простого программного приложения для демонстрации особенностей применения объектно-ориентированного программирования на примере языка программирования C#.

3. Реализация прикладного программного приложения с использованием объектно-ориентированной методологии

3.1 Обзор и анализ предметной области

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

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

Каждый объект (вклад) имеет следующий набор атрибутов:

  • ФИО клиента банка, который открывает вклад;
  • Срок (количество лет), на который открывается вклад;
  • Сумма вклада (в рублях);
  • Процентная ставка.

В качестве программного средства для реализации приложения был выбран язык программирования C#, который является одним из наиболее востребованных и адаптивных языков программирования, способным выполнить поставленные задачи.

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

Самым популярным русскоязычным программным решением для расчета доходности и подбора вклада по заданным характеристикам является калькулятор сайта banki.ru, общий вид которого приведен на рисунке 3.

Рисунок 3 – Калькулятор вкладов на сайте Банки.ру. Доступен выбор валюты

3.2 Проектирование классов программы

Класс в C # определяется ключевым словом class, за которым следует идентификатор (имя) класса и набор элементов данных и методов в отдельном блоке кода. Классы в C# могут содержать следующие элементы:

  • Поля - переменные-члены определенного типа;
  • Свойства – это особый тип элементов, которые расширяют функциональность полей, предоставляя возможность дополнительного управления данными при их чтении из полей и записи в поля класса;
  • Методы – функции, которые реализуют манипулирование данными.

В программе реализовано четыре класса – абстрактный (базовый) класс вклада Deposit и три класса-наследника: DepositYear (вклад с ежегодной капитализацией), DepositMonth (вклад с ежемесячной капитализацией), DepositDay (вклад с ежедневной капитализацией).

Для разработки семейства классов вкладов были выделены следующие сущности:

  1. Класс Deposit. Этот класс является базовым для всех остальных классов семейства вкладов, поэтому от него наследуются все остальные классы. Он содержит в себе следующие свойства:
    1. ФИО клиента банка, который открывает вклад (тип string);
    2. Срок (количество лет), на который открывается вклад (тип int);
    3. Сумма вклада в рублях (тип double);
    4. Процентная ставка по вкладу (тип double).

Для реализации условий по вкладу предусмотрены два статических поля MIN_SUM (тип double) и MIN_YEARS (тип int) для хранения минимальной вносимой суммы и минимального срока вклада соответственно.

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

p – это итоговая сумма, которую получит пользователь;

с – начальная сумма, которую положит пользователь на вклад;

r/n – это процентная ставка, деленная на количество дней начисления денег (n будет меняться в зависимости от того, какое значение выберет пользователь, например, если проценты начисляются ежедневно, то начислять деньги будут каждый день, и n=365, если ежемесячно, то начислять проценты будут раз в месяц, то есть 12 раз, значит n=12);

nt – это количество дней начисления денег, умноженное на количество лет, на которое хочет положить деньги пользователь.

  1. Класс DepositYear. Этот класс наследует свойства класса Deposit и переопределяет метод GetOverall способом, описанным в пункте 1 – проценты по вкладу рассчитываются ежегодно.
  2. Класс DepositMonth. Этот класс наследует свойства класса Deposit и переопределяет метод GetOverall способом, описанным в пункте 1 – проценты по вкладу рассчитываются ежемесячно.
  3. Класс DepositDay. Этот класс наследует свойства класса Deposit и переопределяет метод GetOverall способом, описанным в пункте 1 – проценты по вкладу рассчитываются ежедневно.

Кроме того, в каждом из классов реализованы три конструктора: по умолчанию (инициализирует поля значениями по умолчанию), с параметрами (присваивает полям значения переданного параметра), копирования (копирует поля из переданного в качестве параметра объекта этого же класса). Кроме того, реализован деструктор для удаления объекта класса, переопределенный метод ToString для получения информации о вкладе в виде строки.

Классы реализуют интерфейс IComparable для сравнения объектов. Для этого в нем реализованы методы CompareTo и GetHashCode. Сравнение осуществляется по доходности вклада, вычисляемой при помощи GetOverall. Лучшим считается вклад с большей доходностью.

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

Значения статических полей MIN_SUM и MIN_YEARS переопределяются в классах-наследниках для реализации условий соответствующего вклада.

На рисунке 4 представлена диаграмма классов.

Рисунок 4 – Диаграмма классов программы

3.4 Проектирование интерфейса и алгоритмов программы

Окно приложения содержит единственную форму, вид которой приведен на рисунке 5. На форме расположен выпадающий список (ComboBox) для выбора типа вклада, текстовые поля (TextBox) для ввода ФИО пользователя, отображения процентной ставки и вывода информации соответственно. Кроме того, на форме присутствуют два регулятора типа «вверх-вниз» (NumericUpDown) – для ввода суммы вклада и срока хранения, две кнопки (Button) – для расчета дохода по выбранному вкладу за указанный срок с заданной суммой и подбора вклада по заданным параметрам (сроку и сумме). На форме также расположены шесть меток (Label) с необходимыми подписями к вышеуказанным элементам.

Рисунок 5 – Главное окно приложения

В случае попытки подбора вклада или расчета доходности с полем ФИО, которое является пустой либо состоящей только из пробелов строкой, программа выдаст сообщение об ошибке, как показано на рисунке 6.

Основные алгоритмы для расчета описаны в пункте 1 подраздела 3.2 и реализованы в переопределенном методе GetOverall классов-наследников (листинги 6 – 8). Полный исходный код модулей программы с подробными комментариями приведен в приложении 1.

Листинг 6. Метод вычисления доходности по вкладу с ежегодным начислением процентов

public override double GetOverall()

{

double r = Rate / 100;

return Math.Round(Sum * Math.Pow(1 + r, Years), 2);

}

Рисунок 6 – Сообщение об ошибке при вводе пустой строки для хранения ФИО вкладчика

Листинг 7. Метод вычисления доходности по вкладу с ежемесячным начислением процентов

public override double GetOverall()

{

double r = Rate / 100;

double monthRate = r / 12;

double months = Years * 12;

return Math.Round(Sum * Math.Pow(1 + monthRate, months), 2);

}

Листинг 8. Метод вычисления доходности по вкладу с ежедневным начислением процентов

public override double GetOverall()

{

double r = Rate / 100;

double dayRate = r / 365;

int days = Years * 365;

return Math.Round(Sum * Math.Pow(1 + dayRate, days), 2);

}

Программные модули с исходным кодом классов DepositYear, DepositMonth и DepositDay соответственно описаны в файлах DepositYear.cs, DepositMonth.cs и DepositDay.cs. Исходный код базового (абстрактного) класса Deposit расположен в файле Deposit.cs. Файл Form1.cs описывает код формы приложения, реализующей графический пользовательский интерфейс.

3.5 Тестирование программы

Результатом тестирования приложения является набор тестов, на которых была проверена работа спроектированной программы, в виде входных текстов, предъявленных программе, и текстов ее ответных реакций [7.]. Существует несколько стратегий тестирования, в том числе «тестирование черного ящика» (приложение рассматривается как черный ящик). Целью здесь ставится выяснение обстоятельств, в которых поведение приложения не соответствует спецификации. Для обнаружения всех ошибок в приложении необходимо выполнить тестирование на всевозможных наборах данных [9.]. Для большинства приложений такое невозможно, поэтому применяют ручное тестирование, при котором тестирование программы ограничивается небольшим подмножеством всевозможных наборов данных. Объем этих данных должен быть достаточным для демонстрации всех функций разработанной программы с учетом проверок и обработки исключительных ситуаций. При этом необходимо выбирать наиболее подходящие подмножества, то есть подмножества с наивысшей вероятностью обнаружения ошибок (контрольный пример) [6.].

Для проверки на тестовом примере необходимо выполнить запуск программы. Он осуществляется так же, как и запуск любой другой программы в среде Windows, то есть для ее запуска необходимо щелкнуть мышью по пиктограмме запускающего (исполняемого) файла «DepositProject.exe» двойным щелчком мыши. Установка приложения не требуется.

На рисунке 7 проиллюстрирован результат работы приложения, в котором осуществляется ввод данных для открытия и для подбора вклада. По результатам выполнения соответствующих действий необходимые выходные данные записываются в текстовое поле с меткой «Информация». Проведенные отладка и тестирование подтверждают корректность работы разработанного программного приложения.

Рисунок 7 – Результат работы приложения

ЗАКЛЮЧЕНИЕ

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

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

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

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

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

Во второй главе работы был выполнен краткий обзор языков программирования C++ и Objective-C, изучены основные принципы реализации ООП в этих языках. Оба языка программирования появились в 1983-м году; они опирались на структурную парадигму программирования, но взяли за основу объектно-ориентированный подход прежде всего с целью избавиться от повторного использования ранее написанного кода. Также были рассмотрены плюсы и минусы каждого из языков, выделены основные различия в реализации ООП.

В третьей главе работы для демонстрации применения объектно-ориентированной методологии с целью создания прикладной программы в качестве инструмента был выбран язык программирования C# со средой разработки Visual Studio 2017, обеспечивающей быструю многоуровневую разработку, свободно распространяемой для учебных проектов и обладающей хорошей документацией. В процессе разработки прикладного приложения «Калькулятор вкладов» самостоятельно были изучены функции и возможности объектно-ориентированного языка программирования C#. Разработка программного средства поспособствовала систематизации, закреплению и расширению знаний, полученных при изучении. Закрепление теоретического материала происходило в ходе решения поставленной задачи – проектирования классов, перевода основных методов на машинный язык и исправления появившихся во время решения ошибок. Завершением курсовой работы явилось написание пояснительной записки, при выполнении которой также были закреплены основные знания по написанию отчетов в соответствии с методическими рекомендациями. Созданная программа имеет понятный графический интерфейс, выводит все необходимые пояснения и подсказки, является законченной и удобной для использования.

Таким образом, выполнены все поставленные задачи, и цель курсовой работы достигнута в полном объеме.

СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ

  1. Abott, R. Program Design by Informal English Descriptions, CACM, 1993, Vol. 26 No. 11, pp. 892 - 894.
  2. Biddle, R. L., Tempero, E. D., Teaching C++ Experience at Victoria University of Wellington, 1994, In Proceedings of Software Education Conference, Dunedin, New Zealand, pp. 274 - 281.
  3. Champeaux, D. D., Panel: Structured Analysis and Object-Oriented Analysis, ECOOP/OOPSLA ’90 Proceedings, pp. 135 - 139.
  4. Deitel, P. J., Deitel, H. M., Java How To Program, USA, Pearson Inc., 7th Ed., 2007, pp. 421 - 423.
  5. Hubbard, J. R., Programming with C++ Schaum’s Outlines, New York, 2005, McGraw-Hill Companies, Inc., pp. 273 – 299
  6. Istatkova, G. Algebra of Algorithms in Procedural and Object-Oriented, structured Programming, Automatica & Informatics, 2001, Vol.3, No. 4, pp. 56 - 62.
  7. Louden, K. C., Programming Languages: Principles and Practice, Boston, 1993, PWS Publishing Company, pp. 300 - 345.
  8. Owolabi, O., Ndeekor, C. B. Structured Programming with Pascal, Aba, 2005, Granite Ventures Nig. Ltd, pp. 19 - 21.
  9. Pressman, R. S., Software Engineering: A Practitioner’s Approach, New York, 2005, McGrawHill International 6th Ed., pp. 217 - 218.
  10. Страуструп Б. Язык программирования C++: специальное издание / Б. Страуструп; пер.: С. Анисимов, М. Кононов; ред.: Ф. Андреев, А. Ушаков. - [Б. м.]: Бином-Пресс, 2008. - 1098 с.
  11. Advantages and Disadvantages of C++ [Электронный ресурс] / Режим доступа: https://data-flair.training/blogs/advantages-and-disadvantages-of-cpp/ (дата обращения: 17.10.2020)
  12. Objective-C [Электронный ресурс] / Режим доступа: http://www.fact-index.com/o/ob/objective_c.html (дата обращения: 15.10.2020)

Приложение 1. Исходные коды программ

Листинг А1. Исходный код модуля Deposit.cs

using System;

namespace DepositProject

{

/// <summary>

/// Абстрактный класс Вклад с возможностью сравнения

/// </summary>

abstract class Deposit : IComparable

{

protected string name;

protected int years;

protected double sum;

protected double rate;

/// <summary>

/// Минимальная вносимая на вклад сумма

/// </summary>

public static readonly int MIN_SUM = 50000;

/// <summary>

/// Минимальное число лет для хранения вклада

/// </summary>

public static readonly int MIN_YEARS = 1;

/// <summary>

/// Свойство для хранения ФИО

/// </summary>

public string Name

{

get { return name; }

set { name = string.IsNullOrWhiteSpace(value) ? "John Doe" : value; }

}

/// <summary>

/// Свойство для хранения и изменения количества лет для хранения вклада

/// </summary>

public int Years

{

get { return years; }

set { years = value < MIN_YEARS ? MIN_YEARS : value; }

}

/// <summary>

/// Свойство для хранения и изменения суммы вклада

/// </summary>

public double Sum

{

get { return sum; }

set { sum = value < MIN_SUM ? MIN_SUM : value; }

}

/// <summary>

/// Свойство для хранения процентной ставки вклада

/// </summary>

public double Rate

{

get { return rate; }

set { rate = value < 1.0 ? 1.0 : value; }

}

/// <summary>

/// Конструктор по умолчанию

/// </summary>

public Deposit()

{

name = "John Doe";

years = 1;

sum = 50000;

Rate = 1.0;

}

/// <summary>

/// Конструктор с параметрами

/// </summary>

/// <param name="nm"></param>

public Deposit(string t, int d, double s)

{

name = string.IsNullOrWhiteSpace(t) ? "John Doe" : t;

years = d < MIN_YEARS ? MIN_YEARS : d;

sum = s < MIN_SUM ? MIN_SUM : s;

Rate = 1.0;

}

/// <summary>

/// Конструктор копирования

/// </summary>

/// <param name="st"></param>

public Deposit(Deposit dep)

{

name = dep.name;

years = dep.years;

sum = dep.sum;

Rate = dep.Rate;

}

/// <summary>

/// Деструктор

/// </summary>

~Deposit()

{

Console.Write("Вклад удален!");

}

/// <summary>

/// Метод сравнения текущего вклада с переданным в качестве параметра

/// </summary>

/// <param name="obj">вклад для сравнения</param>

/// <returns>0, если равны; -1, если второй вклад лучше; 1 в противном случае</returns>

public int CompareTo(object obj)

{

if (obj is Deposit)

{

Deposit st = obj as Deposit;

double o1 = GetOverall(),

o2 = st.GetOverall();

return o1.CompareTo(o2); // вклады сравниваются по общему доходу

}

throw new Exception("Переданный для сравнения объект не является вкладом!");

}

/// <summary>

/// Переопределенный метод вывода информации о вкладе в виде строки

/// </summary>

/// <returns></returns>

public override string ToString()

{

return string.Format("{0}, вам одобрен вклад на {1} лет с суммой {2} рублей и процентной ставкой {3}.\n",

name, years, sum, Rate);

}

/// <summary>

/// Метод хэш-функции для реализации сравнения

/// </summary>

/// <returns></returns>

public override int GetHashCode()

{

return name.GetHashCode() + Rate.GetHashCode() +

sum.GetHashCode() + years.GetHashCode();

}

/// <summary>

/// Виртуальный метод, возвращающий общую сумму вклада

/// </summary>

/// <returns></returns>

public virtual double GetOverall()

{

return sum;

}

}

}

Листинг А2. Исходный код модуля DepositYear.cs

using System;

namespace DepositProject

{

/// <summary>

/// Класс вклада с ежегодной капитализацией, наследник базового абстрактного класса Deposit

/// </summary>

class DepositYear : Deposit

{

public static new readonly int MIN_SUM = 100000;

public static new readonly int MIN_YEARS = 1;

public new double Rate { get; } = 4.7;

public DepositYear() : base() { rate = 4.7; }

public DepositYear(string t, int d, double s) : base(t, d, s) { }

public DepositYear(DepositYear dep) : base(dep) { }

~DepositYear()

{

Console.Write("{0}, вклад с ежегодной капитализацией удален!", Name);

}

public override string ToString()

{

return string.Format("{0}, вам одобрен вклад с ежегодной капитализацией " +

"на {1} лет с суммой {2} рублей и процентной ставкой {3}.\n", Name, Years, Sum, Rate);

}

public override double GetOverall()

{

double r = Rate / 100;

return Math.Round(Sum * Math.Pow(1 + r, Years), 2);

}

}

}

Листинг А3. Исходный код модуля DepositMonth.cs

using System;

namespace DepositProject

{

/// <summary>

/// Класс вклада с ежемесячной капитализацией, наследник базового абстрактного класса Deposit

/// </summary>

class DepositMonth : Deposit

{

public static new readonly int MIN_SUM = 200000;

public static new readonly int MIN_YEARS = 2;

public new double Rate { get; } = 4.5;

public DepositMonth() : base() { rate = 4.5; }

public DepositMonth(string t, int d, double s) : base(t, d, s) { }

public DepositMonth(DepositMonth dep) : base(dep) { }

~DepositMonth()

{

Console.Write("{0}, вклад с ежемесячной капитализацией удален!", Name);

}

public override string ToString()

{

return string.Format("{0}, вам одобрен вклад с ежемесячной капитализацией " +

"на {1} месяцев с суммой {2} рублей и процентной ставкой {3}.\n", Name, Years * 12, Sum, Rate);

}

public override double GetOverall()

{

double r = Rate / 100;

double monthRate = r / 12;

double months = Years * 12;

return Math.Round(Sum * Math.Pow(1 + monthRate, months), 2);

}

}

}

Листинг А4. Исходный код модуля DepositDay.cs

using System;

namespace DepositProject

{

/// <summary>

/// Класс вклада с ежедневной капитализацией, наследник базового абстрактного класса Deposit

/// </summary>

class DepositDay : Deposit

{

public static new readonly int MIN_SUM = 500000;

public static new readonly int MIN_YEARS = 3;

public new double Rate { get; } = 4.2;

public DepositDay() : base() { rate = 4.2; }

public DepositDay(string t, int d, double s) : base(t, d, s) {}

public DepositDay(DepositDay dep) : base(dep) {}

~DepositDay()

{

Console.Write("{0}, вклад с ежедневной капитализацией удален!", Name);

}

public override string ToString()

{

return string.Format("{0}, вам одобрен вклад с ежедневной капитализацией " +

"на {1} дней с суммой {2} рублей и процентной ставкой {3}.\n", Name, Years * 365, Sum, Rate);

}

public override double GetOverall()

{

double r = Rate / 100;

double dayRate = r / 365;

int days = Years * 365;

return Math.Round(Sum * Math.Pow(1 + dayRate, days), 2);

}

}

}

Листинг А5. Исходный код модуля Form1.cs

using System;

using System.Collections.Generic;

using System.Windows.Forms;

namespace DepositProject

{

public partial class Form1 : Form

{

/// <summary>

/// Выбранный вклад

/// </summary>

Deposit mydep;

/// <summary>

/// Осуществляет настройку выбранного вклада по номеру в списке

/// </summary>

/// <param name="type">индекс в списке вкладов</param>

void OnSettings(int type)

{

// если выбран какой-то вклад

if (type < 3)

{

// сделать неактивной кнопку подбора вкладов, активной - кнопку выбора

findDepButton.Enabled = false;

calcButton.Enabled = true;

// установить настройки минимальной суммы и срока в зависимости от типа вклада

if (type == 0)

{

mydep = new DepositYear();

sumNUD.Minimum = DepositYear.MIN_SUM;

yearsNUD.Minimum = DepositYear.MIN_YEARS;

}

else if (type == 1)

{

mydep = new DepositMonth();

sumNUD.Minimum = DepositMonth.MIN_SUM;

yearsNUD.Minimum = DepositMonth.MIN_YEARS;

}

else

{

mydep = new DepositDay();

sumNUD.Minimum = DepositDay.MIN_SUM;

yearsNUD.Minimum = DepositDay.MIN_YEARS;

}

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

rateTB.Text = mydep.Rate.ToString();

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

sumNUD.Value = sumNUD.Minimum;

yearsNUD.Value = yearsNUD.Minimum;

}

// иначе (необходим подбор вклада по заданным параметрам)

else

{

// сделать активной кнопку подбора вкладов, неактивной - кнопку выбора

findDepButton.Enabled = true;

calcButton.Enabled = false;

// создать три вклада со стандартными настройками

DepositYear d1 = new DepositYear();

DepositMonth d2 = new DepositMonth();

DepositDay d3 = new DepositDay();

// создать новый список из созданных вкладов

List<Deposit> ld = new List<Deposit>() { d1, d2, d3 };

// отсортировать по возрастанию выплат

ld.Sort();

// выбрать вклад с минимальной капитализацией

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

sumNUD.Value = sumNUD.Minimum = new decimal(ld[0].Sum);

yearsNUD.Value = yearsNUD.Minimum = new decimal(ld[0].Years);

// не выводить ставку

rateTB.Text = "";

}

}

/// <summary>

/// Конструктор класса формы

/// </summary>

public Form1()

{

InitializeComponent();

depositCB.SelectedIndex = 0; // выбрать вклад с ежегодной капитализацией (первый в списке)

OnSettings(depositCB.SelectedIndex); // настроить поля формы

}

/// <summary>

/// Возникает при нажатии кнопки "Рассчитать"

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

private void calcButton_Click(object sender, EventArgs e)

{

// если ФИО не было введено, сообщить об ошибке и выйти

if (string.IsNullOrWhiteSpace(nameTB.Text))

{

MessageBox.Show("Введите ФИО!", "Ошибка",

MessageBoxButtons.OK, MessageBoxIcon.Error);

return;

}

// дописать пустую строку в текстовое поле с выводом информации

resultTB.Text += Environment.NewLine;

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

mydep.Name = nameTB.Text;

mydep.Sum = (double)sumNUD.Value;

mydep.Years = (int)yearsNUD.Value;

// вывести результат - информацию о вкладе и выплате в конце срока

resultTB.Text += mydep.ToString() + Environment.NewLine +

"Выплата по вкладу в конце указанного срока составит " +

mydep.GetOverall() + " рублей." + Environment.NewLine;

// очистить текстовое поле с ФИО

nameTB.Clear();

}

/// <summary>

/// Возникает при изменении типа вклада в выпадающем списке

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

private void depositCB_SelectedIndexChanged(object sender, EventArgs e)

{

OnSettings(depositCB.SelectedIndex);

}

/// <summary>

/// Возникает при нажатии на кнопку "Подобрать"

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

private void findDepButton_Click(object sender, EventArgs e)

{

// если ФИО не было введено, сообщить об ошибке и выйти

if (string.IsNullOrWhiteSpace(nameTB.Text))

{

MessageBox.Show("Введите ФИО!", "Ошибка",

MessageBoxButtons.OK, MessageBoxIcon.Error);

return;

}

// дописать пустую строку в текстовое поле с выводом информации

resultTB.Text += Environment.NewLine;

// записать в переменные введенные значения

string name = nameTB.Text;

double sum = (double)sumNUD.Value;

int years = (int)yearsNUD.Value;

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

DepositYear d1 = new DepositYear();

d1.Sum = sum;

d1.Years = years;

DepositMonth d2 = new DepositMonth();

d2.Sum = sum;

d2.Years = years;

DepositDay d3 = new DepositDay();

d3.Sum = sum;

d3.Years = years;

// создать новый список из созданных вкладов и отсортировать их по возрастанию выплат

List<Deposit> ld = new List<Deposit>() { d1, d2, d3 };

ld.Sort();

// выбрать вклад с максимальной капитализацией

Deposit bestDep = ld[ld.Count - 1];

string bestDepType;

double minSum; int minYears;

// получить тип вклада и его минимальные значения суммы и срока

if (bestDep.GetType().ToString().Contains("DepositY"))

{

bestDepType = "ежегодной капитализацией.";

minSum = DepositYear.MIN_SUM;

minYears = DepositYear.MIN_YEARS;

}

else if (bestDep.GetType().ToString().Contains("DepositM"))

{

bestDepType = "ежемесячной капитализацией.";

minSum = DepositMonth.MIN_SUM;

minYears = DepositMonth.MIN_YEARS;

}

else

{

bestDepType = "ежедневной капитализацией.";

minSum = DepositDay.MIN_SUM;

minYears = DepositDay.MIN_YEARS;

}

// вывод в текстовое поле с информацией типа вклада

resultTB.Text += name + ", вам подойдет вклад с " + bestDepType + Environment.NewLine;

// если заданная сумма меньше минимальной, то расчитать необходимый остаток и добавить к сумме вклада

if (minSum > bestDep.Sum)

{

double addSum = minSum - bestDep.Sum;

resultTB.Text += "Необходимо внести еще хотя бы " + addSum + " рублей." + Environment.NewLine;

bestDep.Sum += addSum;

}

// если заданный срок меньше минимального, то расчитать необходимый остаток и добавить к сроку вклада

if (minYears > bestDep.Years)

{

int addYears = minYears - bestDep.Years;

resultTB.Text += "Срок вклада должен быть увеличен как минимум на " + addYears + " лет." + Environment.NewLine;

bestDep.Years += addYears;

}

// рассчитать выплату по вкладу с учетом суммы и срока и вывести в текстовое поле с информацией

resultTB.Text += "Выплата по вкладу в конце указанного срока составит " +

bestDep.GetOverall() + " рублей." + Environment.NewLine;

// очистить текстовое поле с ФИО

nameTB.Clear();

}

}

}