суббота, 5 декабря 2009 г.

6.18. Размышления об объектах: программирование классов для моделирования лифта

В разделах «Размышления об объектах» глав с 1 по 5 мы познакомились с основами объектного ориентирования и провели объектно-ориентированное проектирование модели лифта. В рамках главы 6 мы познакомились с де­талями программирования и использования классов на языке С++. К насто­ящему моменту вы готовы (и, возможно, страстно желаете) начать програм­мирование вашей модели лифта.

Лабораторное задание 5 по лифту
       Для каждого класса, который вы выделили в разделах «Размышления об объектах» глав со 2 по 5, напишите соответствующие определения классов на С++. Для каждого класса включите как заголовочный файл, так и исходный файл определения функций-элементов.
        Напишите программу драйвера, который проверяет каждый из этих классов и пытается запустить на выполнение полную модель лифта. ПРЕДУПРЕЖДЕНИЕ: возможно, вам придется подождать, пока вы не изучите главу 7 «Классы: часть II» , прежде чем вы будете в состоянии создать приемлемо работающую версию вашей модели. Так что будьте терпеливы и реализуйте только те части модели лифта, для которых вам хватает знаний, приобретенных в главе 6. В главе 7 вы узнаете о композиции, т.е. о создании классов, которые содержат другие клас­сы в качестве элементов; эта техника могла бы помочь вам представить, например, объект кнопку внутри лифта как элемент лифта. Кроме того, в главе 7 вы узнаете, как динамически создавать и уничтожать объекты с помощью new и delete; это поможет вам создавать новые объекты пассажиров, когда они должны появиться в модели, и унич­тожать эти объекты пассажиров, когда они покидают модель (после выхода пассажира из лифта).
        Для первой версии вашей модели спроектируйте только простой вывод, ориентированный на текст, который отобразит сообщения о каждом существенном событии, которое будет происходить. Ваши сообщения могли бы включать такие строки, как «Пассажир 1 прибывает на Этаж 1», «Пассажир 1 нажимает Кнопку «Вверх» Этажа 1», «Лифт прибы­вает на Этаж 1», «Пассажир 1 входит в Лифт» и т.д. Заметим, что мы рекомендуем вам печатать с заглавной буквы те слова каждого сообщения, которые представляют объекты вашей модели. Заметим также, что вы можете предпочесть отложить эту часть лабораторного занятия до прочтения главы 7.
        Наиболее амбициозные студенты захотят использовать анимизирован- ный графический выход, который показывает, как модель лифта дви­жется на экране вверх и вниз.
Резюме
       Структуры — это совокупности типов данных, построенные с исполь­зованием данных других типов.
       Ключевое слово struct начинает определение структуры. Тело струк­туры заключается в фигурные скобки ({ и }). Каждое определение структуры должно заканчиваться точкой с запятой.
       Тэг (имя-этикетка) структуры может использоваться для объявления переменных данного типа структуры.
       Определения структуры не резервируют место в памяти; они создают новые типы данных, которые используются далее для объявления пере­менных.
       Элементы структуры или класса доступны при использовании операций доступа к элементу — операции точка (.) и операции стрелка (->).
