J'ai une classe modèle qui ressemble à quelque chose comme ( extrêmement em> simplifié; certains membres et beaucoup, de nombreuses méthodes omises pour la clarté): P> Le problème est que cette classe est cultivée au fil des ans et compte maintenant plusieurs milliers de lignes de code; il est devenu un excellent exemple de la façon de violer le Principe de responsabilité unique . P> Il a des méthodes pour définir le «texte», «état», etc. directement (après la désérialisation) et le même ensemble de méthodes permettant de les définir de l'interface utilisateur, qui a des effets secondaires, comme la mise à jour de «LastChangedDate» et «LastChangeDUser», etc. Certaines méthodes ou des groupes de méthodes existent encore plus de deux fois, avec tout le monde qui fait fondamentalement la même chose, mais légèrement différent. P> Lorsque vous développez de nouvelles parties de l'application, vous utilisez très probablement le mauvais des cinq manières différentes de manipuler Compte tenu de cette classe historiquement cultivée et trop complexe, le but est de séparer tous les différents des préoccupations dans différentes classes, ne laissant que les éléments de données de base. p> idéalement, je préférerais une solution w Ici, un objet Chacune de ces classes spéciales pourrait alors contenir une implémentation concrète réelle de la logique commerciale (une séchis de «texte» pourrait faire quelque chose comme »si le texte à définir commence par une certaine sous-chaîne et l'état est égal à« State1 », réglez-le sur 'State2 '" em>). p> pour charger et stocker tout le modèle, composé de nombreux Lorsque l'utilisateur entre dans un texte spécifique, je souhaite valider cette entrée. La même validation doit être faite si l'entrée provient d'une autre partie de l'application, ce qui signifie que je ne peux pas déplacer la validation dans l'interface utilisateur (en tout cas, la validation de l'interface utilisateur est souvent une mauvaise idée). Mais si la validation se produit dans le Il est clair maintenant que la validation doit être déplacée à l'extérieur de l'interface utilisateur et du modèle, en une sorte de contrôleur (dans une collection de classes MVC). Ceux-ci devraient ensuite décorer / visiter / etc la classe modèle muette réelle avec ses données. P> Quel modèle de conception logicielle convient au mieux à l'affaire décrite, afin de permettre différentes manières de modifier les instances de ma classe ? strong> p> Je demande, car aucun des modèles que je ne connais ne résout tout à fait que je me manque tout à fait que je manque quelque chose ici ... P> Merci beaucoup Pour vos idées! P> h2>class MyModelItem
{
public:
enum ItemState
{
State1,
State2
};
QString text() const;
ItemState state() const;
private:
QString _text;
ItemState _state;
}
myModelitem code>, ce qui le rend extrêmement fastidieux et frustrant. p>
Exigences h2>
myModelitem code> n'a que
Const code> Les membres pour accéder aux données et aux modifications ne peuvent être effectués qu'à utiliser des classes spéciales. P>
première partie de la solution h2>
myModelitem code> objets et certains Plus, le modèle de visiteur ressemble à une solution prometteuse. Je pourrais implémenter plusieurs classes de visiteurs pour différents formats de fichier ou des schémas de base de données et disposer d'un
Enregistrer code> et
charger code> méthode dans
myModelitem code>, qui accepte un tel objet visiteur. Chacun. P>
Question ouverte H2>
myModelitem code> elle-même, j'ai à nouveau deux problèmes: P>
4 Réponses :
plaine Stratégie modèle semble la meilleure stratégie pour moi.
Ce que je comprends de votre déclaration est que: P>
validatetext (chaîne), validateState (itemState) code> ... li>
- chaque source forte> a-un em> validateur fort>. Ce validateur peut être une instance du validateur de base ou peut hériter et remplacer certaines de ses méthodes. LI>
- chaque validateur strong> a-un em> référence au modèle
fort>. li>.
- Une source d'abord définit son propre validateur puis prend la tentative de mutation. LI>
ol>
maintenant, p> xxx pré> Le comportement de différents validateurs peut être différent. p> p>
Merci beaucoup pour votre réponse! Cela pourrait être exactement ce que je cherchais. J'ai commencé à mettre en œuvre le modèle de visiteur pour tous les importateurs / exportateurs et le modèle de stratégie pour les différents mutateurs (par exemple via l'UI).
Bien que vous ne l'indiquez pas explicitement, refactorise des milliers de lignes de code est une tâche ardue, et j'imagine que certains processus incrémentiels sont préférés sur un tout ou rien.
En outre, le compilateur devrait aider autant que possible de détecter des erreurs. S'il s'agit de beaucoup de travail et de frustration maintenant de déterminer quelles méthodes doivent être appelées, elle sera encore pire si l'API avait été faite uniforme. P>
Par conséquent, je proposerais d'utiliser le enveloppez une collection d'API mal conçue avec une seule API bien conçue (selon les besoins de tâches) P>
BlockQuote> Parce que c'est essentiellement ce que vous avez: une collection d'API dans une classe, qui doit être séparée en différents groupes. Chaque groupe aurait sa propre façade, avec ses propres appels. Donc, le myModelitem actuel, avec toutes ses invocations de méthodes différentes soigneusement fabriquées au fil des ans: p> devient: p> si Nous supprimons les membres actuels de MyModelitem à MyModelitemData, puis nous obtenons: p> bien sûr, des variantes de mise en œuvre existent ici. Il pourrait également bien être: p> dépendant plus de restrictions sur la mémoire, la création d'objets, la concurrence, etc. Le point est que jusqu'à présent, il est tout droit (Je ne dirai pas la recherche et la remplacement) et le compilateur aide à chaque étape du moyen d'attraper des erreurs. P> La bonne chose à propos de la façade est qu'elle peut former une interface à plusieurs bibliothèques / Des classes. Après avoir fractionné les choses, les règles de l'entreprise sont toutes dans plusieurs façades, mais vous pouvez les refroidir davantage: p> et le code de l'interface graphique ne doit pas être changé un bit. P> Après tout ce que vous pourriez choisir de normaliser les façades, de sorte qu'ils ont tous les mêmes noms de méthodes. Cela n'est pas nécessaire, cependant, et de la clarté, cela pourrait même être préférable de garder les noms distincts. Peu importe, encore une fois, le compilateur aidera à valider tout refactoring. P> (Je sais que je souligne beaucoup le compilateur beaucoup, mais dans mon expérience, une fois que tout a le même nom et travaille à travers une ou plusieurs couches de indirecte, il devient une douleur de savoir où et quand quelque chose se passe mal.) p> Quoi qu'il en soit, voici comment je le ferais, car cela permet de séparer de gros morceaux de code assez rapidement, dans une manière contrôlée, sans avoir à penser trop. Il fournit une belle tranche pour une peau de peau plus poussée. Je suppose qu'à un moment donné, la classe MyModelitem devrait être renommée à MyModelitemMediator. P> bonne chance avec votre projet. P> P>
Si ma solution à parler avec le client n'est pas possible, puis-je aller pour votre solution, car c'est le plus flexible. Une classe servant plus d'objectifs puis prévu à l'origine peut être mieux divisée dans différentes classes pour retrouver le principe de responsabilité singulier pour chaque classe. Une façade rend ça possible.
Parler avec le client et refactoring n'a pas besoin d'être exclusif mutuel. Dans la description du problème, je ne suis pas sûr qu'ils soient déjà à la "fin de la ligne". En tant que tel, l'application est toujours une énorme ressource WRT le domaine problématique et des solutions spécifiques. Après un refactoring initial, il peut être utilisé pour démarrer des pourparlers concrets avec le client sur ce qui fonctionne et quelles parties doivent être repensées pour anticiper le développement futur. Et oui, à ce stade, je pense que la flexibilité et la conservation des options est importante.
La refactorise une partie d'une application qui a disparu pendant une longue période ne vaut pas toujours l'effort. Je n'ai jamais été dans cette situation sans avoir besoin d'une conversion de données. Le plus souvent, le problème sous-jacent que le modèle de données est désynchronisé. Refactoring une partie de l'application laissez ce problème devenir intact. J'ai suscité votre réponse parce que je pense que c'est la meilleure solution si ce n'est que parler au client n'est pas possible, mais je sais de l'expérience qu'elle est mutuellement exclusive. La cause est le modèle de données étant hors de synchronisation avec la réalité. Le refactoring ne résout pas ce problème.
Si le modèle de données n'est pas obsolète, le refactoring ne le résoudra pas, non. Mais la séparation du modèle de données de base de toutes sortes d'autres préoccupations aidera à déterminer s'il est obsolète, ou non, et combien. Donc, ce que je voulais dire, c'est que le refactoring peut apporter un certain contrôle et constituer un meilleur point de départ pour des pourparlers que "l'application est totalement incontrôlable".
Je suis d'accord avec vous que la séparation des préoccupations est une bonne étape. Je ne pense pas que nous sommes vraiment en désaccord. Ma première phrase dans le commentaire précédent devrait lire «que si elle est hors de contrôle, alors ..». En ce qui concerne cette application, je ne sais pas (j'ai commencé ma réponse avec 'si je comprends ..'). J'adore mon point de départ pour des pourparlers en fonction du contact avec le client. Puisque je ne sais pas, je ne peux rien dire à ce sujet et ne répond que théoriquement. C'est plus noir et blanc par rapport à la réalité colorée. Sans contradiction, puis-je dire que je suis pleinement d'accord avec votre dernier commentaire.
Cette approche a l'air très prometteuse, merci beaucoup pour votre réponse! J'aime bien quand le compilateur peut prendre en charge / vérifier les étapes de refactorisation.
Vous êtes très bienvenu. Lire vos réponses à @Loekbergman, c'est plus ou moins la situation que j'avais à l'esprit lorsque j'ai écrit cela. Pas une solution de fin en soi, mais la préparation des étapes de l'analyse fonctionnelle / de la refonte. Très bien que vous puissiez le faire en coopération avec le client. Je suis d'accord avec Loek que cela ressemble bien à la gestion du projet. Encore une fois, bonne chance avec ça!
Si je comprends votre problème correctement, puis-je ne pas décider que le motif de conception a choisi. Je pense que j'ai vu du code comme celui-ci plusieurs fois auparavant et le principal problème de mon point de vue a toujours été que le changement était construit sur le changement de changement sur le changement. La classe avait perdue est l'objectif initial et servait maintenant plusieurs objectifs, qui n'étaient tous pas clairement définis et définis. Le résultat est une grande classe (ou une grande base de données, code spaghetti, etc.), qui semble être indispensable mais un cauchemar pour la maintenance. P>
La grande classe est le symptôme d'un processus qui est sorti du contrôle. C'est là que vous pouvez le voir arriver, mais je suppose que lorsque cette classe a été récupérée, de nombreuses autres classes seront les premières à repenser. Si je suis correct, alors existe-t-il aussi beaucoup de corruption de données, car dans de nombreux cas, la définition des données incertaine. p>
Mon conseil serait de retour à votre client, parlez des processus métier, de réorganiser la gestion de projet de la demande et d'essayer de déterminer si l'application sert bien le processus métier. Ce n'est peut-être pas - je suis dans ce type de situation plusieurs fois dans différentes organisations. Si le processus d'entreprise est compris et que le modèle de données est converti en ligne avec le nouveau modèle de données, pouvez-vous remplacer l'application par une nouvelle conception, ce qui est beaucoup plus facile à créer. La grande classe qui existe maintenant ne doit plus être réorganisée, car sa raison de l'existence est partie. Cela coûte de l'argent, mais le maintien coûte maintenant de l'argent. Une bonne indication pour la refonte est si de nouvelles fonctionnalités ne sont plus implémentées, car elle est devenue trop chère ou d'erreur sujette à exécuter. P>
Je suis d'accord: Retournez au client, parlez de la façon dont tout doit changer. En théorie. En pratique, le client vous dira généralement qu'ils vous ont déjà payé de résoudre tout cela, qu'ils vous ont tendu les exigences, que c'était votre travail de bien le faire, et franchement, qu'ils n'ont pas le budget de fabrication changements qui n'ajouteront aucune fonctionnalité. Si vous jouez correctement vos cartes, vous pouvez obtenir une certaine indemnité de refactoring, mais il s'agit généralement d'une décision sur le coût immédiat par rapport au paiement à long terme pour le créateur de logiciels. Les clients ne veulent pas payer plus pour ce qui fonctionne déjà.
@Jer: Dans la majorité des cas, vous avez absolument raison. Cependant, j'ai eu plusieurs missions avec un problème similaire et la tâche de résoudre. Votre dernière phrase est le creux: cela peut finir de fonctionner. Une fois, c'était la mission de savoir qui était le véritable propriétaire de problème: le client ou la société que j'ai travaillé. C'était difficile. :-)
Merci beaucoup pour votre réponse! Retour au client est quelque chose que nous avons déjà fait. Maintenant, nous avons d'abord besoin de restructurer le code de base et de séparer différentes préoccupations qui peuvent alors être concrètement être mises à jour avec le problème des clients des clients. Toutes les opérations différentes doivent rester (chargement / sauvegarde, importation / exportation, modifier via UI, etc.) mais elles sont totalement mélangées dans une classe spaghetti. Et vous avez absolument raison, il y a déjà une corruption de données. En outre, il est devenu presque impossible d'ajouter de nouvelles fonctionnalités ni même de changer le ...
Pour être exactement, ce que nous essayons de faire est quelque chose entre le refactoring et la refonte: séparant d'abord le désordre, analysant quelles sont les fonctionnalités respectives Devraient i> faire (cette partie du code a été écrite par une autre société), puis Simplifier et corriger les choses. L'interface de la classe peut changer ici est bien, car elle n'est utilisée que dans notre propre application et nous pouvons également modifier tout le code dépendant.
J'ai déjà montré à un client dans une présentation que son modèle d'entreprise avait changé au fil des ans, mais son modèle de données n'avait pas. Cela a créé un problème à long terme, car les données ne pouvaient pas correspondre correctement. L'organisation client l'a admis, mais n'a rien changé. La cause était une lutte interne pour le pouvoir dans laquelle les bonnes données au mauvais endroit contribuaient aux administrateurs de sa lutte pour le pouvoir. C'était la principale cause de problème dans l'application. Très intéressant de voir être externe, mais il était très frustrant pour beaucoup de personnes travaillant dans l'organisation.
La solution d'une façade fournie par @jer est une bonne imo. Être une façade sera alors la seule responsabilité de cette classe. Avez-vous eu le temps de créer une documentation fonctionnelle du code? En tant qu'ingénieur logiciel, je suis toujours plus intéressé par la raison pour laquelle quelque chose a été fait, alors la façon dont cela a été fait. Je pouvais voir ce dernier, mais c'était parfois un vrai casse-tête de comprendre quel but il servi en premier lieu. Bonne chance pour votre projet! Il semble bon comment vous manipulez la situation.
Je vais essayer de vous donner une perspective différente de la situation que vous avez. Veuillez noter que explications sont écrites dans mes propres mots forts> pour la simplicité. Cependant, les termes mentionnés proviennent des modèles d'architecture d'applications d'entreprise. P>
Vous concevez la logique commerciale de l'application. Donc, enregistrement actif fort>: une entité commerciale qui peut se cru elle-même et peut gérer
la logique commerciale liée à elle-même. p>
blockQuote>
La logique commerciale contenue dans l'enregistrement actif a augmenté et est devenue difficile à gérer. C'est situation très typique forte> avec des enregistrements actifs. C'est là que vous devez passer du modèle d'enregistrement actif au modèle code> Mapeur de données code>. P>
Alors, ici, nous sommes arrivés à la solution évidente: Créez une mappeuse de données pour l'entité code> MyModelitem code>. Simplifiez l'entité pour ne pas gérer la cartographie de lui-même. Migrer la gestion de la cartographie sur le mappeur de données. P>
Si le Plusieurs notes sur la manière dont je voudrais le mettre en œuvre: p>
En général, vous devez modèle em> votre application sans les données à l'esprit. Ensuite, concevez mappeuse pour gérer les transformations des objets vers les données et vice-Verca. P>
Si la validation est la même dans tous les cas, puis mettez-la dans l'entité, comme cela me semble naturel. Dans la plupart des cas, cette approche est suffisante. P>
Si la validation diffère et dépend de quelque chose em>, abstrait que quelque chose d'absence et appelez la validation à travers l'abstraction. D'une manière (si cela dépend de l'héritage) serait de mettre la validation dans le mappeur ou de l'avoir dans la même famille d'objets que MAPER, créé par l'usine abstraite commune. P> myModelitem code> doit être une sorte d'entité commerciale. Je dirais que c'est l'enregistrement actif
code> que vous avez. P>
myModelitem code> participe à l'héritage, envisagez de créer un mappeur de données abstrait et des mappeurs de données concrets pour chaque classe de béton que vous souhaitez planer de manière différente. P>
maintenant à propos de la validation h2>
+1 pour une question bien structurée. Réponse courte: je préfère le service de validateur séparé utilisé pour cet objet. Et il peut utiliser la composition sur l'héritage pour mettre en œuvre sa propre méthode "Validate ()".
Avez-vous pensé à l'utilisation du modèle de décorateur, d'ajouter un comportement à un objet sans que l'objet soit changé directement? en.wikipedia.org/wiki/decorator_pattern
@ Merlin069: Le motif de décorateur a été l'une de nos premières idées, mais les frais de vue de la mémoire n'étaient pas acceptables (il y a des millions d'instances de cette classe, qui grandiraient alors si nous avions besoin d'un ou de plusieurs décorateurs par instance). En outre, le décorateur semble plus approprié pour la lecture des opérations que pour les opérations de mutation dans la plupart des cas.