2009-08-09 7 views
2

Je ne trouve pas cela dans les scénarios d'utilisation pour le modèle de visiteur (ou peut-être que je ne comprends pas). Ce n'est pas non plus hiérarchique.modèle de visiteur contre conditions?

Utilisons un exemple d'authentification. Un UserAuthenticator authentifie les informations d'identification fournies par un utilisateur. Il renvoie un objet résultat. L'objet résultat contient le résultat de l'authentification: authentification réussie, non réussie car le nom d'utilisateur n'a pas été trouvé, pas réussi car des caractères illégaux ont été utilisés, etc. Le code client peut avoir recours à des conditions pour gérer cela. En pseudocode:

AuthResult = Userauthenticator.authenticate(Username, Password) 
if AuthResult.isAuthenticated: do something 
else if AuthResult.AuthFailedBecauseUsernameNotFound: do something else 
else if etc... 

Est-ce qu'un modèle de visiteur s'adapter ici? :

Authresult.acceptVisitor(AuthVisitor) 

Authresult appelle alors une méthode sur AuthVisitor en fonction du résultat:

AuthVisitor.handleNotAuthenticatedBecauseUsernameNotFound 

Répondre

1

Je recommande de ne pas utiliser les modèles pour intention qu'ils ne sont pas faits pour.

Les intentions de the visitor patterns sont:

  • représentent une opération à effectuer sur les éléments d'une structure d'objet. Visitor vous permet de définir une nouvelle opération sans modifier les classes des éléments sur lesquels elle opère.
  • La technique classique pour récupérer des informations de type perdu.
  • Faites la bonne chose en fonction du type de deux objets.
  • Double expédition

Cette solution serait utile si vous aviez prévu de faire différentes méthodes d'authentification, mais si vous prévoyez seulement faire un, vous devrez utiliser conditionals de toute façon.

+0

Je ne suis pas d'accord pour ne pas utiliser ce pour quoi ils n'ont pas été faits. Quelque chose peut résoudre un problème sans qu'il soit prévu de le faire. Si le modèle de visiteur résout mon problème, dans un bon sens, pourquoi ne devrais-je pas l'utiliser? La question devient alors: la solution est-elle bonne? Personne ne l'utilise de cette façon ne signifie pas que c'est une mauvaise solution, même si cela peut laisser entendre dans cette direction. Plus important est pourquoi c'est une bonne ou une mauvaise solution. Si McGyver suivait votre conseil, il n'aurait plus de travail. – koen

+0

Une autre chose est: la façon dont l'authentification est traitée est agnostique à l'exemple. Je ne vois pas pourquoi mon exemple limite UserAuthenticator à un seul moyen d'authentification (par exemple seulement LDAPUserAuthentication, OpenIdUserAuthentication etc) – koen

1

Visiteur est un concept utile lorsque vos données ne change pas rapidement que votre comportement. Un exemple typique est un arbre d'analyse syntaxique:

  • votre hiérarchie de classes (données) est gelé
  • votre comportement varie trop, vous ne voulez pas briser vos classes en ajoutant une autre méthode virtuelle

Je ne pense pas qu'un visiteur soit une solution valable ici, puisque chaque fois que vous ajoutez une sous-classe d'AuthResult, vous cassez votre visiteur.

Le visiteur traite de l'encapsulation de trading avec double expédition.

Vous pouvez essayer une approche similaire:

interface Handler { 

    void onUsernameNotFound(); 

    void onWrongPassword(); 

    void authOk(); 
} 

interface Authenticator { 
    void authenticate(String username, String password, Handler handler); 
} 

class SimpleAuthenticator implements Authetnciator { 

    void authenticate(String username, String password, Handler handler) { 
     if (username.equals("dfa")) { 
      if (password.equals("I'm1337")) { 
       handler.authOk(); 
      } else { 
       handler.onWrongPassword(); 
      } 
     } else { 
      handler.onUsernameNotFound(); 
     } 
    } 
} 

certains gestionnaire Stategies:

class FatalHandler implements Handler { 

    void onUsernameNotFound() { 
     throw new AuthError("auth failed"); 
    } 

    void onWrongPassword() { 
     throw new AuthError("auth failed"); 
    } 

    void authOk() { 
     /* do something */ 
    } 
} 

et:

class DebugHandler implements Handler { 

    void onUsernameNotFound() { 
     System.out.println("wrong username"); 
    } 

    void onWrongPassword() { 
     System.out.println("wrong password"); 
    } 

    void authOk() { 
     System.out.println("ok"); 
    } 
} 

vous pouvez maintenant résumer la manipulation et operatorion erreur dans votre Handler s c'est beaucoup moins de code que Visiteur puisque vous n'avez pas vraiment besoin de double dispa tch ici.

+0

J'utiliserais cette conception. Toutefois, une observation: vous commentez que Visitor n'est pas approprié car chaque nouvelle sous-classe d'AuthResult nécessite des modifications au visiteur. Ici les méthodes sur Handler correspondent à des résultats différents, ce qui aurait effectivement été différentes sous-classes de AuthResult. Le même couplage se rapporte ... nouveau résultat possible L'interface du gestionnaire et toutes les implémentations changent beaucoup. Étant donné que nous avons deux dimensions de la variabilité, Auth + Handler, jusqu'où sommes-nous réellement en réquisition de Double Dispatch? – djna

+0

Il me manque quelque chose. Pourquoi une sous-classe Auth échouerait-elle pour le visiteur? Il pourrait y avoir une classe de base abstraite qui implémente le acceptVisitor. Je vois comment une stratégie fonctionnerait ici, mais d'une certaine manière une stratégie est liée au visiteur à mon humble avis, sauf qu'elle ne fait pas de hiérarchie. – koen

+0

@djna: avec le visiteur vous avez dupliqué la logique puisque vous avez besoin d'une chaîne if-elseif pour construire la bonne classe AuthResponse concrète ... alors vous avez besoin d'une autre chaîne if-elseif pour l'envoi – dfa