Шрифт:
В литературе встречается понятие неформального, свободного (informal) протокола. На самом деле это категория, содержащая список группы методов, но не реализующая их. Все (или почти все) наследуется из одного корневого объекта, поэтому неформальные категории часто определяются для корневого класса. Иногда неформальные протоколы называют также абстрактными (abstract) протоколами.
В header-файле могут встретиться объявления методов, которые выглядят следующим образом: @interface NSObject (NSComparisonMethods) -(BOOL)isEqualTo:(id)object; -(BOOL)isLessThanOrEqualTo:(id)object; -{B00L}isLessThan:(id)object; -<BOOL}isGreaterThanOrEqualTo:(id)object; BOOLJisGreaterThan: (id)object; -(BOOL)isNotEqualTo:(id)object; -{BOOL)doesContain:(id)object; -(B00L)isLike:(NSString *)object; -(BOOL)isCaselnsensitiveLike:(NSString *)object; @end
Здесь определяется категория с именем NSComparisonMethods для класса NSObject. В этом неформальном протоколе содержится список группы методов (в данном случае — девять), которые могут быть реализованы как часть этого протокола. Неформальный протокол — это фактически просто группа методов под определенным именем. Это полезно с точки зрения документирования и модульной организации методов.
Класс, где объявляется неформальный протокол, не реализует методы в самом этом классе. В подклассе, выбранном для реализации этих методов, требуется переобъявить их в секции interface, а также реализовать один или несколько из этих методов. В отличие от формальных протоколов, компилятор не оказывает никакой помощи с неформальными протоколами: здесь нет никакой концепции подчинения или проверки компилятором.
Если объект принимает какой-либо формальный протокол, этот объект должен подчиняться всем требуемым сообщениям в этом протоколе. Это можно сделать как на этапе выполнения (runtime), так и во время компиляции. Если объект принимает неформальный протокол, то он не обязан принять все методы данного протокола (в зависимости от самого протокола). Подчинение неформальному протоколу можно сделать обязательным на этапе выполнения (с помощью respondsToSelector:), но не во время компиляции.
Примечание. Описанную выше директиву @optional (она была добавлена в Objective-C 2.0) можно использовать вместо неформальных протоколов. Она используется в нескольких классах UIKit (UIK.it — составная часть структур Cocoa Touch framework). 11.3. Составные объекты
Вы уже знаете несколько способов, позволяющих расширить определение класса с помощью таких средств, как подклассы и категории. Еще один способ — это определение класса, который содержит один или несколько объектов из других классов. Объект из этого класса называется составным (composite) объектом, поскольку он составлен из других объектов.
В качестве примера рассмотрим класс Square (квадрат), который мы определили в главе 8. Он был определен как подкласс класса Rectangle (прямоугольник), поскольку квадрат это прямоугольник с равными сторонами. Подкласс, который мы определяем наследует все переменные экземпляра и методы родительского класса. В некоторых случаях это нежелательно. Метод setWidth:andHeight: (задание ширины и высоты) класса Rectangle наследуется классом Square, но реально не относится к квадрату (хотя действует правильно). Кроме того, создавая подкласс, мы должны обеспечить правильность работы наследуемых методов, поскольку пользователи этого подкласса будут иметь к ним доступ.
Вместо подкласса можно определить новый класс, который содержит в качестве одной из своих переменных экземпляра объект из класса, который вы хотите расширить. Затем нужно определить в новом классе только те методы, которые для него подходят. В примере с классом Square можно определить Square следующим образом. @interface Square: NSObject { Rectangle *rect; } -(int) setSide: (int) s; -(int) side; (сторона) •(int) area; (площадь) -(int) perimeter; (периметр) @end
Здесь определен класс Square с четырьмя методами. В отличие от версии с подклассом, которая дает непосредственный доступ к методам класса Rectangle (setWidth:, setHeight:, setWidthiandHeight:, width и height), этих методов нет в определении для Square. Это имеет смысл, поскольку не все методы подходят для работы с квадратами.
Если мы определяем класс Square таким способом, то он становится ответственным за выделение памяти для прямоугольника (rectangle), который содержит. Например, без заметающих методов оператор Square *mySquare = [[Square alloc] init];
выделяет память для нового объекта типа Square, но не выделяет память для объекта типа Rectangle, хранящегося в его переменной экземпляра red.
Чтобы выполнить выделение памяти, требуется замещение метода init или добавление нового метода, например, initWithSide:. Этот метод может выделять память для переменной Rectangle red и задавать соответствующим образом сторону (side). Необходимо также заместить метод dealloc (как описано для класса Rectangle в главе 8), чтобы освободить память, используемую для Rectangle red, когда освобождается сам объект Square.
Определяя свои методы в классе Square, мы по-прежнему можем использовать методы класса Rectangle. Например, мы можем реализовать метод area следующим образом: -(int) area { return [rect area]; }
Реализацию остальных методов вы можете написать в качестве упражнения (см. ниже упражнение 5). Упражнения
Выполните расширение категории MathOps из программы 11.1, чтобы дополнительно включить метод invert, который возвращает дробь (Fraction), обратную получателю.
Добавьте в класс Fraction категорию с именем Comparison. В этой категории добавьте два метода в соответствии со следующими объявлениями.
– (BOOL) isEqualTo: (Fraction *) 1; -(int) compare: (Fraction *) f; Первый метод должен возвращать значение YES, если две дроби равны, и значение N0 в противном случае. Правильно сравнивайте дроби (например, при сравнении дробей 3/4 и 6/8 следует возвращать значение YES). Второй метод должен возвращать значение -1, если получатель меньше дроби, представляемой аргументом; возвращать 0, если две дроби равны; возвращать 1, если получатель больше дроби, представляемой аргументом.
Выполните расширение класса Fraction, добавив методы, которые подчиняются неформальному протоколу NSComparisonMethods, описанному з этой главе. Напишите реализацию первых шести методов из этого протокола (isEqualTo:, isLessThanOrEqualTo:, isLessThan:, isGreaterThanOrEquaHo:, isGreaterThan:,isNotEqualTo:) и выполните их тестирование.
Функции sin ,cos и tan {) включены в стандартную библиотеку Standard Library (как и scant ). Эти функции объявлены в header-файле , который вы должны импортировать в программу с помощью строки #import <math.h> Эти функции можно использовать для вычисления синуса, косинуса и тангенса аргумента типа double, выраженного в радианах. Возвращаемый результат является значением с плавающей точкой двойной точности. Например, для вычисления синуса аргумента d, где d — угол, выраженный в радианах, можно использовать следующую строку: result = sin (d); Добавьте категорию с именем Trig в класс Calculator, определенный в главе 6. Включите в эту категорию методы для вычисления синуса, косинуса и тангенса в соответствии со следующими объявлениями.
– (double) sin; -(double) cos; -(double) tan;