Question Java: utiliser le polymorphisme pour éviter les if-instructions?


J'essaie d'écrire un programme Java qui initialise certaines mises en page en fonction de ce que l'utilisateur sélectionne. Ce que je veux faire est d’essayer d’éviter l’écriture d’un grand nombre de if-déclarations pour que le code puisse être évolutif pour une utilisation ultérieure si des dispositions supplémentaires doivent être ajoutées. J'ai entendu dire que la meilleure façon de l'implémenter est d'utiliser le polymorphisme, mais ma compréhension du polymorphisme est encore un peu floue.

Disons que je veux mettre en œuvre ce cas:

if (user choose layoutA) { initialize layoutA }
if (user choose layoutB) { initialize layoutB }
if (user choose layoutC) {initialize layoutC }

Je pensais à créer une interface pour les classes à implémenter. Ce qui me dérange, c'est comment cela fonctionne dans main (), n'aurai-je pas toujours besoin d'une instruction if if ou switch pour déterminer quelle classe instancier?

interface LayoutHandler {
    public void initializeLayout();
}

class layoutA implements LayoutHandler { 
    public void initialize Layout {initialize layout A}
}
class layoutB implements LayoutHandler { 
    public void initialize Layout {initialize layout B}
}
class layoutC implements LayoutHandler { 
    public void initialize Layout {initialize layout C}
}

Puis quelque part dans le main:

public static void main() {
   getlayoutselectionfromuser()
   if (user choose layoutA) { LayoutHandler layout = new layoutA(); }
   if (user choose layoutB) { LayoutHandler layout = new layoutB(); }
   if (user choose layoutC) { LayoutHandler layout = new layoutC(); }

}

Est-ce que je n'aurais toujours pas besoin d'un switch ou d'une instruction if dans le programme principal pour déterminer la mise en page choisie par l'utilisateur lors de l'exécution?

Merci!


15
2017-09-27 18:50


origine


Réponses:


En règle générale, il sera difficile d'éviter une sorte d'instruction conditionnelle à un moment donné pour créer une instance de la classe appropriée.

L'avantage du polymorphisme survient lorsque vous avez plusieurs déclarations if-else à plusieurs endroits. Le polymorphisme encapsule la logique conditionnelle pour vous. Voir cette question pour d'autres discussions sur ce sujet.

Cette sorte de logique dispersée:

void initLayout() {
   if (user choose layoutA) { initialize layoutA }
   if (user choose layoutB) { initialize layoutB }
   if (user choose layoutC) {initialize layoutC }
}

void refreshLayout() {
   if (user choose layoutA) { refresh layoutA }
   if (user choose layoutB) { refresh layoutB }
   if (user choose layoutC) { refresh layoutC }
}

void cleanupLayout() {
   if (user choose layoutA) { cleanup layoutA }
   if (user choose layoutB) { cleanup layoutB }
   if (user choose layoutC) { cleanup layoutC }
}

Est remplacé par quelque chose de plus simple:

   layout = getLayout(user choice);

   layout.initLayout();
   layout.refreshLayout();
   layout.cleanupLayout();

17
2017-09-27 18:58



Comme java n'a pas de fonctions de première classe, vous pouvez utiliser des interfaces pour les envelopper.

LayoutHandler ~> Interface

LayoutHandlerA, LayoutHandlerB, etc implements LayoutHandler

Map<String, LayoutHandler> handlers = new HashMap<...>();

LayoutHandler handler = handlers.get(userSelectedLayout);

handler.handle();

3
2017-09-27 19:03



En bref, oui. Vous aurez besoin de ifs ou de mécanicien de cartographie.

Vous aurez besoin d'un moyen de convertir les entrées de l'utilisateur dans la classe de votre choix. Les options que vous avez dans votre code fonctionneront bien et seront claires et lisibles. Vous pouvez également utiliser un commutateur.

Il est possible d'éviter cela, mais vous finirez par masquer votre code et, à la fin, il y aura probablement toujours quelque chose comme un if. Vous devez définir le mappage de l'entrée utilisateur sur l'objet; cela ne peut pas être contourné. Le moyen le plus facile à gérer est ce que vous avez déjà (même si je mettais autre chose à ce sujet :))

L'idée que vous avez d'une classe différente pour chaque mise en page n'est pas mauvaise. Cela ressemble beaucoup au concept de classe de traits. Regardez ça aussi. Ce sera utile pour votre conception.


2
2017-09-27 18:54



Non, utilisez une carte. Lorsque vous avez le choix, il vous suffit de rechercher le gestionnaire dans la carte et de partir.

