В. А. Основы объектного программирования на языке C# оо – технология и обучение программированию

Вид материалаДокументы

Содержание


Роль языка и среды программирования
Начала программирования. Модели
Модель структурного программирования
Модель визуального, событийно-управляемого программирования
Модель модульного программирования
Модель объектно-ориентированного программирования
Класс – это модуль
Класс – это тип данных
Развернутые и ссылочные типы
Стиль программирования
Правило «И не вздумайте!»
Правило «Never, never, never!»
Правило «Нет литералам!»
Жизненный цикл программной системы
Три закона программотехники
Не бывает некорректных систем. Каждая появляющаяся ошибка при эксплуатации системы – это следствие незнания спецификации системы
Если спецификацию можно нарушить, – она будет нарушена. Новичок (чечако) способен «подвесить» любую систему.
Метод Флойда и утверждения Assert
Расширенная архитектура приложений.
LINQ и деревья выражений
...
Полное содержание
Подобный материал:
Биллиг В.А.

Основы объектного программирования

на языке C#


ОО – технология и обучение программированию

Уже более 10 лет основной технологией разработки программных систем является ОО -технология.

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

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

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

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

Рассмотрим, как решаются эти вопросы в курсе «Основы объектного программирования на C#», который я читаю в последние годы.

Роль языка и среды программирования

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

В данном контексте понятие языка включает и среду программирования, в которую погружен язык. Поэтому в докладе неявно подразумевается, что разработка программ на C# ведется в среде Visual Studio .Net с использованием возможностей каркаса Framework .Net. Но на начальных этапах обучения программированию о среде следует говорить как можно меньше. Современные среды программирования тем и хороши, что напоминают официантов в хорошем ресторане – прекрасное обслуживание, не обращающее на себя внимания.

Многие годы лидирующую роль первого языка играл язык Паскаль. Он и сегодня сохраняет свои позиции, хотя и появились другие претенденты, среди которых назову двух – Java и C#.

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

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

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

предоставляет большой набор образцов программирования, представленных классами библиотеки Framework .Net.


Среди многих важных особенностей среды разработки Visual Studio 2008 остановлюсь лишь на архитектуре приложений. Существенно расширился набор возможных архитектурных типов построения приложений. Помимо традиционных Windows- и консольных приложений, появилась возможность построения Web-приложений. Большое внимание уделяется возможности создания повторно используемых компонентов – разрешается строить библиотеки классов, библиотеки элементов управления и библиотеки Web-элементов управления. Популярным архитектурным типом являются Web-службы, ставшие сегодня благодаря открытому стандарту одним из основных видов повторно используемых компонентов.

Начала программирования. Модели

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

Модель структурного программирования

Модель языка Паскаль с главной программой, процедурами и функциями легко реализуема консольным проектом.

Пример консольного проекта «Приветствие»

Модель визуального, событийно-управляемого программирования

Эта модель легко реализуется в Windows проекте.

Пример Windows проекта «Приветствие».

Модель модульного программирования

Эта модель реализуема с использованием библиотек классов – DLL.

Пример проектов MyMath, «Поиск и сортировка».

Модель объектно-ориентированного программирования

Эта модель поддерживается всеми средствами языка C#.

Модули и классы

Две роли классов

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

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

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

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

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

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

Язык C# допускает как классы, являющиеся типами данных, так и классы, играющие единственную роль модуля. Классы, являющиеся только модулями, и предоставляющие свои сервисы другим классам, хорошо знакомы, поскольку появляются в самых первых программах. К ним относятся, например, такие классы как Console, Convert, Math.

Развернутые и ссылочные типы

Для C# программистов важным является деление типов на значимые и ссылочные. Для значимых типов, называемых также развернутыми, значение переменной (объекта) является неотъемлемой собственностью переменной (точнее, собственностью является память, отводимая значению, само значение может изменяться). Значимый тип принято называть развернутым, подчеркивая тем самым, что значение объекта развернуто непосредственно в памяти, отводимой объекту (в стеке).

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

Ссылочные и значимые типы являются классами. Синтаксически ссылочные типы объявляются с ключевым словом class, значимые – с ключевым словом struct (структура). На структуры накладываются дополнительные ограничения, главное из которых отсутствие наследования классов. Структура может лишь наследовать интерфейсы. Арифметические типы, логический и символьный тип C# реализованы как структуры.

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

Стиль программирования

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

Правило «И не вздумайте!»

Это правило требует от программиста, чтобы каждый класс, каждый общедоступный (public)метод и поле класса сопровождались соответствующими содержательными комментариями. На языке C# для такого комментирования используются теги summary, которые помимо пояснения кода, дают подсказки при работе клиента с классом и его методами, позволяя также автоматически получить отчет, описывающий интерфейс класса. Тем самым решается важная задача самодокументироваемости программного кода.

Правило «Never, never, never!»

Это правило запрещает использовать аббревиатуры в именах класса и его public полях и методах. Все такие имена должны быть содержательными.

Правило «Нет литералам!»

Это правило требует, чтобы литеральные константы появлялись только при определении именованных констант.

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

