Редактировать : изменения добавлены, потому что некоторые люди считают, что я несу ответственность за ограничения Objective-C.
Краткий ответ : вы не можете. Objective-C не имеет эквивалента Ruby Mixins.
Чуть менее краткий ответ : Objective-C действительно имеет что-то похожее: протоколы. Протоколы (Интерфейсы в некоторых других языках) - это способ определения набора методов, которые класс использует для реализации протоколов. Протокол не обеспечивает реализацию, хотя. Это ограничение не позволяет использовать протоколы в качестве точного эквивалента миксинов Ruby.
Еще менее краткий ответ: Однако среда выполнения Objective C имеет открытый API, который позволяет вам играть с динамическими функциями языка. Затем вы выходите за пределы языка, но у вас могут быть протоколы с реализациями по умолчанию (также называемыми конкретными протоколами). Ответ Владимира показывает один из способов сделать это. В этот момент мне кажется, что с Ruby mixins все в порядке.
Однако я не уверен, что рекомендовал бы это сделать. В большинстве случаев другие шаблоны отвечают всем требованиям, не играя в игры со временем выполнения. Например, у вас может быть подобъект, реализующий смешанный метод ( has-a вместо is-a ). Игра со временем выполнения нормальная, но имеет 2 недостатка:
Вы делаете свой код менее читабельным, так как читатели должны знать намного больше, чем язык. Конечно, вы можете (и должны) это комментировать, но помните, что любой необходимый комментарий можно рассматривать как дефект реализации.
Вы зависите от этой реализации языка. Конечно, платформы Apple на сегодняшний день являются наиболее распространенными для Objective-C, но не забывайте, Cocotron или GnuStep (или Etoilé), которые имеют разные среды выполнения, которые могут быть совместимы или не совместимы с Apple в этом отношении.
В качестве примечания ниже я утверждаю, что категории не могут добавлять состояние (переменные экземпляра) в класс. Используя API времени выполнения, вы также можете снять это ограничение. Однако это выходит за рамки этого ответа.
Длинный ответ:
Две возможности 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
.