pseudo-code

 Map handlers = new Map()
 handlers.put(layoutA, HandlerForA)
 // more handlers, possibly use dependency injection

 Layout chosen = user choose layout // get a ref to the selected layout
 Handler handler = handlers.get(chosen)
 if (handler == null) { // No HandlerException }
 handler.go()

une seule si déclaration.


2
2017-09-27 18:55



Quelque chose, quelque part, doit spécifier la mise en œuvre. Cela pourrait être une chaîne d'instructions if dans le code source - mais cela pourrait aussi être quelque chose d'autre; Par exemple, un nom de classe spécifié dans une source de données externe, instancié via la réflexion.

Pour certaines interfaces, il peut y avoir beaucoup plus d'appels à l'interface que les instanciations. Le polymorphisme peut réduire couplage à des implémentations spécifiques.

Ce couplage réduit peut aider à rendre le code plus facile à entretenir. Vous pouvez ajouter une nouvelle implémentation sans modifier potentiellement plusieurs appelants.


1
2017-09-27 18:56



Je suis un peu "flou" sur votre architecture de code mais l'idée est que vous n'avez que le changement un place plutôt que d'avoir plusieurs commutateurs éparpillés dans votre code qui ont les mêmes signatures de fonction exactes, sauf l'objet sur lequel il opère.

L'implémentation exacte dépend de vos objectifs, mais je pense que vous auriez une classe implémentant LayoutHander et une référence de membre à une disposition. Lorsque l'utilisateur choisit sa mise en page, le polymorphisme prend le pas ICI, pas le niveau que vous avez maintenant. C'est-à-dire que si l'objet qui définit le comportement différent est la "mise en page", vous devez rendre la mise en page polymorphe, et non LayoutHander.

Lorsque vous pensez au polymorhisme, pensez à la réutilisation du code. "Quel ensemble commun de fonctions ces objets liés mais différents partagent-ils?"


1
2017-09-27 18:58



Je pense que l'utilisation des instructions if pour l'initialisation est correcte. Vous essayez d'éviter l'utilisation répétée des instructions if pour "sélectionner" le comportement tout au long du programme. L'utilisation des instructions if une fois pour l'initialisation est correcte.

Pour être sûr, il existe certainement des moyens d'éviter même les instructions if d'initialisation, mais vous devez décider quel niveau de complexité et de perte de lisibilité est approprié pour votre application.

Par exemple, voici quelques approches de ce problème, du plus simple au plus complexe:

  • utiliser ces instructions if-initializer
    • utilise des références codées en dur pour implémenter des classes directement dans la logique du programme (plus difficile à trouver)
  • initialiser une structure de données (par exemple, une carte) avec les classes d'implémentation possibles
    • références encore codées en dur à des classes d'implémentation
    • généralement plus facile d'ajouter plus de classes d'implémentation
    • le code est un peu plus compliqué et abstrait pour comprendre et déboguer
  • utiliser l'enregistrement dynamique
    • pas de références codées en dur aux classes d'implémentation dans l'application
    • plus de configuration est nécessaire
    • le code est plus difficile à comprendre sans savoir comment l’enregistrement est configuré pour fonctionner

Un bon exemple de la dernière méthode (enregistrement dynamique) consiste à examiner le fonctionnement de JDBC. Les pilotes JDBC sont enregistrés dans l'application en utilisant Class.forName() puis un pilote JDBC spécifique est sélectionné à l'aide d'une URL JDBC. Voici un workflow typique:

  1. Les pilotes JDBC cibles potentiels sont ajoutés classpath.

  2. L'application est configurée avec une liste de ces pilotes, par exemple en répertoriant les pilotes JDBC dans un fichier de propriétés.

  3. L'application s'initialise en appelant Class.forName() sur chaque pilote dans la liste. Chaque pilote sait s’inscrire avec le DriverManager quand il est chargé par Class.forName()

  4. L'application détermine la ressource de base de données cible à utiliser et à la résoudre en une URL JDBC, par exemple dans une boîte de dialogue de configuration ou une invite utilisateur.

  5. L'application demande à DriverManager une connexion basée sur l'URL JDBC cible. DriverManager interroge chaque pilote enregistré pour voir s'il peut gérer l'URL JDBC cible jusqu'à ce que DriverManager en trouve un qui fonctionne.

Ce serait l'extrême opposé pour éviter ces déclarations if.


1
2017-09-27 19:26



"..pour utilisation future si plus de dispositions doivent être ajoutées"

Je vous suggère également de regarder le modèle d'usine. Si vous contenez cette logique conditionnelle dans une usine, cela devrait vous aider à maintenir la maintenance et la lisibilité.


1
2017-09-27 19:49



Vous devez utiliser un cadre pour éviter de créer un comportement.

Spring nous permet de gérer et de résoudre ce problème. Il utilise un motif d'usine et plus de motifs de conception. Donc, en utilisant des annotations ou un fichier de configuration xml, vous pouvez décider quelles classes créer. PD: Bien sûr, je suis sûr à 100% que le printemps utilise si. Mais il est prouvé et utilisé dans de nombreuses applications solides et fiables.


0
2017-08-17 18:51