Ответ 1
Изменить: добавлены изменения, потому что некоторые люди считают, что я несу ответственность за ограничения Objective-C.
Короткий ответ: вы не можете. Objective-C не имеет эквивалента Ruby mixins.
Слегка менее короткий ответ: Objective-C имеет что-то, возможно, с тем же вкусом: протоколы. Протоколы (интерфейсы на некоторых других языках) - это способ определения набора методов, который принимает эти протоколы для реализации. Однако протокол не обеспечивает реализацию. Это ограничение не позволяет использовать протоколы в качестве точного эквивалента Ruby mixins.
Еще менее короткий ответ: Тем не менее, среда выполнения Objective-C имеет открытый API, который позволяет играть с динамическими функциями языка. Затем вы выходите за пределы языка, но вы можете иметь протоколы с реализацией по умолчанию (также называемые конкретными протоколами). Ответ Владимира показывает один из способов сделать это. В этот момент мне кажется, что вы получаете Ruby mixins в порядке.
Однако я не уверен, что рекомендую это сделать. В большинстве случаев другие шаблоны соответствуют законопроекту, не играя в игры со временем выполнения. Например, у вас может быть под-объект, реализующий смешанный метод (имеет-a вместо is-a). Игра со временем выполнения в порядке, но имеет 2 недостатка:
-
Вы делаете свой код менее читабельным, так как он требует, чтобы читатели знали намного больше, чем язык. Конечно, вы можете (и должны) прокомментировать это, но помните, что любой необходимый комментарий можно рассматривать как дефект реализации.
-
Вы зависите от этой реализации языка. Конечно, платформы Apple на сегодняшний день являются наиболее распространенными для Objective-C, но не забывайте, что Cocotron или GnuStep (или Etoilé) имеют разные режимы работы, которые могут или не могут быть совместимы с Apple в этом отношении.
В качестве побочного примечания я указываю ниже, что категории не могут добавлять состояние (переменные экземпляра) в класс. Используя API runtime, вы также можете снять это ограничение. Однако это выходит за рамки этого ответа.
Длинный ответ:
Две функции Objective-C выглядят как возможные кандидаты: категории и протоколы. Категории на самом деле не являются правильным выбором, если я правильно понимаю вопрос. Правильная функция - это протокол.
Позвольте мне привести пример. Предположим, вы хотите, чтобы куча ваших классов имела определенную способность, называемую "петь". Затем вы определяете протокол:
@protocol Singer
- (void) sing;
@end
Теперь вы можете объявить, что любой из ваших собственных классов принимает протокол следующим образом:
@interface Rectangle : Shape <Singer> {
<snip>
@end
@interface Car : Vehicle <Singer> {
<snip>
@end
Объявив, что они принимают протокол, они берут на себя обязательство реализовать метод sing
. Например:
@implementation Rectangle
- (void) sing {
[self flashInBrightColors];
}
@end
@implementation Car
- (void) sing {
[self honk];
}
@end
Затем вы используете эти классы, например:
void choral(NSArray *choir) // the choir holds any kind of singer
{
id<Singer> aSinger;
for (aSinger in choir) {
[aSinger sing];
}
}
Обратите внимание, что певцам в массиве не нужно иметь общий суперкласс. Также обратите внимание, что класс может иметь только один суперкласс, но многие принятые протоколы. Обратите внимание, наконец, что проверка типов выполняется компилятором.
По сути, механизм протокола - это множественное наследование, используемое для шаблона mixin. Это множественное наследование сильно ограничено, потому что протокол не может добавлять новые переменные экземпляра в класс. Протокол описывает только общедоступный интерфейс. В отличие от модулей Ruby он не содержит реализации.
Это больше всего. Однако укажите категории.
Категория объявляется не в угловых скобках, а между скобками. Разница в том, что категория может быть определена для существующего класса для ее расширения без подклассификации. Вы даже можете сделать это для системного класса. Как вы можете себе представить, можно использовать категории для реализации чего-то похожего на mixin. И они долгое время использовались обычно как категория NSObject
(типичный корень иерархии наследования) до такой степени, что их называли "неформальными" протоколами.
Это неформально, потому что 1-й тип проверки не выполняется компилятором, а 2- реализация методов протокола является необязательной.
Сегодня нет необходимости использовать категории в качестве протоколов, особенно потому, что официальные протоколы теперь могут объявлять, что некоторые из их методов являются необязательными с ключевым словом @optional
или обязательными (по умолчанию) с помощью @required
.
Категории по-прежнему полезны для добавления определенного поведения домена к существующему классу. NSString
является общей целью для этого.
Также интересно отметить, что большинство (если не все) объектов NSObject
фактически объявлены в протоколе NSObject
. Это означает, что это не очень важно использовать NSObject
как обычный суперкласс для всех классов, хотя это все еще обычно делается по историческим причинам и хорошо... потому что для этого нет недостатка. Но некоторые системные классы, такие как NSProxy
, не являются NSObject
.