Objective-C prend-il en charge Mixin comme Ruby ?
-
21-09-2019 - |
Question
Dans Ruby, il y a des modules et vous pouvez étendre une classe en "mélangant" le module.
module MyModule
def printone
print "one"
end
end
class MyClass
include MyModule
end
theOne = MyClass.new
theOne.printone
>> one
En Objective-C, je trouve que j'ai un ensemble de méthodes communes dont je souhaite qu'un certain nombre de classes "héritent".De quelles autres manières puis-je y parvenir sans créer une classe commune et tout dériver de cette classe commune ?
La solution
Modifier:changements ajoutés parce que certaines personnes pensent que je suis responsable des limitations d'Objective-C.
Réponse courte:tu ne peux pas.Objective-C n'a pas l'équivalent des mixins Ruby.
Réponse un peu moins courte:Objective-C a quelque chose avec sans doute la même saveur :protocoles.Les protocoles (interfaces dans certains autres langages) sont un moyen de définir un ensemble de méthodes qu'une classe qui adopte ces protocoles s'engage à mettre en œuvre.Cependant, un protocole ne fournit pas d'implémentation.Cette limitation empêche d'utiliser les protocoles comme équivalent exact aux mixins Ruby.
Réponse encore moins courte : Cependant, le runtime Objective-C dispose d'une API exposée qui vous permet de jouer avec les fonctionnalités dynamiques du langage.Ensuite, vous sortez du langage, mais vous pouvez avoir des protocoles avec des implémentations par défaut (également appelés protocoles concrets).La réponse de Vladimir montre une façon de procéder.À ce stade, il me semble que vous obtenez bien les mixins Ruby.
Cependant, je ne suis pas sûr de recommander cela.Dans la plupart des cas, d’autres modèles conviennent sans jouer avec le runtime.Par exemple, vous pouvez avoir un sous-objet qui implémente la méthode mixte (a un au lieu de est un).Jouer avec le runtime est acceptable, mais présente 2 inconvénients :
Vous rendez votre code moins lisible car il oblige les lecteurs à connaître bien plus que le langage.Bien sûr, vous pouvez (et devez) le commenter, mais rappelez-vous que tout commentaire nécessaire peut être considéré comme un défaut de mise en œuvre.
Vous dépendez de que mise en œuvre du langage.Bien sûr, les plateformes Apple sont de loin les plus courantes pour Objective-C mais n'oubliez pas Cocotron ou GnuStep (ou Etoilé) qui ont des runtimes différents, qui peuvent ou non être compatibles avec ceux d'Apple à cet égard.
En remarque, j'indique ci-dessous que les catégories ne peuvent pas ajouter d'état (variables d'instance) à une classe.En utilisant l'API d'exécution, vous pouvez également lever cette limitation.Cela dépasse cependant la portée de cette réponse.
Longue réponse:
Deux fonctionnalités Objective-C semblent possibles :catégories et protocoles.Les catégories ne sont pas vraiment le bon choix ici, si je comprends bien la question.La bonne fonctionnalité est un protocole.
Laissez-moi vous donner un exemple.Supposons que vous souhaitiez qu'un certain nombre de vos classes aient une capacité spécifique appelée « chanter ».Ensuite vous définissez un protocole :
@protocol Singer
- (void) sing;
@end
Vous pouvez maintenant déclarer que n’importe laquelle de vos propres classes adopte le protocole de la manière suivante :
@interface Rectangle : Shape <Singer> {
<snip>
@end
@interface Car : Vehicle <Singer> {
<snip>
@end
En déclarant qu'ils adoptent le protocole, ils s'engagent à mettre en œuvre les sing
méthode.Par exemple:
@implementation Rectangle
- (void) sing {
[self flashInBrightColors];
}
@end
@implementation Car
- (void) sing {
[self honk];
}
@end
Ensuite, vous utilisez ces classes par exemple comme ceci :
void choral(NSArray *choir) // the choir holds any kind of singer
{
id<Singer> aSinger;
for (aSinger in choir) {
[aSinger sing];
}
}
Notez que les chanteurs du tableau n’ont pas besoin d’avoir une superclasse commune.Notez également qu'une classe ne peut avoir qu'une seule superclasse, mais plusieurs protocoles adoptés.Notez enfin que la vérification du type est effectuée par le compilateur.
En effet, le mécanisme du protocole est l'héritage multiple utilisé pour le modèle de mixage.Cet héritage multiple est sévèrement limité car un protocole ne peut pas ajouter de nouvelles variables d'instance à une classe.Un protocole décrit uniquement une interface publique que les adoptants doivent mettre en œuvre.Contrairement aux modules Ruby, il ne contient pas d'implémentation.
C'est le plus.Mentionnons cependant les catégories.
Une catégorie n’est pas déclarée entre crochets, mais entre parenthèses.La différence est qu'une catégorie peut être définie pour une classe existante afin de l'étendre sans la sous-classer.Vous pouvez même le faire pour une classe système.Comme vous pouvez l'imaginer, il est possible d'utiliser des catégories pour implémenter quelque chose de similaire au mixin.Et ils ont été utilisés de cette façon pendant longtemps, généralement comme catégorie pour NSObject
(la racine typique de la hiérarchie successorale), à tel point qu'on les qualifiait de protocoles « informels ».
C'est informel car 1- aucune vérification de type n'est effectuée par le compilateur, et 2- l'implémentation des méthodes de protocole est facultative.
Il n'est pas nécessaire aujourd'hui d'utiliser des catégories comme protocoles, d'autant plus que les protocoles formels peuvent désormais déclarer que certaines de leurs méthodes sont facultatives avec le mot-clé @optional
ou obligatoire (valeur par défaut) avec @required
.
Les catégories sont toujours utiles pour ajouter un comportement spécifique à un domaine à une classe existante. NSString
est un objectif commun pour cela.
Il est également intéressant de souligner que la plupart (sinon la totalité) des NSObject
les installations sont en effet déclarées dans un NSObject
protocole.Cela signifie qu'il n'est pas vraiment obligatoire d'utiliser NSObject
en tant que superclasse commune à toutes les classes, bien que cela soit encore couramment fait pour des raisons historiques, et bien...car il n’y a aucun inconvénient à le faire.Mais certaines classes système, comme NSProxy
, sont pas NSObject
.
Autres conseils
Shameless plug: ObjectiveMixin
Il profite de la capacité d'exécution Objective-C d'ajouter des méthodes à une classe d'exécution (par opposition aux catégories, qui sont à la compilation seulement). Check it out, cela fonctionne assez bien et de façon similaire aux mixins de Ruby.
Vous pouvez mixin littéralement le code à l'aide #include. Ce n'est pas souhaitable et est contre toutes les religions en Objective-C, fonctionne cependant parfaitement.
S'il vous plaît, ne le faites pas dans le code de production.
par exemple dans le fichier:
MixinModule.header (ne doit pas être compilé ou copié à la cible)
-(void)hello;
MixinModule.body (ne doit pas être compilé ou copié à la cible)
-(void)hello{
NSLog(@"Hello");
}
en classe mixin:
@interface MixinTest : NSObject
#include "MixinModule.header"
@end
@implementation MixinTest
#include "MixinModule.body"
@end
cas d'utilisation:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]){
@autoreleasepool {
[[[MixinTest new] autorelease] hello];
}
return 0;
}
S'il vous plaît, ne le faites pas dans le code de production.
Ceci est mon avis sur la mise en œuvre Mixins en Objective-C, sans utiliser le moteur d'exécution Objective-C directement. Peut-être qu'il est utile à quelqu'un: https://stackoverflow.com/a/19661059/171933