web-dev-qa-db-fra.com

Comment puis-je promouvoir l'utilisation du modèle de constructeur dans mon équipe?

Notre codeBase est vieux et de nouveaux programmeurs, comme moi-même, apprenez rapidement à le faire la façon dont cela est fait pour l'uniformité. Pensant que nous devons commencer quelque part, je l'ai pris sur moi-même pour refacturer une classe de titulaires de données en tant que telle:

  • Méthodes de setter retirées et fait tous les champs final (je prends "_ final est bon" axiomatiquement). Les setters n'ont été utilisés que dans le constructeur, à mesure qu'il s'avère, cela n'avait donc aucun effet secondaire.
  • Introduit une classe de constructeurs

La classe Builder était nécessaire parce que le constructeur (qui est ce qui a été invité à refactoring en premier lieu) s'étend sur 3 lignes de code. Il a beaucoup de paramètres.

Comme la chance l'aurait, un compagnon d'équipe de la mienne travaillait sur un autre module et qu'il a eu besoin de la nécessité des Setters, car les valeurs qu'il avait obligées sont devenues disponibles à différents moments de la circulation. Ainsi, le code ressemblait à ceci:

public void foo(Bar bar){
    //do stuff
    bar.setA(stuff);
    //do more stuff
    bar.setB(moreStuff);
}

J'ai fait valoir qu'il devait utiliser le constructeur à la place, car se débarrasser des Setters permet aux champs de rester immuables (ils m'ont entendu parler de l'immuabilité auparavant), et aussi parce que les constructeurs permettent une création d'objets de manière transactionnelle. J'ai esquissé le pseudocode suivant:

public void foo(Bar bar){
    try{
        bar.setA(a);
        //enter exception-throwing stuff
        bar.setB(b);
    }catch(){}
}

Si cette exception incendie, bar aura des données corrompues, qui auraient été évitées avec un constructeur:

public Bar foo(){
        Builder builder=new Builder();
    try{
        builder.setA(a);
        //dangerous stuff;
        builder.setB(b);
        //more dangerous stuff
        builder.setC(c);
        return builder.build();
    }catch(){}
    return null;
}

Mes coéquipiers ont réalisé que l'exception en question ne tiendra jamais, ce qui est assez juste pour cette zone de code particulière, mais je pense qu'il manque la forêt pour l'arbre.

Le compromis consistait à revenir à l'ancienne solution, à savoir utiliser un constructeur sans paramètres et tout mettre avec des setters au besoin. La justification était que cette solution suit le KISS principe, que la mine viole.

Je suis nouveau chez cette entreprise (moins de 6 mois) et je suis pleinement conscient que j'ai perdu celui-ci. La ou les questions que j'ai sont:

  • Y a-t-il un autre argument pour utiliser des constructeurs au lieu de "vieillir"?
  • Est le changement que je propose même vraiment la peine?

mais réellement,

  • Avez-vous des conseils pour mieux présenter de tels arguments lors de la déformation d'essayer quelque chose de nouveau?
22
rath

Penser que nous devons commencer quelque part, je l'ai pris sur moi de factoriser une classe de support de données

C'est là que ça a mal tourné - vous avez fait un changement architectural majeur à la base de code tel qu'il est devenu très différent de ce qui était attendu. Vous surpris vos collègues. La surprise est bonne que si elle implique une partie, le principe de moindre surprise est d'or (même si elle implique des parties, alors je sais porter caleçon propre!)

Maintenant, si vous pensiez que ce changement en valait la peine, il aurait été beaucoup mieux d'esquisser le pseudo-code montrant le changement et la présenter à vos collègues (ou du moins celui qui a le rôle le plus proche de l'architecte) et de voir ce qu'ils pensaient avant de faire quoi que ce soit. Comme il est, vous êtes allé rogue, pensiez que tout était à vous de codebase jouer avec vous comme cela est jugé approprié. Un joueur de l'équipe, pas un joueur d'équipe!

Je pourrais être d'être un peu dur sur vous, mais je suis à l'endroit opposé: consultez un code et de penser " WTF est arrivé ici ?! ", pour trouver le nouveau gars (son presque toujours le petit nouveau) a décidé de changer une charge de choses en fonction de ce qu'il pense que le " droit chemin " devrait être. Vis avec l'histoire SMC trop ce qui est un autre problème majeur.

46
gbjbaanb

Comme la chance l'aurait, un compagnon d'équipe de la mienne travaillait sur un autre module et qu'il a eu besoin de la nécessité des Setters, car les valeurs qu'il avait obligées sont devenues disponibles à différents moments de la circulation.

