Je démarre un projet de groupe scolaire à Java, en utilisant Swing. Il s'agit d'une interface graphique simple sur l'application de bureau de base de données.
Le professeur nous a donné le code du projet de l'année dernière afin que nous puissions voir comment il fait les choses. Mon impression initiale est que le code est beaucoup plus compliqué qu'il ne devrait l'être, mais j'imagine que les programmeurs pensent souvent cela en regardant du code qu'ils n'ont pas simplement écrit.
J'espère trouver des raisons pour lesquelles son système est bon ou mauvais. (J'ai demandé au professeur et il m'a dit que je verrais plus tard pourquoi c'est mieux, ce qui ne me satisfait pas.)
Fondamentalement, pour éviter tout couplage entre ses objets persistants, ses modèles (logique métier) et ses vues, tout se fait avec des chaînes. Les objets persistants qui sont stockés dans la base de données sont des tables de hachage de chaînes, et les modèles et les vues "s'abonnent" les uns aux autres, fournissant des clés de chaîne pour les "événements" auxquels ils s'abonnent.
Lorsqu'un événement est déclenché, la vue ou le modèle envoie une chaîne à tous ses abonnés qui décident quoi faire pour cet événement. Par exemple, dans l'une des méthodes d'écoute d'action des vues (je crois que cela définit simplement le BicycleMakeField sur l'objet persistant):
else if(evt.getSource() == bicycleMakeField)
{
myRegistry.updateSubscribers("BicycleMake", bicycleMakeField.getText());
}
Cet appel arrive finalement à cette méthode dans le modèle de véhicule:
public void stateChangeRequest(String key, Object value) {
... bunch of else ifs ...
else
if (key.equals("BicycleMake") == true)
{
... do stuff ...
Le professeur dit que cette façon de faire est plus extensible et plus facile à entretenir que le simple fait que la vue appelle une méthode sur un objet logique métier. Il dit qu'il n'y a pas de couplage entre les points de vue et les modèles parce qu'ils ne connaissent pas l'existence les uns des autres.
Je pense que c'est un pire type de couplage, car la vue et le modèle doivent utiliser les mêmes chaînes pour fonctionner. Si vous supprimez une vue ou un modèle ou effectuez une faute de frappe dans une chaîne, vous n'obtenez aucune erreur de compilation. Cela rend également le code beaucoup plus long que je ne le pense.
Je veux en discuter avec lui, mais il utilise son expérience de l'industrie pour contrer tout argument que moi, l'étudiant inexpérimenté, pourrais faire. Y a-t-il un avantage à son approche qui me manque?
Pour clarifier, je veux comparer l'approche ci-dessus, avec le fait que la vue soit évidemment couplée au modèle. Par exemple, vous pouvez passer l'objet de modèle de véhicule à la vue, puis pour modifier la "marque" du véhicule, procédez comme suit:
vehicle.make = bicycleMakeField.getText();
Cela réduirait 15 lignes de code actuellement utilisées UNIQUEMENT pour définir la marque du véhicule en un seul endroit, en une seule ligne de code lisible. (Et puisque ce type d'opération est effectué des centaines de fois dans l'application, je pense que ce serait une grande victoire pour la lisibilité et la sécurité.)
Mise à jour
Mon chef d'équipe et moi avons restructuré le cadre de la façon dont nous voulions le faire en utilisant la frappe statique, en avons informé le professeur et nous avons fini par lui faire une démonstration. Il est assez généreux pour nous permettre d'utiliser notre framework tant que nous ne lui demandons pas d'aide, et si nous pouvons tenir le reste de notre équipe au courant - ce qui nous semble juste.
Réponses:
L'approche que propose votre professeur est mieux décrite comme typée de façon stricte et est erronée à presque tous les niveaux.
Le couplage est mieux réduit par l' inversion de dépendance , qui le fait par une répartition polymorphe, plutôt que de câbler un certain nombre de cas.
la source
Votre professeur se trompe . Il essaie de découpler complètement la vue et le modèle en forçant la communication à voyager via une chaîne dans les deux directions (la vue ne dépend pas du modèle et le modèle ne dépend pas de la vue) , ce qui va totalement à l'encontre du but de la conception orientée objet et du MVC. Il semble qu'au lieu d'écrire des cours et de concevoir une architecture basée sur OO, il écrit des modules disparates qui répondent simplement aux messages textuels.
Pour être un peu juste envers le professeur, il essaie de créer en Java ce qui ressemble beaucoup à l'interface .NET très courante appelée
INotifyPropertyChanged
, qui informe les abonnés des événements de changement en passant des chaînes qui spécifient quelle propriété a changé. Dans .NET au moins, cette interface est principalement utilisée pour communiquer à partir d'un modèle de données pour afficher des objets et des éléments d'interface graphique (liaison).Si elle est bien faite , cette approche peut présenter certains avantages¹:
events
mais en Java, vous devez créer des classes ou des objets et écrire du code d'abonnement / de désabonnement pour chaque événement.Donc, en bref (et pour répondre à votre question), cela peut être fait, mais l'approche de votre professeur est en faute pour ne pas autoriser au moins une dépendance à sens unique entre la vue et le modèle. Ce n'est tout simplement pas pratique, trop académique et ce n'est pas un bon exemple de la façon d'utiliser les outils à portée de main.
¹ Effectuez une recherche sur Google pour
INotifyPropertyChanged
trouver un million de façons de trouver des gens pour éviter d'utiliser la chaîne magique lors de sa mise en œuvre.la source
enum
s (oupublic static final String
s) doit être utilisé à la place des cordes magiques.Votre professeur ne comprend pas OO . Par exemple, avoir une classe de véhicule concrète?
Et pour que cette classe comprenne la marque d'un vélo?
Le véhicule doit être abstrait et il doit y avoir une sous-classe concrète appelée Vélo. Je jouerais selon ses règles pour obtenir une bonne note, puis j'oublierais son enseignement.
la source
Je pense que vous avez un bon point, les cordes magiques sont mauvaises. Quelqu'un de mon équipe a dû déboguer un problème causé par des cordes magiques il y a quelques semaines (mais c'est dans leur propre code qu'ils ont écrit, alors j'ai gardé la bouche fermée). Et c'est arrivé ... dans l'industrie !!
L'avantage de son approche est qu'il pourrait être plus rapide de coder, en particulier pour les petits projets de démonstration. Les inconvénients sont qu'elle est sujette à des fautes de frappe et plus la chaîne est utilisée, plus le code typo a de chances de gâcher les choses. Et si jamais vous voulez changer une chaîne magique, la refactorisation des chaînes est plus difficile que les variables de refactorisation, car les outils de refactorisation peuvent également toucher des chaînes qui contiennent la chaîne magique (en tant que sous-chaîne) mais qui ne sont pas elles-mêmes la chaîne magique et ne doivent pas être touchées. En outre, vous devrez peut-être conserver un dictionnaire ou un index de chaînes magiques pour que les nouveaux développeurs ne commencent pas à inventer de nouvelles chaînes magiques dans le même but et ne perdent pas de temps à rechercher des chaînes magiques existantes.
Dans ce contexte, il semble qu'un Enum puisse être meilleur que des chaînes magiques ou même des constantes de chaînes globales. De plus, les IDE vous fourniront souvent une sorte d'assistance de code (auto-complétion, refactorisation, recherche de références, etc.) lorsque vous utilisez des chaînes constantes ou des énumérations. Un IDE n'apportera pas beaucoup d'aide si vous utilisez des cordes magiques.
la source
Utiliser «l'expérience de l'industrie» est un mauvais argument. Votre professeur doit trouver un meilleur argument que cela :-).
Comme d'autres l'ont déclaré, l'utilisation de cordes magiques est certainement mal vue, l'utilisation d'énumérations est considérée comme beaucoup plus sûre. Une énumération a un sens et une portée , pas les cordes magiques. Hormis cela, la manière dont votre professeur dissocie les préoccupations est considérée comme une technique plus ancienne et plus fragile qu'aujourd'hui.
Je vois que @backtodos a couvert cela pendant que je répondais à cela, alors j'ajouterai simplement que selon Inversion of Control (dont Dependency Injection est un formulaire, vous allez parcourir le framework Spring dans l'industrie avant longtemps. ..) est considéré comme un moyen plus moderne de gérer ce type de découplage.
la source
Soyons assez clairs; il y a inévitablement un couplage là-dedans. Le fait qu'il existe un tel sujet souscriptible est un point de couplage, tout comme la nature du reste du message (comme la «chaîne unique»). Compte tenu de cela, la question devient alors de savoir si le couplage est plus important lorsqu'il est effectué via des chaînes ou des types. La réponse à cela dépend si vous êtes préoccupé par le couplage distribué dans le temps ou dans l'espace: si les messages vont être conservés dans un fichier avant d'être lus plus tard, ou s'ils seront envoyés à un autre processus (surtout si ce n'est pas écrit dans la même langue), l'utilisation de chaînes peut rendre les choses beaucoup plus simples. L'inconvénient est qu'il n'y a pas de vérification de type; les erreurs ne seront pas détectées avant l'exécution (et parfois même pas alors).
Une stratégie d'atténuation / de compromis pour Java peut consister à utiliser une interface typée, mais où cette interface typée se trouve dans un package séparé. Il doit être composé uniquement de Java
interface
et de types de valeurs de base (par exemple, énumérations, exceptions). Il ne devrait y avoir aucune implémentation d'interfaces dans ce package. Ensuite, tout le monde implémente contre cela et, si la transmission des messages de manière complexe est nécessaire, une implémentation particulière d'un délégué Java peut utiliser des chaînes magiques si nécessaire. Il facilite également la migration du code vers un environnement plus professionnel tel que JEE ou OSGi, et facilite grandement les petites choses comme les tests…la source
Ce que votre professeur propose, c'est l'utilisation de "cordes magiques". Bien qu'il "couple librement" les classes entre elles, permettant des changements plus faciles, c'est un anti-modèle; les chaînes doivent très, TRÈS rarement être utilisées pour contenir ou contrôler des instructions de code, et lorsque cela doit absolument se produire, la valeur des chaînes que vous utilisez pour contrôler la logique doit être définie de manière centrale et constante de sorte qu'il y ait UNE autorité sur la valeur attendue de toute chaîne particulière.
La raison en est très simple; les chaînes ne sont pas vérifiées par le compilateur pour la syntaxe ou la cohérence avec d'autres valeurs (au mieux, elles sont vérifiées pour la forme appropriée concernant les caractères d'échappement et d'autres formats spécifiques à la langue dans la chaîne). Vous pouvez mettre tout ce que vous voulez dans une chaîne. En tant que tel, vous pouvez obtenir un algorithme / programme désespérément cassé à compiler, et ce n'est que lorsqu'il s'exécute qu'une erreur se produit. Considérez le code suivant (C #):
... tout ce code se compile, essayez d'abord, mais l'erreur est évidente avec un deuxième regard. Il existe de nombreux exemples de ce type de programmation, et ils sont tous sujets à ce type d'erreur, MÊME SI vous définissez des chaînes comme constantes (car les constantes dans .NET sont écrites dans le manifeste de chaque bibliothèque dans laquelle elles sont utilisées, qui doivent puis tous être recompilés lorsque la valeur définie est modifiée afin que la valeur change "globalement"). Comme l'erreur n'est pas détectée au moment de la compilation, elle doit être détectée lors des tests au moment de l'exécution, et cela n'est garanti qu'avec une couverture de code à 100% dans les tests unitaires avec un exercice de code adéquat, qui ne se trouve généralement que dans la sécurité absolue la plus absolue. , systèmes en temps réel.
la source
En fait, ces classes sont encore étroitement couplées. Sauf que maintenant ils sont étroitement couplés de telle manière que le compilateur ne peut pas vous dire quand les choses sont cassées!
Si quelqu'un change "BicycleMake" en "BicyclesMake", personne ne découvrira que les choses sont cassées jusqu'à l'exécution.
Les erreurs d'exécution sont beaucoup plus coûteuses à corriger que les erreurs de compilation - c'est juste toutes sortes de maux.
la source