Utilisation des categories

6ix6ix Membre
12:00 modifié dans API AppKit #1
Hello,

Je me retrouve avec une classe assez conséquente, et mon idée est de la "séparer" grâce à  différentes categories: la classe de base, et des categories par "thème", regroupant les méthodes traitant d'un même sujet.

Mon problème est que certaines de ces categories font appel à  des méthodes définies dans la classe de base, mais non déclarées dans le header (ces méthodes n'ont pas à  être connues du reste du programme). Le compilateur affiche donc un warning lors de l'utilisation d'une de ces méthodes, par contre pas de problème à  l'exécution puisque ces méthodes existent effectivement.

La question que je me pose est de savoir si:
- premièrement, cette manière de séparer le code en categories est une bonne chose (Apple le donne comme argument dans sa doc sur les categories si j'ai bien compris)?
- si tel est le cas, quelle est la meilleure façon d'éviter ces warning; déclarer les méthodes utilisées dans le header de la classe de base, sachant qu'elles sont "privées"? Autre chose?

Merci!

Réponses

  • CéroceCéroce Membre, Modérateur
    12:00 modifié #2
    Non, les catégories ne servent pas à  ça: elles servent à  ajouter une méthode à  une classe existante. Par exemple, si tu voulais ajouter une méthode -secondObject à  NSArray, tu pourrais " sans disposer du code source.
    La limitation des catégories, c'est que tu ne peux pas ajouter de variable d'instance à  une classe.

    Maintenant, ce que tu souhaites faire, c'est masquer certaines méthodes. Le plus simple est de les déclarer privées... cependant, il se peut que tu utilises, uniquement en interne, des types qui n'ont pas à  être connu à  l'extérieur. Tu peux alors utiliser le design pattern "façade" (il me semble que c'est fait pour ça).

    Il me semble qu'Apple se contente parfois de ne pas mettre les protos des méthodes dans le .h. Si, si on peut.
  • 6ix6ix Membre
    12:00 modifié #3
    Effectivement, ce n'est pas la fonction première des categories, mais Apple précise bien que c'est une façon d'avoir un code "propre".

    You can also use categories to distribute the implementation of a new class into separate source  files"for example, you could group the methods of a large class into several categories and put each category in a different file



    dans 1228846449:

    Maintenant, ce que tu souhaites faire, c'est masquer certaines méthodes. Le plus simple est de les déclarer privées... cependant, il se peut que tu utilises, uniquement en interne, des types qui n'ont pas à  être connu à  l'extérieur. Tu peux alors utiliser le design pattern "façade" (il me semble que c'est fait pour ça).

    Il me semble qu'Apple se contente parfois de ne pas mettre les protos des méthodes dans le .h. Si, si on peut.


    Non, je ne masque pas certaines méthodes. Au lieu de définir maMéthode dans MaClasse, je fais une catégorie pour MaClasse, et j'ajoute maMéthode dans cette catégorie; maMéthode ne se trouve qu'à  un seul endroit.

    Par contre -c'est là  le truc-, maMéthode utilise une autre méthode (maMéthode2) qui elle est implémentée dans MaClasse, mais pas déclarée dans le header; la catégorie n'a donc pas connaissance de cette méthode à  la compilation, mais elle la trouve lors de l'exécution puisqu'elle existe effectivement.

    Le but est donc d'enlever ce warning, si l'on veut. Il suffit pour cela de déclarer maMéthode2 dans le header de la classe de base, comme ça la categorie en aura connaissance, mais comme il s'agit d'une méthode qui ne devrait être connue que de la classe, je me demande si c'est très propre...
  • NseaProtectorNseaProtector Membre
    12:00 modifié #4
    Je vais surrement dire une bêtise, mais au cas ou:
    Il me semble que ton code peut-être séparé dans plusieurs fichiers à  partir du moment ou tu l'inclus.
    Ou peut-être que les pragma mark pourrait t'aider à  t'organiser.
    Sinon, les warnings ne sont que des warnings si Apple dis que c'est ok, c'est que c'est ok.  J'imagine qu'ils font "un peu la même chose" pour les classes "secrètes" ou plutôt non documenté.
  • 6ix6ix Membre
    12:00 modifié #5
    Les pragam mark, c'est une idée, et je les utilise.  ::)

    Quant aux categories, Apple dit que c'est un moyen de répartir l'implémentation d'une classe dans plusieurs fichiers, mais pas que "si vous avez des warning parce qu'une categorie utilise une méthode de la classe de base, ce n'est pas grave".  ::)

    Donc....
  • Philippe49Philippe49 Membre
    décembre 2008 modifié #6
    dans 1228843472:

    Mon problème est que certaines de ces categories font appel à  des méthodes définies dans la classe de base, mais non déclarées dans le header (ces méthodes n'ont pas à  être connues du reste du programme). Le compilateur affiche donc un warning lors de l'utilisation d'une de ces méthodes, par contre pas de problème à  l'exécution puisque ces méthodes existent effectivement.

    J'utilises actuellement ce procédé pour y regouper les méthodes d'intiialisations, et je ne rencontre pas ce problème de warning.
    Mon .h de la catégorie n'importe rien, rien d'autre que le contenu du fichier prefix
    Le .m importe l'interface de la classe dont je fais une catégorie, et évidemment l'interface de la catégorie, qui porte un nom différent de la classe forcément.

    dans 1228843472:

    La question que je me pose est de savoir si:
    - premièrement, cette manière de séparer le code en categories est une bonne chose (Apple le donne comme argument dans sa doc sur les categories si j'ai bien compris)?

    Là , tu vas lancer un Troll ... Les goûts et les couleurs.



    dans 1228851818:

    Ou peut-être que les pragma mark pourrait t'aider à  t'organiser.

    C'est également pratique, avec le
    #pragma mark -
    #pragma mark Le titre


  • 6ix6ix Membre
    12:00 modifié #7
    dans 1228858602:

    J'utilises actuellement ce procédé pour y regouper les méthodes d'intiialisations, et je ne rencontre pas ce problème de warning.
    Mon .h de la catégorie n'importe rien, rien d'autre que le contenu du fichier prefix
    Le .m importe l'interface de la classe dont je fais une catégorie.


    Par "le contenu du fichier prefix", tu veux dire le .m de la classe "de base"? Ou le .h (ce que je fais actuellement)?

    Si dans une de tes méthodes d'initialisation tu utilises une autre méthode de la classe "catégorisée" mais non déclarée dans le .h de celle-ci, tu devrais avoir un warning, non?

    dans 1228858602:

    C'est également pratique, avec le
    #pragma mark -
    #pragma mark méthodes de data source


    Là  j'avoue que je ne connais pas les différences d'écriture. Qu'est-ce cela change avec le "-" ou autre...? Pour l'instant, je mets simplement "#pragma mark --- Init methods ----", par exemple.
  • MalaMala Membre, Modérateur
    12:00 modifié #8
    dans 1228852032:

    Quant aux categories, Apple dit que c'est un moyen de répartir l'implémentation d'une classe dans plusieurs fichiers, mais pas que "si vous avez des warning parce qu'une categorie utilise une méthode de la classe de base, ce n'est pas grave".  ::)

    C'est surtout un moyen de contourner en partie l'un des plus gros manques d'Obj-C qu'est l'héritage multiple. C'est bien dommage.
  • AliGatorAliGator Membre, Modérateur
    12:00 modifié #9
    L'héritage multiple, quelle hérésie, je suis bien content qu'ils ne le supportent pas dans Objective-C, du moment qu'on a l'héritage simple et les @protocols à  côté... pour moi si tu as à  faire de l'héritage multiple c'est que ton programme est mal designé qqpart.

    Pour revenir au pb de 6ix, La solution ne serait-elle pas les extensions de classes ? (qui sont en fait exactement comme des catégories, sauf qu'elles n'ont pas de nom) ?
    http://developer.apple.com/DOCUMENTATION/Cocoa/Conceptual/ObjectiveC/Articles/chapter_6_section_5.html#//apple_ref/doc/uid/TP30001163-CH20-SW2

    La première idée étant de mettre dans ton extension les méthodes "privées" que tu ne veux pas voir s'afficher dans le header que tout le monde consulte (ce qui ne t'empêche pas de les mettre en @private en plus), mais de les mettre à  part, et comme ça dans tes autres extensions tu pourras les appeler.
  • NoNo Membre
    12:00 modifié #10
    dans 1228843472:

    Mon problème est que certaines de ces categories font appel à  des méthodes définies dans la classe de base, mais non déclarées dans le header (ces méthodes n'ont pas à  être connues du reste du programme). Le compilateur affiche donc un warning lors de l'utilisation d'une de ces méthodes, par contre pas de problème à  l'exécution puisque ces méthodes existent effectivement.
    - si tel est le cas, quelle est la meilleure façon d'éviter ces warning; déclarer les méthodes utilisées dans le header de la classe de base, sachant qu'elles sont "privées"? Autre chose?


    Intègre un bloc de catégorie @interface juste avant le bloc @implementation dans le .m qui contient le source de ta catégorie.
    Dans ce bloc, mets y les prototypes de tes méthodes "masquées" (par exemple celle de methode2 pour reprendre ton exemple).

    Ainsi, ces méthodes "masquées" ne seront pas dans le .h, mais le compilateur ne râlera pas non plus. Tout le monde sera content.

    dans le .m de ta catégorie :
    <br />#import &quot;MaClasse.h&quot;<br /><br />@interface MaClasse(methodesPrivees) <br />- (void)methode2;<br />@end<br /><br />@implementation MaClasse<br /><br />@end<br />
    

  • Philippe49Philippe49 Membre
    12:00 modifié #11
    dans 1228859429:

    Par "le contenu du fichier prefix", tu veux dire le

    le monApp_Prefix.pch qui se trouve dans le groupe "Other sources"

    dans 1228859429:

    C'est également pratique, avec le
    #pragma mark -
    #pragma mark méthodes de data source

    Le #pragma mark - permet de séparer les zones par un trait dans le menu déroulant de XCode
  • MalaMala Membre, Modérateur
    12:00 modifié #12
    dans 1228862428:

    L'héritage multiple, quelle hérésie, je suis bien content qu'ils ne le supportent pas dans Objective-C, du moment qu'on a l'héritage simple et les @protocols à  côté...


    En quoi l'héritage multiple est-il une hérésie?  ???

    C'est une notion à  la base même de l'évolution. Chaque chose dans la vraie vie bénéficie d'un héritage multiple. Il n'y a rien de plus naturel en terme de compréhension humaine.

    dans 1228862428:

    pour moi si tu as à  faire de l'héritage multiple c'est que ton programme est mal designé qqpart.

    Bein là  j'apprends un truc.

    Tu peux développer? Ca m'intéresse.
  • AliGatorAliGator Membre, Modérateur
    12:00 modifié #13
    Bon, ok, j'ai peut-être été un peu virulent concernant l'héritage multiple :P
    Mais ce que je veux dire c'est que ça peut poser des problèmes (multi-surcharge par exemple) et des incohérences très rapidement et donc que c'est à  éviter dans la mesure du possible car dans 99% des cas on peut faire autrement.
    J'ai plusieurs fois eu en C++ des plantages que j'ai mis du temps à  comprendre et qui étaient dûs au fait qu'on avait un héritage multiple. Dans ce cas la table des pointeurs de fonctions virtuelle n'était pas cohérente, et l'appel à  une méthode virtuelle, qui était sensé en toute logique en lisant le code appeler la méthode d'une des classe parent bien définie, appelait en réalité au runtime une méthode qui n'avait rien à  voir et avec un nom et signature totalement différente à  cause de ce micmac.
    D'ailleurs on s'aperçoit que dans un langage qui permet l'héritage multiple comme le C++, si on hérite de A et B ça ne donne pas les mêmes comportements que si on hérite de B et A.

    Enfin, l'héritage multiple pose le problème connu sous le nom du "problème du diamant" ou "problème du losange" : Si tu as une classe Racine, avec une méthode toto(), classe qui est dérivée en une classe D1 et une classe D2 (directement ou indirectement), et que tu surcharges toto() dans D1 et dans D2 (avec des implémentations différentes)... Jusque là , rien d'exotique.
    Maintenant si tu crées une classe Enfant qui hérite à  la fois de D1 et de D2 (ou d'une de leurs classes dérivées), quand tu appelles la méthode toto() dans une instance de Enfant, c'est sensé faire quoi ?

    C'est à  ce moment là  qu'en C++ on commence à  rentrer dans des considérations d'héritage virtuel pour contourner le problème, sans quoi tu as deux pointeurs vers la classe parent "Racine" dans "Enfant" selon le chemin d'héritage suivi. Et là  je te garantis que ça commence à  devenir assez complexe et casse-gueule quand tu te retrouves avec ce genre de cas, surtout si tu mélanges un héritage virtuel et non virtuel (ce qui peut arriver vite surtout quand ce n'est pas des héritages directs mais avec des classes intermédiaires donc le problème est loin de te sauter aux yeux).

    Entre ces considérations qui font vite mal à  la tête et le fait que les comportements sont différents selon l'ordre dans lequel tu hérites... pour moi c'est loin d'être "rien de plus naturel en terme de compréhension humaine" :P En terme de design, éventuellement, ça peut mieux coller à  la réalité, même si j'ai pas d'exemple concret en tête, mais en terme de réalisation et de bon vouloir du compilateur et de complexité à  gérer dans le programme, c'est autre chose vu les petits travers que ça procure.
  • 6ix6ix Membre
    12:00 modifié #14
    Ok je vois les différentes pistes, je vais trouver ce qu'il me convient le mieux.

    Merci.
Connectez-vous ou Inscrivez-vous pour répondre.