Manière la plus propre d'écrire des logiciels logiquement procéduraux dans un langage OO

12

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 #regions 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?

Lombard
la source
2
Je ne suis pas sûr qu'il s'agit strictement d'un doublon, mais veuillez lire Méthode unique avec de nombreux paramètres par rapport à de nombreuses méthodes qui doivent être appelées dans l'ordre .
@Snowman, en tant que débutant dans l'écriture de morceaux de code plus volumineux, il est rassurant de voir que je ne suis pas la seule personne à rencontrer ces types de problèmes. J'ai bien aimé votre réponse à cette question. Ça aide.
Lombard
@Lombard l'idée de réduire la complexité à un niveau gérable est une très bonne compétence à développer. Personne ne peut comprendre une grande base de code, pas même Superman (peut-être Batman cependant). Trouver des moyens d'exprimer des idées simplement et d'auto-documenter le code est crucial.
"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 résumé page, l'imprime, puis se ferme. ": Êtes-vous sûr de ne pas confondre" procédural "et" impératif "? Les procédures et les objets sont (peuvent être) impératifs, c'est-à-dire qu'ils peuvent exprimer des opérations à effectuer dans une séquence.
Giorgio

Réponses:

18

É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.

Tyler Durden
la source
1
+1 pour "la clarté est beaucoup plus importante [que l'efficacité]"
Stephen
Pourriez-vous me diriger vers un lien qui décrit ce qui constitue un "programme OO mal écrit" ou ajouter plus d'informations à ce sujet dans une édition?
Lombard
@Lombard, je l'ai fait.
Tyler Durden
1

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 DatabaseInfoGetterc'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 DatabaseInfoGetterbesoin de référencer d'autres classes comme ProductTableEntryou 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:

int main() 
{
    var thisthat = Database.GetThisThat();
}

public class ThisThatClass
{
    public String This;
    public String That;
}

public static class Database 
{
    public ThisThatClass GetThisThat()
    {
        return new ThisThatClass 
        {
            This = GetThis(),
            That = GetThat()
        };
    }

    public static String GetThis() 
    { 
        //stuff
    }
    public static String GetThat() 
    { 
        //stuff
    }

}

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.

Scepticisme
la source
0

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:

var summary = SummaryPageGenerator.GeneratePage(thisThing, thatThing);

en cela:

var summary = SummaryPageGenerator.GeneratePage(new This(DatabaseInfoGetter), new That(DatabaseInfoGetter));

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

Wim ten Brink
la source
-3

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.

Pablo Granados
la source
1
Les acronymes sont acceptables s'ils sont largement utilisés et connus dans le domaine ciblé par le logiciel. Essayez d'écrire un logiciel de contrôle du trafic aérien sans utiliser le - nous avons des acronymes composés d'acronymes - personne ne saurait de quoi vous parliez si vous avez utilisé le nom complet. Des choses comme HTTP pour la mise en réseau, l'ABS dans l'automobile, etc. sont tout à fait acceptables.
mattnz