Objective C поддерживает Mixin как Ruby?

В Ruby существуют Модули, и можно расширить класс путем "смешивания - в" модуле.

module MyModule
  def printone
    print "one" 
  end
end

class MyClass
  include MyModule
end

theOne = MyClass.new
theOne.printone 
>> one

В Objective C я нахожу, что у меня есть ряд общепринятых методик, которые я хочу, чтобы много Классов "наследовали". Что другие пути я могу достигнуть этого, не создавая общий класс и получив все из того общего класса?

26
задан Paul Brit 2 November 2013 в 22:02
поделиться

1 ответ

Правка: изменения добавлены, потому что некоторые люди считают меня ответственным за ограничения Objective-C.

Короткий ответ: вы не можете. Objective-C не имеет эквивалента Ruby mixins.

Чуть менее короткий ответ: В Objective-C есть нечто, имеющее, возможно, такой же вкус: протоколы. Протоколы (интерфейсы в некоторых других языках) - это способ определить набор методов, которые класс, принявший этот протокол, обязуется реализовать. Однако протокол не предоставляет реализацию. Это ограничение не позволяет использовать протоколы в качестве точного эквивалента миксинов Ruby.

Еще менее короткий ответ: Однако среда выполнения Objective-C имеет открытый API, который позволяет вам играть с динамическими возможностями языка. Тогда вы выходите за пределы языка, но у вас могут быть протоколы с реализацией по умолчанию (также называемые конкретными протоколами). В ответе Владимира показан один из способов сделать это. На этом этапе, как мне кажется, вы получите Ruby mixins.

Однако я не уверен, что рекомендую делать это. В большинстве случаев другие паттерны подходят для этого, не играя в игры со временем выполнения. Например, вы можете иметь подобъект, реализующий метод mixed-in (has-a вместо is-a). Игра с временем выполнения - это хорошо, но имеет два недостатка:

  • Вы делаете свой код менее читабельным, поскольку это требует от читателя знания гораздо большего, чем язык. Конечно, вы можете (и должны) комментировать его, но помните, что любой необходимый комментарий может быть расценен как дефект реализации.

  • Вы зависите от той реализации языка. Конечно, платформы 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.

28
ответ дан 28 November 2019 в 06:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: