Pour quelles raisons utiliseriez-vous une extension de classe distincte pour chaque délégué dans Swift?

13

Je travaillais sur un tutoriel Ray Wenderlich et j'ai remarqué que l'auteur utilise des extensions de classe pour contenir les rappels des délégués plutôt que de les faire gérer dans la classe elle-même, c'est-à-dire:

déléguer les rappels à l'intérieur de l'extension de classe:

extension LogsViewController : UIPopoverPresentationControllerDelegate {
    func adaptivePresentationStyleForPresentationController(controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
        ...
    }
}

au lieu de le faire figurer dans la classe:

déléguer les rappels à l'intérieur de la classe:

class LogsViewController : UITableViewController, UIPopoverPresentationControllerDelegate {
    func adaptivePresentationStyleForPresentationController(controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
        ...
    }
}

J'ai trouvé cela étrange et intéressant à la fois. Il a un fichier dédié uniquement aux extensions de la classe LogsViewController nommé "LogsViewControllerExtension.swift" et possède une extension différente pour chaque protocole de délégué: UITableViewDataSource, UISplitViewDelegate, etc. c'est-à-dire:

plusieurs extensions de classe chacune avec des rappels délégués dans son propre fichier:

extension LogsViewController: UISplitViewControllerDelegate {
    ... callbacks
}

extension LogsViewController : UIPopoverPresentationControllerDelegate {
    ... callbacks
}

Pourquoi?

Quels avantages y a-t-il à faire cela? Je peux voir où il pourrait être un peu plus lisible de séparer cela, mais en même temps, c'est un niveau d'indirection. Y a-t-il des principes OO qui sont favorables ou défavorables à cela?

morbidhawk
la source
1
Vous pouvez écrire beaucoup de structures comme celle-ci qui sont fondamentalement soutenues par les principes OO mais qui sont toujours coupables de suringénierie.
Robert Harvey
1
@RobertHarvey true, mais voulez-vous dire que l'exemple de code que j'ai montré ici est une forme de suringénierie? La délégation est un modèle courant dans le développement iOS. De plus, que vous utilisiez des extensions de classe ou non ne change pas le code qu'il contient, il s'agirait donc plus de restructuration de code que de re (/ over) -engineering
morbidhawk
1
Je vois ce modèle de jeter un tas d'extensions dans le même fichier, et je ne sais pas d'où cela vient. Il semble que certaines personnes en abusent déjà et jettent juste arbitrairement chaque petit morceau de code dans une extension dans le même fichier. Je suis sûr qu'il y a une bonne raison de le faire à l'occasion, mais j'ai l'impression que certains programmeurs le font simplement sans comprendre pourquoi. Je continue à chercher un avantage sur l'utilisation de MARK: - et j'aimerais trouver une source définitive sur les raisons pour lesquelles les extensions doivent être utilisées de cette façon.
David Lari

Réponses:

15

Je ne sais pas pourquoi vous avez dit que cela ajoute un niveau d'indirection. Peut-être que vous voulez dire quelque chose de différent par cela que le sens traditionnel, car il n'y a pas d'indirection supplémentaire créée en faisant cela. Mais pourquoi faire ça?

Je le fais car il est plus modulaire. Tout le code requis par l'interface est regroupé en un seul endroit (à l'exception des propriétés réelles.) Si je choisis plus tard de créer une classe distincte pour implémenter ce protocole (et donc d'introduire un niveau réel d'indirection), tout ce dont j'ai besoin faire est de changer l'extension en une classe qui lui est propre (en passant les propriétés nécessaires via une fonction init) et de créer une propriété sur le ViewController pour instancier l'objet dans.

J'ai également mis toutes les fonctions privées qui ne sont utilisées que par les fonctions de ce protocole dans l'extension. Je ne suis pas allé jusqu'à créer un fichier complètement séparé pour l'extension, mais cela montre clairement que ces fonctions privées sont uniquement pour ce protocole.

Et en tout cas, les gens se plaignent souvent des gros contrôleurs de vue et la décomposition d'un contrôleur de vue de cette manière aide à le garder mieux organisé, même si cela ne rend pas réellement le contrôleur de vue plus mince.

Daniel T.
la source
Je vois ce que vous entendez par modularité. Une fois que j'ai compris ce qui se passait, cela ressemblait à un moyen propre de séparer les préoccupations. Le niveau d'indirection je pense est pour un nouvel ensemble d'yeux (et je suis biaisé venant de la manière Obj-c). Je ressens le même niveau d'indirection lorsque je sous-classe où du code est référencé qui est défini ailleurs dans la classe de base et c'est un peu plus difficile à trouver. Je conviens que sur le plan organisationnel, cela ajoute des avantages (avec un petit coût d'indirection)
morbidhawk
Je suppose que cela a déjà été fait avec des catégories de classe en Obj-C. Je n'ai jamais été un grand fan de ceux-ci mais je pense qu'ils avaient besoin d'un fichier séparé. Depuis maintenant, dans Swift, vous pouvez conserver l'extension dans le même fichier, c'est ce que je ferai probablement si je décide de les décomposer comme ça
morbidhawk
2

Comme Daniel l'a dit à propos de l'indirection, il n'y en a pas.
Je suis d'accord avec lui et je voudrais ajouter une fonctionnalité supplémentaire puissante des extensions de protocole que je connaissais récemment.

Disons que vous avez un protocole didCopyTextpar exemple. Vous implémenterez cela comme:

protocol didCopyText {
  var charachtersCount: Int {get}
  func addToClipboardWith(text: String)
}

Dans Swift, les propriétés et les méthodes ne sont pas implémentées dans la déclaration de protocole, vous voudriez écrire l'implémentation dans chaque classe conforme à la didCopyText, avec un nombre incrémentiel de classes conformes à ce protocole avec la même implémentation, cela finirait avec juste un désordre code répété. C'est là que les extensions de protocole sont utiles.

protocol didCopyText {
var charachtersCount: Int {
    get {
     // implementation
    }
}
func addToClipboardWith(text: String) {
      // implementation
 }
}

Avec l'implémentation des propriétés et méthodes du protocole. Maintenant, toute classe conforme à ce protocole, utilisera la même implémentation.

MEnnabah
la source
merci de partager cela. Au moment où j'ai posé cette question, je ne me rendais pas compte de certains des inconvénients de l'héritage OO, et ce que vous décrivez ici est un excellent moyen d'obtenir les mêmes fonctions réutilisées par toutes les classes d'implémentation qui se conforment à cette interface plutôt que d'être forcées de héritez de tout d'un objet. Et si vous suivez le principe de la séparation d'interface, vous pouvez séparer les protocoles selon les besoins pour vous assurer que les classes d'implémentation n'ont jamais de propriétés / méthodes de l'interface dont elles n'ont pas besoin.
morbidhawk
1

Disons que votre classe prend en charge trois protocoles et que vous devez donc ajouter trois ensembles de fonctions. Le seul but de ces fonctions est de prendre en charge un protocole, vous avez donc besoin de documentation.

Cependant, si vous ajoutez une extension pour chaque protocole, et dans chaque extension, vous implémentez exactement les fonctions nécessaires pour ce protocole, cela rend votre code beaucoup plus lisible.

Je ne les mettrais probablement pas dans des fichiers séparés à moins que ces extensions ne soient vraiment grandes.

gnasher729
la source