Comme décrit, je ne vois pas ce qui fait le code besoin Setteurs. Je n'en sais probablement pas assez sur le cas d'utilisation, mais pourquoi ne pas attendre que tous les paramètres soient connus avant d'instantifier l'objet?

Comme alternative, si la situation nécessite des configurations: Offrez un moyen de geler votre code à ce que les Setteurs lancent une erreur d'assertion (erreur de programmeur A.K.A.) Si vous essayez de modifier les champs après que l'objet est prêt.

Je pense enlever les Setters et utiliser Final est la méthode "appropriée" de le faire, ainsi que d'appliquer une immuabilité, mais c'est vraiment ce que vous devriez passer votre temps avec votre temps?

Conseils généraux

Old = Bad, New = Good, mon chemin, votre chemin ... Ce type de discussion n'est pas constructif.

Actuellement, la manière dont votre objet est utilisé suit une règle implicite. Cela fait partie de la spécification non écrite de votre code et votre équipe pourrait penser qu'il n'y a pas beaucoup de risques pour d'autres parties du code de la mauvaise utilisation. Ce que vous voulez faire ici, c'est de rendre la règle plus explicite avec un constructeur et des contrôles de compilation.

Dans certains cas, le refactoring de code est lié à la théorie fenêtre cassée : vous vous sentez juste de résoudre tout problème comme vous le voyez (ou faites une note pour une réunion ultérieure de "amélioration de la qualité") afin que le code Semble toujours agréable à travailler avec, est sûr et propre. Vous et votre équipe n'avez pas la même idée de ce qui est "assez bon", cependant. Ce que vous considérez comme une occasion de contribuer et de rendre le code mieux, de bonne foi, est probablement vu différemment de l'équipe existante.

Au fait, votre équipe ne doit pas être supposée être nécessairement souffrant d'inertie, de mauvaises habitudes, de manque d'éducation, ... Le pire que vous puissiez faire est de projeter une image d'un savoir-savoir-tout Qui est là pour leur montrer comment coder. Par exemple, l'immuabilité n'est pas quelque chose NOUVEAU, vos pairs ont probablement lu à ce sujet, mais simplement parce que ce sujet devient devenir Populaire Dans Blogs ne suffit pas pour les convaincre passer du temps à changer son code.

Après tout, il existe des moyens sans fin d'améliorer une base de code existante, mais cela coûte temps et argent. Je pourrais regarder votre code, appelez-le "vieux" et trouvez des choses à Nitpick. Par exemple: Aucune spécification formelle, pas suffisamment d'affirmations, peut-être pas assez de commentaires ou de tests, pas assez de couverture de code, d'algorithme non optimal, trop d'utilisation de la mémoire, sans utiliser le modèle de conception "meilleur" dans chaque cas, etc. ad nausam. Mais pour la plupart des points que je souleverais, vous penseriez: ça va, mon code fonctionne, il n'est pas nécessaire de passer plus de temps dessus.

Peut-être que votre équipe pense que ce que vous suggérez du point de rédaction de rendements. Même si vous aviez trouvé un réel instance d'un bug dû au type d'exemple que vous montrez (à l'exception), ils ne le feraient probablement pas une fois et ne veulent pas refroidir le code.

La question est donc à propos de Comment Pour passer votre temps et votre énergie, étant donné qu'ils sont limité? Il y a des modifications continues effectuées sur des logiciels, mais votre idée commence généralement par un score négatif jusqu'à ce que vous puissiez lui fournir de bons arguments. Même si vous avez raison à propos de l'avantage, ce n'est pas un argument suffisant en soi, car il se terminera dans le " Nice d'avoir, un jour ..." Panier.

Lorsque vous présentez vos arguments, vous devez faire des chèques "SAINITY": essayez-vous de vendre l'idée ou vous-même? Et si quelqu'un d'autre a présenté cela à l'équipe? Souhaitez-vous trouver des défauts dans leur raisonnement? Essayez-vous d'appliquer des meilleures pratiques générales ou avez-vous pris en compte les spécificités de votre code?

Ensuite, vous êtes sûr de ne pas comparaître comme si vous essayez de réparer les "erreurs" de votre équipe. Cela aidait à montrer que vous n'êtes pas votre idée, mais vous pouvez prendre des commentaires raisonnablement. Plus vous faites cela, plus vos suggestions auront la possibilité d'être suivies.

21
coredump

Comment puis-je promouvoir l'utilisation du modèle de constructeur dans mon équipe?

Tu ne le fais pas.

Si votre constructeur a 3 lignes de paramètres, il est trop grand et trop complexe. Aucun modèle de conception ne résoudra cela.

Si vous avez une classe dont les données sont disponibles à des moments différents, il devrait s'agir de deux objets différents, ou votre consommateur devrait agréger les composants et alors Construire l'objet. Ils n'ont pas besoin d'un constructeur pour le faire.

17
Telastyn

Vous semblez plus concentré sur la raison de bien travailler avec les autres. Pire encore, vous reconnaissez que ce que vous faites est une lutte de pouvoir et pourtant manquer de penser à la manière dont les choses sont susceptibles de ressentir au peuple dont l'expérience et l'autorité vous êtes difficile.

Inverser ça.

Concentrez-vous sur le travail avec les autres. Encadrer vos pensées comme suggestions et idées. Demandez-leur de parler à travers leurs idées avant de poser des questions pour les faire penser à la tienne. Évitez les affrontements directs et sortez de l'extérieur à accepter que cela continue à faire des choses que la voie du groupe (pour l'instant) est plus productive que de combattre une bataille en montée pour changer le groupe. (Bien que ramenez les choses.) Faites de travailler avec vous une joie.