Операция точка обеспечивает доступ к элементу структуры посредством имени переменной объекта или ссылки на объект. Операция стрелка обеспечивает доступ к элементу структуры посредством указателя на объект.
       Препятствиями к созданию новых типов данных с помощью struct являются: возможность существования данных, не имеющих началь­ных значений; возможность существования данных с неправильными начальными значениями; необходимость изменения всех программ, ис­пользующих struct, при изменении реализации struct; отсутствие средств защиты, гарантирующих, что данные не содержат несогласо­ванных значений.
        Классы предоставляют программисту возможность моделировать объ­екты с атрибутами и различными вариантами поведения. Типы классов можно определять в С++ , используя ключевые слова class и struct, но обычно для этих целей используется ключевое слово class.
       Имя класса можно использовать для объявления объектов этого класса.
        Определения класса начинаются с ключевого слова class. Тело опре­деления класса заключается в фигурные скобки ({ и }). Определение класса заканчивается точкой с запятой.
        Любые данные-элементы или функции-элементы, объявленные в классе после метки public:, являются открытыми и видимыми для любой функции, для которой доступен объект данного класса.
       Любые данные-элементы или функции-элементы, объявленные в классе после метки private:, являются закрытыми и видимыми только друзьям и другим элементам класса.
       Метки доступа к элементу всегда заканчиваются двоеточием (:) и могут появляться в определении класса неоднократно и в любой последова­тельности.
        Закрытые данные недоступны вне класса.
        Говорят, что реализация класса скрыта от его клиентов.
       Конструктор — это специальная функция-элемент с тем же именем, что и класс, которая используется для инициализации элементов объ­екта класса. Конструкторы вызываются при создании объектов соот­ветствующих классов.
        Функция с тем же именем, что и класс, но предваряемая знаком тильды (-), называется деструктором.
       Набор открытых функций-элементов класса называется интерфейсом класса или открытым интерфейсом.
        Если функция-элемент определена вне определения данного класса, имя функции предваряется именем класса и бинарной операцией раз­решения области действия (::).
        Функции-элементы, определенные с использованием операции разре­шения области действия вне определения класса, имеют областью дей­ствия этот класс.
        Функции-элементы, определенные в определении класса, автоматичес­ки встраиваются inline. Компилятор сохраняет право не встраивать любую функцию.
       Вызов функций-элементов более компактен, чем вызов функций в про­цедурном программировании, потому, что большинство данных, исполь­зуемых в функции-элементе, непосредственно доступно ей в объекте.
       Внутри области действия класс на элементы класса можно ссылаться просто с помощью их имен. Вне области действия класс на элементы класса можно ссылаться посредством либо имени объекта, либо ссылки на объект, либо указателя на объект.
       Для доступа к элементам класса используются операции . и ->.
       Фундаментальный принцип разработки хорошего программного обес­печения состоит в отделении интерфейса от реализации для улучшения модифицируемости программы.
        Определения класса обычно помещаются в заголовочных файлах, а оп­ределения функций-элементов — в файлах исходных кодов, имеющих такое же имя.
        По умолчанию способ доступа в классах — private, так что все эле­менты после заголовка класса и до первого спецификатора доступа считаются закрытыми.
       Открытые элементы класса предоставляют набор услуг, которыми класс обеспечивает своих клиентов.
       Доступом к закрытым данным класса можно эффективно управлять с помощью функций-элементов, называемых функциями доступа. Если класс хочет позволить клиентам читать закрытые данные, он может обеспечить это с помощью функций чтения «get». Изменять закрытые данные класс может позволить своим клиентам с помощью функций записи «set».
       Данные-элементы класса обычно делаются закрытыми, а функции-эле­менты — открытыми. Некоторые функции-элементы могут быть за­крытыми и играть роль функций-утилит для других функций класса.
       Данным-элементам класса нельзя задавать начальные значения в оп­ределении класса. Они должны получать начальные значения в кон­структоре или же их значения могут быть установлены после создания соответствующего объекта.
        Конструкторы можно перегружать.
        После того, как объект класса получил соответствующие начальные значения, все функции-элементы, которые манипулируют объектом, должны давать гарантию, что объект остается в непротиворечивом со­стоянии.
       В объявлении объекта класса могут быть предусмотрены начальные значения. Эти начальные значения передаются конструктору класса.
        Конструкторы могут определять аргументы по умолчанию.
       Конструкторы не могут ни указывать тип возвращаемых значений, ни пытаться возвращать значения.

       Если для класса не определено никаких конструкторов, компилятор создает конструктор с умолчанием. Такой конструктор не выполняет присваивания никаких начальных значений, так что после создания объекта нет гарантий, что он находится в непротиворечивом состоянии.
       Деструктор автоматического объекта вызывается при выходе из области действия объекта. Сам по себе деструктор в действительности не унич­тожает объект, но он выполняет подготовку его уничтожения перед тем, как система использует память объекта, которую ранее занимал объект.
       Деструкторы не получают параметров и не возвращают значений. Класс может иметь только один деструктор.
       Операция присваивания (=) используется для присваивания объекта другому объекту того же типа. Такое присваивание обычно выполня­ется с помощью побитового копирования по умолчанию. Побитовое ко­пирование не является идеальным для всех классов.

