Je suis ingénieur électricien et je ne sais pas ce que je fais. Veuillez enregistrer les futurs responsables de mon code.
Récemment, j'ai travaillé sur un certain nombre de petits programmes (en C #) dont la fonctionnalité est logiquement "procédurale". Par exemple, l'un d'eux est un programme qui collecte des informations à partir de différentes bases de données, utilise ces informations pour générer une sorte de page de résumé, l'imprime, puis se ferme.
La logique requise pour tout cela est d'environ 2000 lignes. Je ne veux certainement pas bourrer tout cela dans un seul main()
et ensuite "nettoyer" avec #region
s comme certains développeurs précédents l'ont fait (frisson).
Voici quelques choses que j'ai déjà essayées sans grande satisfaction:
Créez des utilitaires statiques pour chaque élément grossier de fonctionnalité, par exemple un DatabaseInfoGetter, SummaryPageGenerator et un PrintUtility. Faire ressembler la fonction principale à:
int main()
{
var thisThing = DatabaseInfoGetter.GetThis();
var thatThing = DatabaseInfoGetter.GetThat();
var summary = SummaryPageGenerator.GeneratePage(thisThing, thatThing);
PrintUtility.Print(summary);
}
Pour un programme, je suis même allé avec des interfaces
int main()
{
/* pardon the psuedocode */
List<Function> toDoList = new List<Function>();
toDoList.Add(new DatabaseInfoGetter(serverUrl));
toDoList.Add(new SummaryPageGenerator());
toDoList.Add(new PrintUtility());
foreach (Function f in toDoList)
f.Do();
}
Rien de tout cela ne se sent bien. À mesure que le code s'allonge, ces deux approches commencent à devenir laides.
Quelle est la bonne façon de structurer des trucs comme ça?
Réponses:
Étant donné que vous n'êtes pas un programmeur professionnel, je vous recommande de vous en tenir à la simplicité. Il sera BEAUCOUP plus facile pour le programmeur de prendre votre code procédural modulaire et de le rendre OO plus tard, qu'il ne le sera pour eux de réparer un programme OO mal écrit. Si vous n'êtes pas expérimenté, il est possible de créer des programmes OO qui peuvent se transformer en un gâchis impie qui ne vous aidera ni celui qui viendra après vous.
Je pense que votre premier instinct, le code "this thing-that thing" dans le premier exemple est la bonne piste. Il est clair et évident ce que vous voulez faire. Ne vous inquiétez pas trop de l'efficacité du code, la clarté est beaucoup plus importante.
Si un segment de code est trop long, divisez-le en morceaux de la taille d'une bouchée, chacun ayant sa propre fonction. S'il est trop court, envisagez d'utiliser moins de modules et d'en mettre plus en ligne.
---- Post-scriptum: OO Design Traps
Travailler avec succès avec la programmation OO peut être délicat. Il y a même des gens qui considèrent que tout le modèle est défectueux. Il y a un très bon livre que j'ai utilisé lors de mon premier apprentissage de la programmation OO appelé Thinking in Java (maintenant dans sa 4e édition). Le même auteur a un livre correspondant pour C ++. Il y a en fait une autre question sur les programmeurs traitant des pièges courants dans la programmation orientée objet .
Certains pièges sont sophistiqués, mais il existe de nombreuses façons de créer des problèmes de manière très basique. Par exemple, il y a quelques années, un stagiaire de mon entreprise a écrit la première version d'un logiciel dont j'ai hérité et il a créé des interfaces pour tout ce qui pourraitun jour, avoir plusieurs implémentations. Bien sûr, dans 98% des cas, il n'y avait qu'une seule implémentation, le code a donc été chargé avec des interfaces inutilisées, ce qui a rendu le débogage très ennuyeux car vous ne pouvez pas revenir en arrière lors d'un appel d'interface, vous devez donc faire un recherche de texte pour l'implémentation (bien que maintenant j'utilise IntelliJ, il y a une fonctionnalité "Afficher toutes les implémentations", mais à l'époque je ne l'avais pas). La règle ici est la même que pour la programmation procédurale: toujours coder en dur une chose. Seulement lorsque vous avez deux choses ou plus, créez une abstraction.
Un type similaire d'erreur de conception peut être trouvé dans l'API Java Swing. Ils utilisent un modèle de publication-abonnement pour le système de menu Swing. Cela fait de la création et du débogage des menus dans Swing un cauchemar complet. L'ironie est qu'elle est complètement inutile. Il n'y a pratiquement jamais de situation dans laquelle plusieurs fonctions devraient "s'abonner" à un clic de menu. De plus, la publication-abonnement a été un raté complet car un système de menus est normalement toujours utilisé. Ce n'est pas comme si les fonctions s'abonnaient puis se désabonnaient au hasard. Le fait que les développeurs "professionnels" de Sun aient fait une telle erreur montre à quel point il est facile, même pour les pros, de faire des erreurs monumentales dans la conception OO.
Je suis un développeur très expert avec des décennies d'expérience en programmation OO, mais même je serais le premier à admettre qu'il y en a des tonnes que je ne connais pas et même maintenant, je suis très prudent en utilisant beaucoup d'OO. J'avais l'habitude d'écouter de longues conférences d'un collègue qui était un fanatique OO sur la façon de faire des dessins particuliers. Il savait vraiment ce qu'il faisait, mais en toute honnêteté, j'ai eu du mal à comprendre ses programmes parce qu'ils avaient des modèles de conception si sophistiqués.
la source
La première approche est très bien. Dans les langages procéduraux, comme C, l'approche standard consiste à diviser la fonctionnalité en espaces de noms - utiliser des classes statiques comme
DatabaseInfoGetter
c'est fondamentalement la même chose. Évidemment, cette approche conduit à un code plus simple, plus modulaire et plus lisible / maintenable que de tout mettre dans une seule classe, même si tout est décomposé en méthodes.En parlant de cela, essayez de limiter les méthodes pour effectuer le moins d'actions possible. Certains programmeurs préfèrent un peu moins de granularité, mais les méthodes énormes sont toujours considérées comme nuisibles.
Si vous rencontrez toujours des difficultés avec la complexité, vous devrez probablement interrompre encore plus votre programme. Créez plus de niveaux dans la hiérarchie - peut-être
DatabaseInfoGetter
besoin de référencer d'autres classes commeProductTableEntry
ou quelque chose. Vous écrivez du code procédural mais rappelez-vous, vous utilisez C #, et la POO vous donne un tas d'outils pour réduire la complexité, par exemple:Faites également attention aux classes statiques. Les classes de base de données sont de bons candidats .. tant que vous avez généralement une base de données .. vous avez l'idée.
En fin de compte, si vous pensez aux fonctions mathématiques et que C # ne convient pas à votre style, essayez quelque chose comme Scala, Haskell, etc. Ils sont cool aussi.
la source
Je réponds 4 ans en retard, mais lorsque vous essayez de faire des choses procédurales dans un langage OO, vous travaillez sur un contre-modèle. Ce n'est pas quelque chose que vous devriez faire! J'ai trouvé un blog qui aborde cet anti-modèle et montre une solution, mais il nécessite beaucoup de refactoring et un changement de mentalité. Voir Code procédural dans le code orienté objet pour plus de détails.
Comme vous avez mentionné «ceci» et «cela», vous suggérez essentiellement que vous avez deux classes différentes. Une classe (mauvais nom!) Et une classe. Et vous pourriez par exemple traduire ceci:
en cela:
Maintenant, il serait intéressant de voir ces 2000+ lignes de code procédural et de déterminer comment différentes parties pourraient être regroupées dans des classes distinctes et déplacer diverses procédures vers le bas dans ces classes.
Chaque cours doit être simple et concentré sur une seule chose. Et il me semble que certaines parties du code traitent déjà de superclasses, qui sont en réalité beaucoup trop grandes pour être utiles. Le DatabaseInfoGetter, par exemple, semble très confus. Qu'obtient-il de toute façon? Cela semble trop générique. Pourtant, SummaryPageGenerator est terriblement spécifique! Et PrintUtility est à nouveau générique.
Donc, pour commencer, vous devez éviter les noms génériques de vos classes et commencer à utiliser des noms plus spécifiques à la place. La classe PrintUtility, par exemple, serait davantage une classe Print avec une classe Header, une classe Footer, une classe TextBlock, une classe Image et peut-être un moyen d'indiquer comment elles se dérouleraient dans l'impression elle-même. Espace de noms PrintUtility , cependant.
Pour la base de données, histoire similaire. Même si vous utilisez Entity Framework pour l'accès à la base de données, vous devez le limiter aux éléments que vous utilisez et le diviser en groupes logiques.
Mais je me demande si vous êtes toujours confronté à ce problème après 4 ans. Si c'est le cas, alors c'est un état d'esprit qui doit être changé loin du "concept procédural" pour le "concept orienté objet". Ce qui est difficile si vous n'avez pas l'expérience ...
la source
Je suis à mon avis, vous devriez changer votre code pour qu'il soit simple, cohérent et lisible.
J'ai écrit quelques articles de blog sur ce sujet, et croyez-moi, ils sont simples à comprendre: qu'est-ce qu'un bon code
Simple: Tout comme une carte de circuit imprimé contient différentes pièces, chacune ayant une responsabilité. Le code doit être divisé en parties plus petites et plus simples.
Cohérent: utilisez des patters, une norme pour nommer les éléments et les choses. Vous aimez les outils qui fonctionnent tout le temps de la même manière et vous voulez savoir où les trouver. C'est la même chose avec le code.
Lisible: n'utilisez pas d'acronymes à moins qu'ils ne soient largement utilisés et connus dans le domaine ciblé par le logiciel (mattnz), préférez des noms prononçables clairs et faciles à comprendre.
la source