Надежность

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

Корректность – это способность программной системы работать в строгом соответствии со своей спецификацией. Отладка – процесс, направленный на достижение корректности.

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

Почему так трудно создавать корректные и устойчивые программные системы? Все дело в сложности разрабатываемых систем.

Жизненный цикл программной системы

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

Проектирование  Разработка Развертывание и Сопровождение

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

Три закона программотехники

Первый закон (закон для разработчика)

Корректность системы – недостижима. Каждая последняя найденная ошибка является предпоследней.

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

Второй закон (закон для пользователя)

Не бывает некорректных систем. Каждая появляющаяся ошибка при эксплуатации системы – это следствие незнания спецификации системы.

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

Обратите внимание на философию, характерную для этих законов: при возникновении ошибки разработчик и пользователь должны винить себя, а не кивать друг на друга. Так что часто встречающиеся фразы: «Ох уж эта фирма Чейтософт, – вечно у них ошибки!» характеризует, мягко говоря, непрофессионализм говорящего.

Третий закон (закон чечако)

Если спецификацию можно нарушить, – она будет нарушена. Новичок (чечако) способен «подвесить» любую систему.

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

Отладка

Что должно делать для создания корректного и устойчивого программного продукта? Как минимум, необходимо:

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

отладить этот код;

предусмотреть в нем обработку исключительных ситуаций.

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

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

Нужно стараться создавать надежный код. Но без отладки пока обойтись невозможно. Роль тестеров в современном процессе разработки ПО велика.

Метод Флойда и утверждения Assert

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

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

Схема Флойда используется на практике, по крайней мере, программистами, имеющими вкус к строгим методам доказательства. Утверждения становятся частью программного текста. Само доказательство может и не проводиться: чаще всего у программиста есть уверенность в справедливости расставленных утверждений и убежденность, что при желании он мог бы провести и строгое доказательство. В C# эта схема поддерживается тем, что классы Debug и Trace имеют метод Assert, аргументом которого является утверждение. Что происходит, когда вычисление достигает соответствующей точки и вызывается метод Assert? Если истинно булево выражение в Assert, то вычисления продолжаются, не оказывая никакого влияния на нормальный ход вычислений. Если оно ложно, то корректность вычислений под сомнением, их выполнение приостанавливается и появляется окно с уведомлением о произошедшем событии.

Семейство классов С#

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

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

Пример: Проект «Семейство геометрических фигур»

Новинки

Язык C# живой, развивающийся язык программирования. В нем нет академической строгости языка Паскаль, заметны огрехи. Но в целом – это язык профессионального программирования, в каждой новой версии которого появляются новые средства, отвечающие потребностям практики. Отмечу некоторые новинки, появившиеся в C# 3.0:

Расширенная архитектура приложений.

В частности в языке, а точнее в Visual Studio 2008 появились новые типы проектов, основанные на возможностях предоставляемых технологиями WPF (Windows Presentation Foundation). WCF (Windows Communication Foundation) и WF (Windows Workflow Foundation ). Технология WPF позволяет строить новое поколение систем презентации – с новыми графическими возможностями, связыванием данных и прочими элементами, придающими презентации принципиально новые свойства. Технологии WCF и WF позволяют строить специализированные приложения и службы (Services), позволяющие приложениям обмениваться данными, используя асинхронный ввод-вывод.

LINQ и деревья выражений

В C# 3.0 встроен язык запросов к данным, что существенно облегчает работу с данными, поступающими из внешних источников. Этот языковый механизм поддерживается классами библиотеки FCL Framework .Net 3.5. Пространство System.Linq содержит классы, задающие типы, интерфейсы, стандартные операторы запроса. Пространства System.Data.Linq , System.Data.Linq.Mapping поддерживают работу с реляционными базами данных. Классы пространства System.XML.Linq поддерживают запросы к XML- данным. Новые классы DataRowComparer, DataRowExtensions, DataTableExtensions позволяют локально хранить данные, заменяя объекты DataSet ADO .Net. Классы из пространства System.Linq.Expressions позволяют работать с деревьями выражений, используемых в запросах.

Тренд в направлении функционального программирования

В C# 3.0 появились такие средства как лямбда оператор, анонимные функции – механизмы, характерные для функциональных языков программирования.

Итоги

Язык C# является одним из главных претендентов на роль первого языка программирования.

Использование языка C# и среды разработки Visual Studio .NET позволяет поддерживать обучение программированию, начиная с основ программирования, заканчивая уровнем, соответствующим разработке профессиональных программных продуктов.

Литература
  1. В. А. Биллиг «Основы программирования на C#», Изд. Интернет – Университет ИТ, БИНОМ, Москва 2006 г.
  2. В. А. Биллиг «Основы программирования на C#», Учебный курс на сайте Интернет – Университета ИТ – www.intuit.ru/
  3. В. А. Биллиг Задачник «Алгоритмы и задачи на C#», материалы в электронном виде.
  4. В. А. Биллиг «Основы объектного программирования на C# 3.0», Новая версия учебника (В процессе разработки).