Терминология
class private: protected: public:
абстрактный тип данных атрибут
бинарная операция разрешения области действия (::) встраиваемая inline функция-элемент вхождение в область действия выход из области действия глобальный объект данные-элементы деструктор заголовочный файл закрытые элементы инициализация объекта класса инкапсуляция интерфейс класса исходный файл класс
клиент класса конструктор
конструктор с умолчанием набор функций непротиворечивое состояние данных-элементов нестатический локальный объект область действия класс область действия файл объект
объектно-ориентированное программирование (ООП) операция доступа к элементу класса (.)
операция разрешения области действия (::) операция ссылки & определение класса открытые элементы открытый интерфейс класса побитовая копия поведение
повторно используемый код повторное использование программ­ного обеспечения предикатная функция принцип наименьших привилегий процедурное программирование расширяемость реализация класса скрытие информации сообщение
спецификаторы доступа к элементам список инициализации элементов статический локальный объект тильда (-) в имени деструктора тип данных
тип, определяемый пользователем управление доступом к элементам ускоренная разработка приложений (RAD)
услуги класса функция доступа функция записи (set) функция запроса функция не элемент класса функция чтения (get) функция-утилита функция-элемент экземпляр объекта класса

Типичные ошибки программирования
                 Забывается точка с запятой в конце определения класса (или струк­туры).
                 Попытка явно присвоить начальное значение данным-элементам в определении класса.
                 Попытка перегрузить функцию-элемент класса с помощью функции не из области действия этого класса.
                 Попытка с помощью функции, не являющейся элементом опреде­ленного класса (или другом этого класса) получить доступ к эле­ментам этого класса.
                 Попытка объявить тип возвращаемого значения для конструктора или возвратить значение из конструктора.
                 Указание начальных значений по умолчанию для одной и той же функции-элемента как в заголовочном файле, так и в описании функции-элемента.
                 Попытки передать аргументы деструктору, вернуть значения из де­структора или перегрузить деструктор.
                 Конструктор может вызывать другие функции-элементы класса такие, как функции записи и чтения. Но поскольку конструктор инициализирует объект, данные-элементы могут в этот момент еще не быть в непротиворечивом состоянии. Использование данных-эле- ментов до того, как они получили соответствующие начальные зна­чения, может вызвать ошибки.
Хороший стиль программирования
                  Используйте при определении класса каждый спецификатор доступа к элементам только один раз, что сделает программу более ясной и простотой для чтения. Размещайте первыми элементы public, яв­ляющиеся общедоступными.
                 Используйте директивы препроцессора #ifndef, #define и #endif для того, чтобы избежать включения в программу заголовочных файлов более одного раза.
                 Используйте имя заголовочного файла с символом подчеркивания вместо точки в директивах препроцессора #ifndef и #define заголо­вочного файла.
                 Если вы намереваетесь сначала перечислить в определении класса закрытые элементы, используйте явно метку private, несмотря на то, что private предполагается по умолчанию. Это облегчит чтение программы. Но мы предпочитаем первым в определении класса по­мещать список открытой части public.
                 Несмотря на то, что спецификаторы public и private могут повто­ряться и чередоваться, перечисляйте сначала все элементы класса открытой группы, а затем все элементы закрытой группы. Это кон- центрирует внимание клиентов класса в большей степени на его открытом интерфейсе, чем на реализации класса.
                  Использование спецификаторов доступа к элементам public, pro­tected и private лишь по одному разу в любом определении класса позволяет избежать путаницы.
                 В соответствующих случаях (почти всегда) предусматривайте кон­структор для уверенности в том, что каждый объект получил со­ответствующие, имеющие смысл начальные значения.
                  Каждая функция-элемент (или дружественная функция), которая изменяет исходные данные-элементы, должна гарантировать, что данные остаются в не противоречащем друг другу согласованном состоянии.
                 Объявляйте аргументы функции по умолчанию только в прототипе функции внутри определения класса в заголовочном файле.
                 Функции-элементы, которые записывают значения закрытых дан­ных, должны проверять правильность предполагаемых новых зна­чений; если они неправильные то эти функции должны установить закрытые данные-элементы в соответствующее им непротиворечивое состояние.
                 Никогда не возвращайте из открытой функции-элемента неконстант­ную ссылку (или указатель) на закрытый элемент данных. Возвра­щение такой ссылки нарушает инкапсуляцию класса.