Faites cela de manière cohérente et vous devriez créer moins de conflits et trouver plus de vos idées sont adoptées par d'autres. Nous souffrons tous de l'illusion que l'argument logique idéal va wow les autres et gagner la journée. Et si cela ne fonctionne pas de cette façon, nous pensons qu'il devrait.

Mais c'est en conflit direct avec la réalité. La plupart des arguments sont perdus avant que le premier point logique ne soit effectué. Même parmi des personnes logiques prétendument, la psychologie l'emporte sur la raison. Convaincre les gens consiste à obtenir des obstacles psychologiques passées à bien entendre et non à avoir une logique parfaite.

16
btilly

Y a-t-il un autre argument pour utiliser des constructeurs au lieu de "vieillir"?

"Quand tu as un nouveau marteau, tout ressemblera à un clou." - C'est ce que j'ai pensé quand j'ai lu votre question.

Si j'étais ta coleague, je vous demanderais "pourquoi ne pas initialiser tout objet dans le constructeur?" C'est ce que c'est la fonctionnalité après tout. Pourquoi utiliser le modèle de constructeur (ou d'autre conception)?

Est le changement que je propose même vraiment la peine?

Il est difficile de dire de ce petit extrait de code. Peut-être que c'est - vous et vos coléagues doivent l'évaluer.

Avez-vous des conseils pour mieux présenter de tels arguments lors de la déformation d'essayer quelque chose de nouveau?

Oui, en savoir plus sur le sujet avant de la discuter. Armez-vous avec de bons arguments et de bonnes raisons avant d'entrer dans la discussion.

11
BЈовић

J'ai été dans la situation où j'ai été recruté pour mes compétences. Quand je dois voir le code, eh bien .. c'était plus horrible. Lors de l'essai de la nettoyer, une personne qui a été là-bas, tout en supprimant toujours les changements, car ils ne voient pas les avantages. Cela ressemble à quelque chose que vous décrivez. Cela s'est terminé par moi qui quitte cette position et je peux maintenant évoluer beaucoup mieux dans une entreprise qui a une meilleure pratique.

Je ne pense pas que vous ne devriez que jouer avec les autres dans l'équipe parce qu'ils y ont été plus longs. Si vous voyez vos membres de votre équipe en utilisant de mauvaises pratiques dans les projets que vous travaillez, c'est une responsabilité que vous devez le signaler à l'OMI, comme vous l'avez. Sinon, alors vous n'êtes pas une ressource très précieuse. Si vous dirigez les choses sur les choses encore et encore, personne n'écoute, vous demandez une gestion d'un travail de remplacement ou de changement. Il est très important de ne pas vous permettre de vous adapter à de mauvaises pratiques.

À la question réelle

Pourquoi utiliser des objets immuables? Parce que vous savez ce que vous faites affaire.

Dites que vous avez une connexion DB qui est transmise autour qui vous permet de changer l'hôte via un setter, vous ne savez pas quelle connexion elle est. Être immuable, alors vous savez.

Dites toutefois que vous avez une structure de données pouvant être extraites de la persistance, puis de re-persisté avec de nouvelles données, alors il est logique que cette structure ne soit pas immuable. C'est la même entité, mais les valeurs peuvent changer. Utilisez plutôt des objets immuables objets de valeur comme des arguments.

