2017-09-08 13 views
1

J'ai essayé de saisir la programmation orientée-protocole, mais je ne comprends pas la différence entre les 2 scénarios suivants ...Différence entre le protocole adoptant + l'extension VS utilisant instance d'une classe

Scénario 1 J'ai deux classes UIViewControllers. Ces deux classes doivent utiliser certaines fonctionnalités communes, donc je crée un protocole et une extension avec une implémentation par défaut du protocole, puis les contrôleurs de vue n'ont besoin que du protocole dans la ligne de classe et héritent automatiquement des fonctionnalités nécessaires. à-dire ...

protocol SomeProtocol { 
    func foo() 
} 
extension SomeProtocol { 
    func foo(){ 
     //execute 
    } 
} 

class FirstViewController: UIViewController, SomeProtocol { 
    ... 

    func doSomething(){ 
     foo() 
    } 
} 

class SecondViewController: UIViewController, SomeProtocol { 
    ... 

    func doSomethingElse(){ 
     foo() 
    } 
} 

Scénario 2 J'ai deux classes qui sont UIViewControllers. Ces deux classes doivent utiliser certaines fonctionnalités communes, donc je crée une classe de contrôleur et les deux classes UIViewController utilisent une instance de la classe de contrôleur. c'est-à-dire ...

class FirstViewController: UIViewController { 
    ... 
    let controller = Controller() 

    func doSomething(){ 
     controller.foo() 
    } 
} 

class SecondViewController: UIViewController { 
    ... 
    let controller = Controller() 

    func doSomethingElse(){ 
     controller.foo() 
    } 
} 