Советы по повышению эффективности
                  Обычно структуры передаются вызовом по значению. Чтобы избе­жать накладных расходов на копирование структуры, передавайте структуры вызовом по ссылке.
                 Чтобы избежать накладных расходов вызова по значению и вдобавок получить выгоды защиты исходных данных от изменения, переда­вайте аргументы большого размера как ссылки const.
                 Описание небольших функций-элементов внутри определения класса автоматически встраивает функцию-элемент inline (если компилятор решит делать это). Это может улучшить производительность, но не способствует улучшению качества проектирования программного обеспечения.
                 Передача объекта вызовом по значению хороша с точки зрения без­опасности, поскольку вызываемая функция не имеет доступа к ис­ходному объекту, но вызов по значению может ухудшить произво­дительность в случае создания копии большого объекта. Объект может быть передан вызовом по ссылке путем передачи либо ука­зателя, либо ссылки на объект. Вызов по ссылке способствует хо­рошей производительности, но с точки зрения безопасности хуже предыдущего, поскольку функции предоставлен доступ к исходному объекту. Безопасной альтернативой является вызов со ссылкой const.
Замечания по технике программирования
                 Важно писать программы, которые легко понимать и поддерживать. Изменения являются скорее правилом, чем исключением. Програм­мисты должны предвидеть, что их коды будут изменяться. Как мы увидим, классы способствуют модифицируемости программ.
                 Клиенты класса используют класс, не зная внутренних деталей его реализации. Если реализация класса изменяется (например, с целью улучшения производительности), интерфейс класса остается неиз­менным и исходный код клиента класса не требует изменений. Это значительно упрощает модификацию систем.
                  Функции-элементы обычно короче, чем обычные функции в про­граммах без объектной ориентации, потому что достоверность дан­ных, хранимых в данных-элементах, идеально проверена кон­структором и функциями-элементами, которые сохраняют новые данные.
                  Клиенты имеют доступ к интерфейсу класса, но не имеют доступа к реализации класса.
                  Объявление функций-элементов внутри определения класса и опи­сание функций-элементов вне этого определения отделяет интерфейс класса от его реализации. Это способствует высокому качеству раз­работки программного обеспечения.
                 Использование принципов объектно-ориентированного программи­рования часто может упростить вызовы функции за счет уменьше­ния числа передаваемых параметров. Это достоинство объектно-ори­ентированного программирования проистекает из того факта, что инкапсуляция данных-элементов и функций-элементов внутри объ­екта дает функциям-элементам право прямого доступа к данным- элементам.
                 Помещайте объявление класса в заголовочный файл, чтобы оно было доступно любому клиенту, который захочет использовать класс. Это формирует открытый интерфейс класса. Помещайте определения функций-элементов класса в исходный файл. Это формирует реа­лизацию класса.
                  Клиенты класса не нуждаются в доступе к исходному коду класса для того, чтобы использовать класс. Однако, клиенты должны иметь возможность связаться с объектным кодом класса.
                 Информация, важная для интерфейса класса, должна включаться в заголовочный файл. Информация, которая будет использоваться только внутри класса и которая не является необходимой для кли­ентов класса, должна включаться в неоглашаемый исходный файл. Это еще один пример принципа наименьших привилегий.
                  С++ способствует созданию программ, не зависящих от реализации. Если, изменяется реализация класса, используемого программой, не зависящей от реализации, то код этой программы не требует изменения, но может потребоваться его перекомпиляция.
                 Делайте все данные-элементы класса закрытыми. Используйте от­крытые функции-элементы для задания и получения значений за­крытых данных-элементов. Такая архитектура помогает скрыть ре­ализацию класса от его клиентов, что снижает число ошибок и улучшает модифицируемость программ.
                  Разработчики классов используют доступ типа public, protected или private, чтобы обеспечить скрытие информации и принцип наимень­ших привилегий.
                  Разработчик класса не обязательно должен снабжать каждый эле­мент закрытых данных функциями get и set; такие возможности должны быть обеспечены, только тогда, когда это имеет смысл, и лишь после тщательного обдумывания разработчиком класса.
                  Задание данных-элементов класса закрытыми, а функций-элементов класса открытыми облегчает отладку, так как проблемы с манипу­ляциями данных локализуются в рамках либо функций-элементов класса, либо друзей класса.
                  Функции-элементы можно разбить на ряд категорий: функции, ко­торые читают и возвращают значения закрытых данных-элементов; функции, которые устанавливают значения закрытых данных-эле­ментов; функции, которые реализуют возможности класса; функ­ции, которые выполняют для класса различные вспомогательные операции, такие, как задание начальных значений объектам класса, присваивания объектам класса, преобразования между классами и встроенными типами или между классами и другими классами, вы­деление памяти для объектов класса.
                  Если функция-элемент класса уже обеспечивает все или часть функ­циональных возможностей, требуемых конструктором (или другой функцией-элементом), вызывайте эту функцию-элемент из кон­структора (или другой функции-элемента). Это упрощает сопровож­дение программы и уменьшает вероятность ошибки при изменении реализации кода.
                  Задание закрытых данных-элементов и управление доступом к ним, особенно доступом к записи данных-элементов, посредством откры­тых функций-элементов помогает гарантировать целостность дан­ных.
                  Доступ к закрытым данным посредством функций записи и чтения не только защищает данные-элементы от присваивания им непра­вильных значений, но и отделяет клиентов класса от внутреннего представления данных-элементов. Таким образом, если внутрен­нее представление этих данных по каким-либо причинам (обычно из-за требований сокращения объема памяти или повышения про­изводительности) изменяется, достаточно изменить только функ- ции-элементы, а клиентам не требуется вносить никаких изме­нений, пока остается неизменным интерфейс функций-элементов. Однако, возможно, потребуется перекомпиляция клиентов данно­го класса.