Ce que vous concentrez la question sur la question est cependant un modèle de constructeur pour vous débarrasser des dépendances dans le constructeur. Je dis une grosse graisse [~ # ~ # ~] Non [~ # ~] à cela. L'instance est évidemment complexe déjà. N'essayez pas de le masquer, Fixez-le !

Tl; dr

La suppression des setters peut être correcte, selon la classe. Le modèle de constructeur ne résout pas le problème, le masquant. (La saleté sous le tapis existe toujours même si vous ne pouvez pas le voir)

7
superhero

Bien que toutes les autres réponses soient très utiles (plus utile que le mien se produise), il existe une propriété de constructeurs que vous n'avez pas encore mentionnés: en tournant la création d'un objet dans un processus, vous pouvez appliquer des contraintes logiques complexes.

Vous pouvez exposer par exemple que certains champs sont définis ensemble ou dans un ordre spécifique. Vous pouvez même retourner une implémentation différente de l'objet cible en fonction de ces décisions.

// business objects

interface CharRange {
   public char getCharacter();
   public int getFrom();
   public int getTo();
}

class MultiCharRange implements CharRange {
   final char ch;
   final int from;
   final int to;

   public char getCharacter() { return ch; }
   public int getFrom() { return from; }
   public int getTo() { return to; }
}

class SingleCharRange implements CharRange {
   final char ch;
   final int position;

   public char getCharacter() { return ch; }
   public int getFrom() { return position; }
   public int getTo() { return position; }
}

// builders

class Builder1 {
   public Builder2 character(char ch) {
      return new Builder2(ch);
   }
}

class Builder2 {
   final char ch;
   int from;
   int to;

   public Builder2 range(int from, int to) {
      if (from > to) throw new IllegalArgumentException("from > to");
      this.from = from;
      this.to = to;
      return this;
   }

   public Builder2 zeroStartRange(int to) {
      this.from = 0;
      this.to = to;
      return this;
   }

   public CharRange build() {
      if (from == to)
         return new SingleCharRange(ch,from);
      else 
         return new MultiCharRange(ch,from,to);
   }
}

Ceci est un exemple nodié qui n'a pas beaucoup de sens, mais il démontre les trois propriétés: le caractère est toujours défini en premier, les limites de la plage sont toujours définies et validées ensemble, et vous choisissez votre implémentation à la hauteur de la construction.

Il est facile de voir comment cela peut entraîner un code d'utilisateur plus lisible et plus fiable, mais comme vous pouvez également le voir, il y a un inconvénient: des terrains et beaucoup de code de constructeur (et j'ai même laissé de côté les constructeurs pour raccourcir les extraits). Donc, vous essayez de calculer le compromis, en posant des questions comme:

  • Est-ce que nous instancient seulement de l'objet d'un ou de deux endroits? Est-ce susceptible de changer?
  • Peut-être devrions-nous appliquer ces contraintes en refactant l'objet d'origine dans une composition d'objets plus petits?
  • Pourriez-nous finir par passer le constructeur autour de la méthode à la méthode?
  • Pourrions-nous simplement utiliser une méthode d'usine statique à la place?
  • Cette partie d'une API externe a-t-elle besoin d'être aussi infaillible et slique que possible?
  • Environ combien de notre arriéré actuel de bugs aurait pu être évité si nous avions des constructeurs?
  • Combien d'écriture de documentation supplémentaire sauverions-nous?

Vos collègues ont simplement décidé que le compromis ne serait pas bénéfique. Vous devez discuter des raisons ouvertement et honnêtement, de préférence sans recourir à des principes abstraits, mais concentrez-vous sur les implications pratiques de cette solution ou cela.

2
biziclop

On dirait que cette classe est en fait plus d'une classe, placée ensemble dans la même définition de fichier/classe. Si c'est une DTO, il devrait alors être possible de l'installer en une fois et d'être immuable. Si vous n'utilisez pas tout ce qu'il s'agit de champs/propriétés dans une transaction, il enfreint presque certainement le principe de responsabilité unique. Il pourrait s'agir de la rompre dans des classes DTO immuables plus petites, plus cohérentes et immuables. Pensez à la lecture Clean Code Si vous ne l'avez pas déjà fait, vous êtes mieux en mesure d'articuler les problèmes et des avantages de faire un changement.

Je ne pense pas que vous puissiez concevoir du code à ce niveau par le comité, à moins que vous n'ayez littéralement la programmation de la foule (cela ne ressemble pas à ce genre d'équipe), alors je pense que c'est bien de faire un changement basé sur votre Meilleur jugement, puis prenez-le sur le menton s'il s'avère que vous avez jugé faux.

Cependant, tant que vous êtes en mesure d'articuler les problèmes potentiels avec le code tel qu'il est, vous pouvez toujours débattre de une solution est requise , même Si le vôtre n'est pas l'accepté. Les gens sont alors libres d'offrir des alternatives.

"- opinions fortes, faiblement tenues " est un bon principe - vous continuez de confiance en fonction de ce que vous savez, mais si vous trouvez de nouvelles informations qui comptent, cela, soyez modeste et de descendre et entrez un discussion sur ce que la solution correcte devrait être. La seule mauvaise réponse est de laisser tomber pour s'aggraver.

2
Neil Barnwell