J'ai passé la grande majorité de ma carrière de programmation à l'aide Java et très OO basé C++. Je suis vraiment intéressé à apprendre à penser plus de procédure, donc j'ai donc Commencez à faire des pratiques en utilisant un sous-ensemble plus restreint de C++ (fondamentalement c) et d'essayer de garder mon code très procédural.
Ma question se situe dans la superficie de la structure d'un programme. En Java, je pense constamment à des objets ... alors quand je crée une classe, je pense à:
Dans le cas du code de procédure, comment fonctionne ce genre de chose? Permet de dire vouloir une structure pour représenter une voiture. Je créerais un en-tête pour le définir ... Mais où vont les fonctions qui doivent fonctionner sur un type de voiture? Souhaitez-vous également définir toutes les fonctions liées à la voiture dans la même ligne de fichiers que vous voudriez Java classe? Je pense totalement à cela dans ce mauvais sens, en ce sens qu'il ne devrait pas y avoir de fonctions liées à la voiture depuis le L'emphase ne devrait pas être sur l'objet de la voiture, mais sur les fonctions elles-mêmes? IS L'idée d'une fonction membre a totalement jeté la fenêtre?
La ligne de fond est mon cerveau a été câblée pour réfléchir aux objets, encapsulation, polymorphisme, etc. et je ne peux pas sembler envelopper la tête autour de rien d'autre. Comment puis-je pratiquer?
Tout conseil est apprécié!
Pour être honnête, cela semble un peu comme aller en arrière et la programmation avec une main derrière votre dos, mais si par "structuré", vous voulez dire, comme comment les gens ont créé des programmes avant l'orientation de l'objet dans les langues de procédure, il s'agit de la façon dont vous commencez à penser à le problème. Dans votre quatrième paragraphe, vous réfléchissez essentiellement à une manière orientée objet. La façon dont les gens ont écrit dans des langues comme C, Pascal et Basic Back dans la journée devaient commencer par le code et non les données. Une analogie serait qu'un programme informatique était comme une recette; une série d'étapes à suivre.
Donc, vous ne diriez pas "disons que vous vouliez une structure pour représenter une voiture". Vous diriez "je veux me transporter à l'épicerie". Vous ne commencerez que penser à des objets comme "voiture" lorsque vous êtes arrivé à l'étape qui l'avait requis.
Pour utiliser un exemple plus concret: Supposons que vous souhaitiez envoyer un tas de lettres aux adresses dans un fichier. Pensant à OO, vous commenceriez probablement à penser à des choses comme des objets d'adresse. Mais avec de vieilles langues structurées, vous penseriez probablement d'abord des étapes:
Vous allez transférer chacun de ceux-ci en étapes, comme
4:
Pour que tout cela plus facile, vous mettriez presque certainement les données d'adresse dans une sorte de structure pour le maintenir ensemble, mais cela serait motivé par les étapes que vous preniez. Tu ne commencerais pas avec ça.
La chose est la suivante: cela devient vraiment difficile à gérer une fois que vous arrivez à des programmes de toute taille significative. C'est pourquoi presque tout le monde pense aux objets ces jours-ci à moins qu'ils écrivent un petit script rapide.
Démarrer des structures de données.
Ecrire des fonctions opérant sur ces structures de données.
Si vous souhaitez encapsulation, faites-le au niveau des modules, pas d'objets.
Si vous voulez un polymorphisme, utilisez des fonctions supérieures à l'ordre, pas une expédition virtuelle.
C'est à peu près ça. OOP comme pratiqué n'est pas un départ significatif de la programmation procédurale. C'est principalement un ensemble de défaillances raisonnables pour la construction de programmes de procédure.
La façon dont nous avions l'habitude de faire une sorte de OOP de retour dans les jours de c était en déclarant un struct
puis déclarant des fonctions qui acceptent un tel struct
comme leur premier paramètre. Puis, parfois, la nécessité de polymorphisme lancerait, nous aurions donc notre struct
contienne non seulement des champs de données, mais également des indications sur des fonctions. C'est assez inutile, vraiment, de faire à la main ce que c ++ serait Faites-nous pour nous avec beaucoup moins de lignes de code, plus sûrement, plus élégantes, et de manière à ce que tous ceux qui sentent C++ comprennent, au lieu de chaque magasin qui roule leur propre.
Si vous vraiment VOULEZ ABANDON OOP en faveur de la programmation structurée, vous devez vous éloigner des objets et commencer à penser en termes de modules, mais vous verrez bientôt que vous serez naturellement et inévitablement graviter vers OOP, et il n'y a rien de mal à cela, car OOP est un moyen très pratique de penser à la programmation et j'ose même dire une nécessité pour Construire des systèmes complexes.
Avant C++, nous avions l'habitude d'écrire des modules qui exposent des fonctions exposées, et assez souvent, ces modules étaient des gestionnaires qui contenaient une certaine pluralité d'entités, elles exposeraient donc une sorte de "poignée" pour vous permettre d'indiquer quelle entité vous faisait référence, comme par exemple la L'affaire est avec le système d'exploitation "Poignées de fichier" de l'ancien, que vous avez peut-être entendue. C'était déjà OOP, même s'il n'était pas encore appelé par ce nom. Au début, la poignée était généralement une int
et il a été utilisé par le module comme un index dans une matrice interne de struct
s, mais à un moment donné, quelqu'un a compris que puisque le code de l'utilisateur n'est pas supposé. Pour essayer d'interpréter la poignée de quelque manière que ce soit, la poignée pourrait tout aussi facilement être un pointeur réel sur l'interne struct
; Personne ne remarquerait et personne ne devrait l'inquiéter.
Ainsi, afin de faire n'importe quoi a) utile et b) à grande échelle, vous devez penser à OOP, mais si vous n'utilisez pas de OOP LANGUE, votre implémentation sera alors derrière un plat monolithique interface, et vous devrez toujours faire beaucoup de travail manuel sans esprit afin d'obtenir certaines choses comme l'héritage et le polymorphisme (que vous volonté besoin si vous essayez de faire n'importe quoi de la complexité non triviale ,) Et qui vous sera fourni pour vous si vous utilisez un OOP Language.
La ligne de fond est mon cerveau a été câblée pour réfléchir aux objets, encapsulation, polymorphisme, etc. et je ne peux pas sembler envelopper la tête autour de rien d'autre. Comment puis-je pratiquer?
Le plus grand - sinon la seule différence entre la programmation procédurale et la programmation orientée objet est la notion d'héritage.¹ Par exemple, il est simple de mettre en œuvre un type de liste polymorphe dans C, mais la traduction des relations de succession impliquerait beaucoup de travail. Tant qu'aucune relation d'héritage n'est impliquée, l'écriture
Car myCar;
myCar.start();
en C++ est juste une façon fantaisie d'écrire
Car myCar = car_construct();
car_start(myCar);
dans C. Donc, dans la programmation procédurale, vous résolvez les problèmes en définissant des structures de données et des traitements sur ces structures de données, ce qui fonctionne assez de la même manière que dans les langages OO, sauf que vous ne pouvez pas utiliser des abstractions de héritage .²
Si vous souhaitez progresser dans la programmation de procédure, je suggérerais d'étudier les programmes écrits par d'autres et de rédiger des programmes similaires. Vous apprendrez beaucoup en comparant votre solution à l'autre et vous demandez à quel point les différences sont importantes. Les scripts de lecture et d'écriture d'écriture sont probablement une bonne étape pour apprendre la programmation procédurale, car il s'agit d'une compétence qui est également utile à elle-même. Vous trouvez généralement beaucoup de scripts groupés avec votre système d'exploitation - et vous pouvez trouver scripts de bonne qualité au hasard sur Internet.
Il convient également de noter que l'un des rafricains de complexité importante et pratiquement sans bugs n'a jamais été écrit dans une langue procédurale. Le programme que je pense est Tex qui a été écrit dans une langue de type Pascal, et la source de code est décrite de manière approfondie dans un livre Tex: le programme qui Vous pourriez envisager de lire. Il est facile de trouver ou de produire des copies électroniques du livre, et le campus scientifique le plus proche aura une copie difficile dans sa bibliothèque.
¹ Ce que vous ne mentionnez pas sur votre question!
² Héritage est idéal pour résoudre des problèmes d'ingénierie logicielle, mais cela ne résout pas de problème pratique. Donc, compte tenu du problème résolu par le logiciel, les résousts de héritage meta .
[.____] ³ Je n'en savais pas un autre!
La façon dont je le vois, d'aller de la programmation appropriée orientée objet à un style de procédure, vous devez:
supprimer le comportement de vos objets; Faire-leur juste des conteneurs de données (structures de données)
mettre en œuvre un comportement dans des classes séparées (services); Ils prendront les structures de données comme des arguments et effectueront des mesures en fonction des cas d'utilisation des applications.
utilisez les mêmes critères de la qualité de conception que vous utiliseriez dans OOP (cohésion, couplage, etc.), ne les appliquez que sur les services.
Comme les autres l'ont souligné, il s'agit probablement d'une régression de OOP, en particulier pour les grands projets. Pourrait être une approche valide pour les scénarios simples.
Je recommanderais de lire une partie de la programmation structurée "classiques": livres de Ed Yourdon, Larry Constantine, en particulier beaucoup de choses de Dave Parnas. Ils parlent beaucoup sur la conception de haut niveau, à l'aide de graphiques de structure et de diagrammes de flux de données, de casser le code dans des modules et de la manière de gérer le couplage et la cohésion. C'est une lecture intéressante, et certains des outils (par exemple, des diagrammes de flux de données) peuvent toujours être utilisés aujourd'hui. Je pense parfois qu'une approche structurée est une meilleure solution à certains problèmes (ceux impliquant une analyse d'analyse ou des machines d'État, généralement), mais ce n'est pas une opinion populaire.
Quel est cet objet représentant? Que peut faire cet objet? Qu'est-ce que les autres doivent être capables de faire? Qu'est-ce que je expose, qu'est-ce que je me cache? Etc
Lorsque vous créez une structure, la seule chose que vous besoin Pour répondre, c'est ce que sa structure de données devrait être. Il ne peut rien faire, il ne peut rien cacher, tout ce qui est une méthode pratique d'accès à la mémoire unie. C'est peut-être une bonne idée d'essayer de répondre à d'autres questions sur la structure, mais il n'y a pas de grammaire formelle pour écrire la réponse à ces questions. (C'est aussi vrai pour beaucoup de bonnes questions sur OO objets.)
mais où vont les fonctions qui doivent fonctionner sur un type de voiture?
Partout où vous pensez que cela convient au mieux, la structure ne prend pas une décision pour vous, il ne peut même pas dicter que le code pertinent devrait être une fonction dédiée. Décider d'une implémentation lorsque vous devez réellement l'utiliser.
L'idée d'une fonction membre a-t-elle totalement jeté la fenêtre?
Il n'est pas nécessaire que la structure function(object, parameters)
est équivalente à l'utilisation de base des fonctions des membres. Il devrait compiler à peu près la même chose.
Encapsulation
Un peu simplifié, toute l'encapsulation est jamais utilisée est un message d'erreur si vous essayez de le casser. Bien sûr, il peut être agréable d'avoir des garanties formelles sur quel code ne peut pas faire, mais vous obtenez surtout les mêmes avantages en définissant les frontières non compilatrices. Dans tous les cas, la seule méthode d'encapsulation que vous perdez réellement est des membres privés et des méthodes. D'après mon expérience, ce ne sont pas aussi importants que des champs variables.
Polymorphisme
Le gros cas pour OO. Résout beaucoup de problèmes étranges et n'a pas de simple remplacement. Mais il y a beaucoup de structures qui font des choses similaires, voici quelques-uns:
Faire une structure avec de l'espace pour toutes les données nécessaires à tout sous-type souhaité, faites une marque variable de quel sous-type est. Vous pouvez utiliser plusieurs marques de ce type afin de faire quelque chose qui ressemble à une héritage multi-héritage. Plutôt que de surcharger des méthodes, le code qui traite de telles structures doit simplement vérifier le type et agir en conséquence. Il peut perdre de la mémoire, mais dans la plupart des cas, ce n'est probablement pas une grosse affaire. Si vous avez beaucoup de sous-types différents, il pourrait toutefois diffuser le code définissant les différences entre les sous-types de votre programme.
Inclure les pointeurs de fonction comme variables de membre, ceux-ci peuvent être définis sur les fonctions correspondant au sous-type.
Inclure les pointeurs sur les sous-structures, avec mémoire pour ce sous-type spécifique.
Faites tous les sous-types souhaités, et copier-coller tout code qui s'appliquent à tous. Cela ne semble pas bon, mais si les différences prennent beaucoup plus de code que les similitudes, cela pourrait être un choix raisonnable. La version Ne-à-Fe-this-At-Home est l'endroit où vous construisez la mise en page de la mémoire afin que le même code fonctionne sur différentes structures.
À des fins d'apprentissage, je pense que c'est une idée merveilleuse d'étaler une fonctionnalité avancée, surtout si elle a tendance à surutiliser. Dans le code du monde réel, vous devriez bien sûr utiliser le OO Caractéristiques où cela a du sens.