Упражнения для самопроверки
6.1. Заполнить пробелы в следующих утверждениях:
          Ключевое слово ___________  начинает определение структуры.
          Элементы класса доступны посредством операции_______________ в со­четании с объектом класса или посредством операции ___________________________________________________  в со­четании с указателем на объект класса.
         Элементы класса, указанные как__________ , доступны только функ­циям-элементам класса и друзьям класса.
                             является специальной функцией-элементом, используемой
для задания начальных значений элементам данных класса.
         По умолчанию доступ к элементам класса — _______________ .
         Функция _________  используется для присваивания значений за­крытым данным-элементам класса.
                                               можно использовать для присваивания объекта класса
другому объекту того же класса.
           Функции-элементы класса обычно делаются _______________  типа, а
данные-элементы — ____________  типа.
        Функция__________ используется для получения значений закры­тых данных класса.
j) Набор открытых функций-элементов класса рассматривается как                 класса.
к) Говорят, что реализация класса скрыта от его клиентов или
1) Для введения определения класса можно использовать ключевые слова и            .
т) Элементы класса, указанные как _______________  , доступны везде в
области действия объекта класса.
6.2. Найдите ошибку (или ошибки) в каждом из следующих пунктов и объясните, как их исправить.
         Допустим, что в классе Time объявлен следующий прототип.
void ~Time(int);
         Следующий фрагмент является частью определения класса Time.
class Time { public;
// прототипы функций private:
int hour = 0; int minute = 0; int second = 0;
} ;
        Допустим, что в классе Employee объявлен следующий прототип.
int Employee(const char *, const char *);
Ответы на упражнения для самопроверки
                  a) struct, b) точка (.), стрелка (->). с) private, d) Конструктор. e)pri- vate. f) записи «set», g) Поэлементное копирование по умолчанию (с помощью операции присваивания), h) открытого, закрытого, i) чтения «get», j) интерфейс, к) инкапсулирована. 1) class, struct, m) public.
                 а) Ошибка: Деструкторы не могут возвращать значения или при­нимать аргументы.
Исправление: переместите тип void возвращаемого значения и пара­метр int из определения.
          Ошибка: элементы не могут явно получать начальные значения в определении класса.
