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

         

Многоугольники


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

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

indexing description: "Многоугольники с произвольным числом вершин" class POLYGON creation ... feature -- Доступ count: INTEGER -- Число вершин perimeter: REAL is -- Длина периметра do ... end feature -- Преобразование display is -- Вывод многоугольника на экран. do ... end rotate (center: POINT; angle: REAL) is -- Поворот на угол angle вокруг точки center. do ... См. далее ... end translate (a, b: REAL) is -- Сдвиг на a по горизонтали, на b по вертикали. do ... end ... Объявления других компонентов ... feature {NONE} -- Реализация vertices: LINKED_LIST [POINT] -- Список вершин многоугольника invariant same_count_as_implementation: count = vertices.count at_least_three: count >= 3 -- У многоугольника не менее трех вершин (см. упражнение У14.2) end

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

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

rotate (center: POINT; angle: REAL) is -- Поворот вокруг точки center на угол angle. do from vertices.start until vertices.after loop vertices.item.rotate (center, angle) vertices.forth end end

Чтобы понять эту процедуру заметим, что компонент item из LINKED_LIST возвращает значение текущего элемента списка. Поскольку vertices имеют тип LINKED_LIST [POINT], то vertices.item обозначает точку, к которой можно применить процедуру поворота rotate, определенную для класса POINT в предыдущей лекции.
Это вполне корректно и достаточно общепринято - давать одно и то же имя (в данном случае rotate), компонентам разных классов, поскольку результирующее множество каждого из них имеет свой явно определенный тип. (Это ОО-форма перегрузки.)

Более важна для наших целей процедура вычисления периметра многоугольника. Единственный способ вычислить периметр многоугольника - это в цикле пройти по всем его вершинам и просуммировать длины всех ребер. Вот возможная реализация процедуры perimeter:

perimeter: REAL is -- Сумма длин ребер local this, previous: POINT do from vertices.start; this := vertices.item check not vertices.after end -- Следствие условия at_least_three until vertices.is_last loop previous := this vertices.forth this := vertices.item Result := Result + this.distance (previous) end Result := Result + this.distance (vertices.first) end

В этом цикле просто последовательно складываются расстояния между соседними вершинами. Функция distance была определена в классе POINT. Значение Result, возвращаемое этой функцией, при инициализации получает значение 0. Из класса LINKED_LIST используются следующие компоненты: first дает первый элемент списка, start сдвигает курсор, на этот первый элемент, forth передвигает его на следующий, item выдает значение элемента под курсором, is_last определяет, является ли текущий элемент последним, after узнает, что курсор оказался за последним элементом. Как указано в команде check инвариант at_least_three обеспечивает правильное начало и завершение цикла. Он стартует в состоянии not after, в котором элемент vertices.item определен. Допустимо применение forth один или более раз, что, в конце концов, приведет в состояние, удовлетворяющее условию выхода из цикла is_last.


Содержание раздела