class Controller { 
    func foo(){ 
     //execute... 
    } 
}` 

Alors, quelle est la différence? Partout où j'ai besoin d'utiliser la fonction foo(), je pourrais simplement saisir une instance de Controller(). Quel avantage obtiens-je en mettant la fonction foo() dans un protocole et en ayant les classes qui ont besoin de foo() héritent du protocole?

+0

La question posée semble avoir rien à voir avec la programmation orientée protocole ... – matt

Répondre

1

Il y a deux approches lorsque vous voulez ajouter cette sous-classes fonctionnalité foo à plusieurs UIViewController (ou ce que vous avez):

  1. L'approche de protocole avec les extensions:

    Le problème est que Bien que cela fonctionne très bien avec le code Swift, il ne fonctionne pas très bien lorsque vous écrivez du code qui doit être appelé directement par Cocoa. Le mérite de cette approche (quand vous le pouvez) est que vous commencez à profiter des avantages décrits dans WWDC 2015 Protocol-Oriented Programming in Swift.

  2. L'approche par composants (où vous avez un exemple Controller que vos contrôleurs de vue peuvent achat forcé):

    C'est super quand vous avez besoin de fonctionnalités communes qui intégreront directement avec Cocoa. Par exemple, cela peut être utilisé lors de transitions de contrôleur de vues personnalisées et ne pas vouloir répéter le code dans les différents contrôleurs de vue. Cette approche peut être une manifestation du single responsibility principle et peut aider à combattre le ballonnement du contrôleur de vue.

Par souci d'exhaustivité, il y a quelques options pour parvenir à la réutilisation:

  1. Comme suggéré mat, vous pouvez également mettre en œuvre foo comme une extension une classe de base partagée.

    Cela fonctionne très bien quand la routine est logique pour non seulement vos deux sous-classes existantes, mais toutes les sous-classes. Mais ce n'est pas approprié si c'est une fonctionnalité unique à ces deux sous-classes particulières.

  2. Vous pouvez aussi mettre foo dans une sous-classe de cette classe de base (par exemple FooViewController des sous-classes UIViewController), et ensuite vos deux sous-classes précédentes sous-classe alors que la nouvelle classe FooViewController.

    Cette adresse la nature sans discernement d'une simple extension de la classe de base. Le problème est que ceci n'est pas aussi flexible que les deux premières approches (par exemple pas d'héritage multiple, un ensemble de méthodes Fooable et un autre ensemble de méthodes Barable).

Ligne de fond, la bonne approche dépend du problème spécifique que vous essayez de résoudre. Votre exemple est trop générique pour que nous puissions offrir des conseils spécifiques.

0

Quel avantage puis-je obtenir en mettant la fonction foo() dans un protocole et ayant les classes qui ont besoin foo() Hériter du protocole?

Si vous utilisez des protocoles, vous serez en mesure de stocker une instance d'un SomeProtocol générique sans se soucier de ce qu'il est en réalité:

var myFooable: SomeProtocol 

De toute évidence, ce n'est pas toujours utile. Si vous n'avez pas besoin de stocker une telle instance générique, l'utilisation de protocoles semble un peu excessive. Par contre, l'utilisation d'instances de Controller est une mauvaise idée. Toute classe peut créer un nouvel objet Controller et appeler des méthodes. Ce que vous essayez de faire ici, c'est d'exprimer une propriété «est capable de», ce que fait «se conformer à un protocole». Si vous utilisez une instance de Controller, cela exprimerait une relation "a un".

En outre, pourquoi utilisez-vous des protocoles avec des implémentations par défaut de toute façon? Si vous n'allez pas avoir différentes implémentations d'une méthode de protocole dans les classes conformes, envisagez de faire de cette méthode une fonction globale ou de la transformer en méthode statique dans une "classe de méthodes d'assistance".

0

donc je créer une classe de contrôleur et les deux classes de UIViewController utilisent une instance de la classe contrôleur

Le problème avec cette idée est la suivante: comment voulez-vous le faire pour, disons, un UITableViewController? Vous ne pouvez pas, car vous ne pouvez pas transformer votre classe de contrôleur en une super-classe de UITableViewController. Ce que je suggérerais n'est ni un protocole ni un héritage de classe. Utilisez un extension sur la classe elle-même. Vous pouvez étendre UIViewController, et hop, toutes les sous-classes UIViewController ont la fonctionnalité injectée.

+0

mais si je ne ai pas besoin tous les UIViewController d'avoir la fonctionnalité, seulement quelques-uns? – MikeG

+0

Je ne comprends pas le point. Les UIViewControllers ont déjà beaucoup de fonctionnalités dont vous n'avez pas besoin. Toutes les classes intégrées le font. Qu'est-ce que vous en pensez s'ils en ont encore plus? Si vous n'avez pas besoin d'une méthode, vous ne l'appelez pas. – matt

1

La programmation orientée protocole aide à réduire la redondance de la sous-classe d'une classe plus grande lorsque vous avez seulement besoin d'une fonctionnalité spécifique de cette classe.

Disons que vous avez contrôleur de classe avec trois fonctions

Class Controller { 

    func foo() { } 

    func bar() { } 

    func fooBar() { } 
} 

Maintenant, vous avez une situation où vous avez besoin cependant, un autre objet qui a besoin que la fonctionnalité de foo(), avec subclassing Controller, vous avez désormais tous les 3 les fonctions. Ou vous devez copier le code func foo() pour avoir la fonctionnalité dans les deux classes.

class SomeViewController: UIViewController { 
    /// You only need `func foo()` in this class, but now you have to use an object that carries all the extra functionality you don't need. 
    let controller: ControllerSubclass 
} 

Pour limiter ces contraintes et rendre votre code plus lisible/clair, vous créez un protocole Fooing et d'assigner le type à Fooing.

protocol Fooing { 
    func foo() 
} 

class SomeViewController: UIViewController { 

    let controller: Fooing 
} 

En faisant ce changement vous:

1 - Indiquez dans votre code que vous attendez cet objet pour faire une chose qu'il est capable de faire, appelez foo() fonction.

2 - Vous n'êtes pas limité à la classe Controller, vous pouvez utiliser n'importe quel objet conforme à Fooing, ce qui offre beaucoup plus de flexibilité.

3 - Vous supprimez le risque que d'autres fonctions d'une sous-classe Controller soient appelées lorsqu'elles ne le sont pas.

Dans des situations limitées, cela peut ne pas faire beaucoup de différence. Cependant, vous ne saurez jamais à l'avenir quand les exigences pourraient changer et il est toujours préférable de vous laisser le plus de flexibilité pour quand elles changent.