Исправление: уберите явное задание начальных значений из опре­деления класса и задавайте начальные значения элементов в кон­структоре.
         Ошибка: конструкторы не могут возвращать значения. Исправление: переместите тип int возвращаемого значения из объ­явления.
Упражнения
                  Каково назначение операции разрешения области действия?
                  Сравните и сопоставьте нотацию struct и class в С++.
                  Создайте конструктор, способный использовать текущее время, да­ваемое функцией time(), объявленной в заголовочном файле time.h стандартной библиотеки С, чтобы задавать начальные значения объ­екту класса Time.
                  Создайте класс с именем Complex для выполнения арифметических действий с комплексными числами. Напишите программу драйвера для проверки вашего класса.
Комплексные числа имеют форму
realPart + imaginaryPart *j где j — квадратный корень из -1.
Используйте переменные с плавающей запятой для представления закрытых данных этого класса. Создайте функцию конструктор, ко­торая позволяет объекту этого класса принимать начальные значе­ния при его объявлении. Создайте открытые функции-элементы для каждого из следующих пунктов:
         Сложение двух комплексных чисел: отдельно складываются дей­ствительные и мнимые части.
          Вычитание двух комплексных чисел: действительная часть пра­вого операнда вычитается из действительной части левого операнда, а мнимая часть правого операнда вычитается из мнимой части ле­вого операнда.
        Печать комплексных чисел в форме (а, Ь), где а — действительная часть, a b — мнимая часть.
                  Создайте класс по имени Rational для выполнения арифметических действий с дробями. Напишите программу драйвера для проверки вашего класса.
