2010-09-27 29 views
15

Je tente 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 d'écrire un tas d'instructions if afin que le code puisse être évolutif pour une utilisation future si d'autres mises en page doivent être ajoutées. J'ai entendu que la meilleure façon de mettre en œuvre ceci est l'utilisation du polymorphisme, mais ma compréhension du polymorphisme est encore un peu floue.Java: utiliser le polymorphisme pour éviter les déclarations if?

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 à mettre en œuvre. Ce qui m'embrouille, c'est comment ça marche dans main(), n'aurai-je pas encore besoin d'une instruction conditionnelle if ou switch pour savoir 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 principal:

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(); } 

} 

Ne serait-je encore besoin d'un commutateur ou d'instruction if dans le programme principal de savoir quelle disposition l'utilisateur a choisi lors de l'exécution?

Merci!

Répondre

17

Généralement, 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 vient lorsque vous avez plusieurs instructions if-else à plusieurs endroits. Le polymorphisme encapsule la logique conditionnelle pour vous. Voir this question pour d'autres discussions sur ce sujet.

Ce genre de logique dispersa

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 } 
} 

sera remplacé par quelque chose de plus simple:

layout = getLayout(user choice); 

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

Vous pouvez utiliser la réflexion pour éviter les instructions if toutes ensemble. – Dan

2

Bref, oui. Vous aurez besoin de ifs ou d'un mécanisme de cartographie.

Vous aurez besoin d'un moyen de convertir l'entrée de l'utilisateur dans la classe que vous voulez. Les ifs que vous avez dans votre code fonctionneront très bien et seront clairs et lisibles. Vous pouvez également utiliser un commutateur.

Il est possible d'éviter cela, mais vous finirez par obscurcir votre code et à la fin, il y aura probablement encore quelque chose comme un si. Vous devez définir le mappage de l'entrée de l'utilisateur à l'objet; cela ne peut pas être contourné. Le moyen le plus facile à maintenir est ce que vous avez déjà (bien que j'y ajoute d'autres ifs :) :)

L'idée que vous avez d'une classe différente pour chaque mise en page n'est pas mauvaise. C'est un peu comme le concept d'un traits class. Regarde ça aussi. Ce sera utile pour votre conception.

+0

Dans ce cas, je me demandais quels sont les avantages de la création d'une interface par opposition à l'utilisation d'un tas d'instructions if comme je l'avais initialement? Est-ce principalement pour la lisibilité? À la fin, j'utiliserai encore if-statements en tant que contrôleur. – user459811

+0

Avoir une interface commune à toutes vos mises en page signifie que vous n'avez pas besoin de réécrire votre code pour chaque nouvelle interface.Le code principal utilisera toujours la nouvelle mise en page de la même façon que les anciennes mises en page (via la même interface). C'est génial pour la maintenance. En ce qui concerne les ifs, la suggestion de hvgotcodes d'une carte finirait par être meilleure si vous savez que vous ajouterez un tas de nouvelles dispositions dans le futur. Ce n'est pas une énorme différence (ajouter un if ou ajouter un élément de carte), mais c'est généralement plus propre. – JoshD

+0

Vous commencerez à voir les avantages du polymorphisme la première fois que vous devez répliquer cette structure d'instructions if-else dans un second emplacement pour une fonction différente. –

2

N ° Utilisez une carte. Lorsque vous avez le choix, il suffit de chercher le gestionnaire sur la carte et vous partez.

psuedocode

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 instruction if.

+1

Cela a du sens, mais nous ne connaissons pas encore vraiment ses cas d'utilisation. Une carte peut être appropriée ou non, malgré la propreté de la solution. –

1

Quelque chose, quelque part, doit spécifier l'implémentation. 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é par réflexion.

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

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

1

Je suis un « peu flou » sur l'architecture de votre code, mais l'idée est que vous avez seulement le commutateur dans un placer plutôt que d'avoir plusieurs commutateurs éparpillés dans votre code qui ont les mêmes signatures de fonctions exactes sauf l'objet sur lequel il fonctionne.

La mise en œuvre exacte dépend de vos objectifs, mais je pense que vous auriez une classe qui implémente LayoutHander et a une référence de membre à une mise en page. Lorsque l'utilisateur choisit sa mise en page, le polymorphisme prend racine ICI, pas le niveau ci-dessus comme vous l'avez maintenant. C'est-à-dire, si l'objet qui définit le comportement différent est le "Layout" alors vous devez faire Layout polymorphic, pas LayoutHander. Quand vous pensez polymorhisme, pensez réutiliser le code. "Quel ensemble commun de fonctions partagent ces objets liés mais différents?"

+0

Mon objectif global est de créer une sorte de classe wrapper qui encapsule chaque type de mise en page. Chaque mise en page aura également son propre ensemble de fonctionnalités. Par exemple, layoutA est capable d'imprimer en HTML, tandis que layoutB est capable d'imprimer en XML. Pour cette raison, chaque mise en page est instanciée de différentes manières. Je voulais pouvoir mettre en œuvre mon main() de manière à ce qu'il soit facile à lire et à maintenir. En outre, cela permettrait l'ajout de futures classes de mise en page quelque part sur toute la ligne. – user459811

3

Étant donné que Java n'a pas de fonctions de première classe, vous pouvez utiliser des interfaces pour l'enrouler.

LayoutHandler ~> Interface 

LayoutHandlerA, LayoutHandlerB, etc implements LayoutHandler 

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

LayoutHandler handler = handlers.get(userSelectedLayout); 

handler.handle(); 
1

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 de if-statements une fois pour l'initialisation est correcte. Bien sûr, il existe certainement des moyens d'éviter même ces initiales if-statements, 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 initialiseur instructions if
    • utilise des références codées en dur pour les classes implémentant directement dans la logique du programme (plus difficile à trouver)
  • initialiser une structure de données (par exempleCarte) avec les classes possibles de mise en œuvre
    • références encore codées en dur à la mise en œuvre des classes
    • généralement plus facile d'ajouter d'autres classes implémentant
    • Code
    • est un peu plus complexe et abstraite de comprendre et déboguer
  • utiliser l'enregistrement dynamique
    • Aucune référence codée en dur aux classes d'implémentation dans l'application
    • Une configuration supplémentaire est requise
    • Code
    • est plus difficile à comprendre sans savoir comment l'enregistrement est configuré pour travailler

Un bon exemple de la dernière méthode (enregistrement dynamique) est de regarder comment fonctionne 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 flux de travail typique:

  1. pilotes JDBC cible potentiels sont ajoutés classpath.

  2. L'application est configurée avec une liste de ces pilotes, par ex. 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 de la liste. Chaque pilote sait s'enregistrer auprès de DriverManager lorsqu'il est chargé par Class.forName()

  4. L'application détermine la ressource de base de données cible à utiliser et la résolution de celle-ci en une URL JDBC, par ex. dans une boîte de dialogue de configuration ou une invite de l'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.

Ceci serait l'extrême opposé pour éviter ces instructions if.

1

« ..pour utilisation future si plus mises en page doivent être ajoutés »

Je vous suggère également vous regardez dans le modèle d'usine. Si vous intégrez cette logique conditionnelle dans une usine, il devrait être utile pour la maintenance et la lisibilité.

0

Vous devez utiliser un framework pour éviter de créer ou de définir un comportement.

Spring nous permet de gérer et de résoudre ce problème. Il utilise un modèle d'usine et plus de modèles 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.