Используйте целые переменные для представления закрытых дан­ных класса — числителя и знаменателя. Создайте функцию кон­структор, которая позволяет объекту этого класса принимать на­чальные значения при его объявлении. Конструктор должен содержать значения по умолчанию на случай отсутствия заданных начальных значений и должен хранить дроби в сокращенном виде (т.е. дробь 2/4 должна храниться в объекте как 1 в числителе и 2 в знаменателе). Создайте открытые функции-элементы для каждого из следующих случаев:
          Сложение двух чисел Rational. Результат должен храниться в сокращенной форме.
          Вычитание двух чисел Rational. Результат должен храниться в сокращенной форме.
        Перемножение двух чисел Rational. Результат должен храниться в сокращенной форме.
         Деление двух чисел Rational. Результат должен храниться в со­кращенной форме.
         Печать чисел Rational в форме а / Ь, где а — числитель, a b — знаменатель.
         Печать чисел Rational в форме с плавающей точкой.
                 Модифицируйте класс Time на рис. 6.10 так, чтобы включить функ- цию-элемент tick, которая дает приращение времени, хранящегося в объекте Time, равное одной секунде. Объект Time должен всегда находиться в непротиворечивом состоянии. Напишите программу- драйвер для проверки функции-элемента tick в цикле, которая пе­чатала бы время в стандартном формате на каждой итерации цикла и иллюстрировала правильную работу функции-элемента tick. Удос­товерьтесь в правильности работы в следующих случаях:
         Приращение с переходом в следующую минуту.
          Приращение с переходом в следующий час.
        Приращение с переходом в следующий день (т.е. от 11:59:59 РМ к 12:00:00 AM).
                 Модифицируйте класс Date на рис.6.12 так, чтобы выполнить про­верку ошибки в списке начальных значений для данных-элементов month, day и year. Кроме того, создайте функцию-элемент nextDay, которая бы увеличивала день на единицу. Объект Date должен всег­да находиться в непротиворечивом состоянии. Напишите програм- му-драйвер, проверяющую функцию nextDay в цикле и печатающую время в стандартном формате на каждой итерации цикла, чтобы проиллюстрировать правильную работу функции nextDay. Удосто­верьтесь в правильности работы в следующих случаях:
         Приращение с переходом в следующий месяц.
          Приращение с переходом в следующий год.
                  Объедините модифицированный класс Time упражнения 6.8 и мо­дифицированный класс Date упражнения 6.9 в один класс по имени DateAndTime (в главе 9 мы обсудим наследование, которое позволит нам быстро решить эту задачу без изменения существующих опре­делений классов). Модифицируйте функцию tick так, чтобы вызы­вать функцию nextDay, если время получает приращение с перехо­дом на следующий день. Модифицируйте функции PrintStandard и PrintMilitary, чтобы выводить в добавление к времени еще и дату. Напишите программу-драйвер, проверяющую новый класс DateAndTime. Особо проверьте приращение времени с переходом на следующий день.
                  Модифицируйте набор функций в программе на рис. 6.10 так, чтобы возвращать ошибочные значения в случае попытки задать непра­вильные значения данным-элементам объекта класса Time.
                  Создайте класс Rectangle (прямоугольник). Класс имеет атрибуты length (длина) и width (ширина), каждый из которых по умолчанию равен 1. Он имеет функции-элементы, которые вычисляют периметр (perimeter) и площадь (area) прямоугольника. Он имеет функции записи и чтения как для length, так и для width. Функции записи должны проверять, что length и width — числа с плавающей за­пятой, находящиеся в пределах от 0.0 до 20.0.
                  Создайте более изощренный, чем в упражнении 6.12, класс Rectan­gle. Этот класс хранит только декартовы координаты четырех углов прямоугольника. Конструктор вызывает набор функций, которые принимают четыре группы координат и проверяют, чтобы каждая из координат х и у находилась в первом квадранте, в диапазоне от 0.0 до 20.0. Это множество функций должно также проверять, что переданные координаты действительно определяют прямоугольник. Должны быть предусмотрены функции-элементы, вычисляющие length, width, perimeter, и area. Длиной должно считаться большее из двух измерений. Включите предикатную функцию square, кото­рая определяла бы, не является ли прямоугольник квадратом.
                  Модифицируйте класс Rectangle в упражнении 6.13 так, чтобы включить в него функцию draw, которая изображает прямоугольник внутри окна 25 на 25, перекрывающего часть первого квадранта, в котором находится прямоугольник. Включите функцию setFill- Character, чтобы задавать символ, которым будет заполняться пря­моугольник внутри. Включите функцию setPerimeterCharacter, чтобы задавать символ, которым будут печататься границы прямо­угольника. Если вы войдете во вкус, вы можете включить функции масштабирования размера прямоугольника, его вращения и пере­мещения в пределах первого квадранта.
                  Создайте класс Hugelnteger, который использует массив из 40 эле­ментов для хранения целых чисел вплоть до больших целых, со­держащих по 40 цифр. Создайте функции-элементы inputHugelnteger, outputHugelnteger, addHugelntegers и substrac- tHugelntegers для ввода, вывода, сложения и вычитания этих боль­ших целых. Для сравнения объектов Hugelnteger создайте функции isEqualTo, isNotEqualTo, isGreaterThan, isLessThan, is- GreaterThanOrEqualTo, isLessThanOrEqualToкаждая из них яв­ляется предикатной функцией, которая просто возвращает 1 (исти­на), если соответствующее соотношение между двумя большими целыми выполняется, и О (ложь) если оно не выполняется. Со­здайте предикатную функцию isZero. Если вы войдете во вкус, под­готовьте также функции-элементы multiplayHugelntegers, divide- Hugelntegers и modulusHugelntegers.
6.16. Создайте класс TicTacToe, который предоставит вам возможность написать полную программу для игры в тик-так-тоу. Класс содержит как закрытые данные двумерный массив целых чисел 3 на 3, опи­сывающий доску для игры. Конструктор должен присваивать ну­левые начальные значения всем пустым полям. Играют два игрока. Помещайте 1 в клетку, указываемую при перемещении первым иг­роком, и 2 в клетку, указываемую вторым игроком. Каждое пере­мещение допустимо только на пустую клетку. После каждого пере­мещения определяйте, не выиграна ли игра или не получилась ли ничья. Если вы вошли во вкус, модифицируйте вашу программу так, чтобы компьютер выполнял перемещения за одного из игроков автоматически. При этом позвольте игроку указывать, хотел бы он или она ходить первым или вторым. Если же вы почувствовали исключительное увлечение, развивайте программу так, чтобы играть в трехмерный тик-так-тоу на доске 4 на 4 на 4 (Предупреждение: это чрезвычайно сложный проект, который может потребовать многонедельных усилий!).

Комментариев нет:

Отправить комментарий