0
votes

C ++ Sous-classes d'amis Accès aux membres privés (modèle de stratégie)

J'ai codé une méthode de croisement d'un algorithme génétique (voir https: // fr .wikipedia.org / wiki / crossover_ (génétique_algorithm) ).

La méthode de crossover modifie les membres privés de la classe de chromosome, mais je l'ai sorti du chromosome dans une classe de base virtuelle séparée Crossovergrate (ami de chromosome ) Pour garder chaque méthode de crossover bien encapsulée dans une sous-classe, c'est-à-dire le modèle de stratégie GOF (voir https: // fr .wikipedia.org / wiki / Stratégie_pattern ).

Le problème est que le problème est que les sous-classes de croisement ne peuvent pas accéder aux membres privés de chromosome, car l'amitié n'est pas héritée en C ++. Les 2 seules solutions que je vois sont les suivantes:

1) Ajouter des méthodes d'accesseur à la classe de base virtuelle pure par exemple. CrossoverStrategy :: getgenes () pour rendre les membres privés de chromosome accessibles aux sous-classes. Parce que CrossOverStrategy ne peut pas anticiper tout ce que ses sous-classes peuvent vouloir faire du chromosome, je dois l'exposer à l'avant. Ugly!

2) Transférer déclarer chaque sous-classe de crosssovergrie et en fait explicitement un ami de chromosome. Cela se sent légèrement moins laid, au moins conserve les interfaces et le nettoyant de code. Je me penche vers cette option pour l'esthétique.

Toutes meilleures suggestions de conception? Code ci-dessous. xxx


8 commentaires

Pas seulement laid, fatal. Tu ne retourneras pas la référence à la variable locale.


Bob est ton ami. Vous lui faites confiance avec vos secrets. Mais qu'en est-il de la pharmacie de Bob Dirtbag d'un fils, Griff? Faites-vous automatiquement confiance en griffon parce que vous faites confiance à Bob? Nan. Si les classes dérivées d'un ami étaient automatiquement ami s, vous pouvez sous-classer tout ce que vous vouliez et complètement schtroumpf sur encapsulation. Pas un bon plan.


Si vous ne pouvez pas éventuellement passer un pointeur nul pour un pointeur, envisagez de passer par des références au lieu des pointeurs. Tout sauf élimine les familles entières de becs accidentels.


Étant donné que vous souhaitez que plusieurs classes / fonction ont accès à des données qui constituent un chromosome , pourquoi les données ne sont-elles pas public ?


@Peter parce que n'importe qui pourrait accéder aux données privées. Je préférerais que seul l'ami (et ses sous-classes) pouvait accéder. C'est une bonne option 3, mais le risque est que nous encapsulons entièrement du chromosome.


«Si seulement l'ami (et ses sous-classes». L'amitié n'est pas héritée précisément parce que n'importe qui peut créer une sous-classe de n'importe quoi. Avec votre système d'accès, vous avez une protection nulle.


Il y a toujours quelqu'un qui trouvera une raison de bowevote. Il est possible que la question laisse des informations importantes telles que "doit autoriser non- const accès à des classes" et "d'autres classes doit pouvoir utiliser chromosome de la manière suivante ... "Ce pourrait être la question est extrêmement spécifique à vos objectifs et de peu d'utilisation à quiconque et à" non utile ". Cela pourrait être quelque chose d'aussi trivial que l'exemple de code de travail ne fonctionne pas en raison d'un support égaré.


En pensant à cela plus, Engineering logiciel peut être meilleur ajustement pour cette question. Le débordement de la pile est généralement meilleur pour "pourquoi ça ne marche pas?" et soigneusement construit "Comment est-ce que je?" des questions. "Comment devrais-je?" est généralement dans le domaine de l'ingénierie. Notez que j'ai lié à la page d'aide sur la poser des questions. Il y a des attentes différentes de ce qui fait une bonne question.


3 Réponses :


2
votes

L'option 2 doit être rejetée car elle n'a pas échoué. Vous allez continuellement modifier chromosome code> pour le garder à jour avec le nouveau CrossOverStrategies code>.

L'option 1 est une idée étrange car elle place la fonction getter pour chromosome code> Membres de données de l'extérieur de chromosome code>. Je peux voir des cas où il s'agit d'une idée attrayante, si getgenes code> est créé, mais je ne suis pas convaincu ici. Considérez plutôt P>

Option 1-A H1>
#include "Chromosome.h"
#include "CrossoverStrategies.h" // A bad idea. Forces recompilation when strategies are added

int main()
{
    Chromosome * p1 = ChromosomeFactory(/* some construction parameters here */);
    p1->m_genes.push_back(0.0); // will fail to compile (incomplete type)
    Chromosome * p2 = ChromosomeFactory(/* some construction parameters here */);
    
    // probably should hide the next line with a factory as well
    CrossoverStrategy * strategy = new CrossoverStrategyExample1();
    strategy->crossover(p1, p2);
}


7 commentaires

Merci pour les suggestions. Je suis au courant du fait que j'aurais besoin de modifier continuellement Chromosome.h lorsque j'ajoute une nouvelle stratégie. Mais c'est une limitation connue et (seulement) 2 lignes de code doivent changer, il semble donc que cela semble être un compromis acceptable pour la propreté du code.


En ce qui concerne votre option 1 Suggestion: L'objectif est de fournir un accès non constitué aux membres privés de Chromosome. Si nous faisons le getter non-cons autres, une classe aléatoire pourrait jouer avec les privés de Chromsome. De toute évidence, je souhaite limiter cet accès uniquement à CrossSoverStrategy et à ses sous-classes. N'oubliez pas que nous prenons en effet ce qui était à l'origine chromosome :: crossover () et l'externalisant afin que nous puissions échanger avec différentes stratégies. Javascript le fait avec des fonctions anonymes mais je trouve C ++ Lamdbas laid et illisible par comparaison.


@Cridgit Quelles types de choses ne peuvent pas crossovertrategies faire avec chromsome ? Si la réponse n'est rien, voir si vous faites chromsome public et appliquer L'idiome PIMPL a du sens.


Le code comprend un exemple de ce qu'il a à voir avec le chromosome privés: "Parent1-> M_Genes [i] = 0,0;" Il doit accéder et modifier cela et peut-être d'autres membres privés. J'ai regardé Pimpl mais qui semble également cacher complètement les membres privés, alors je ne suis pas sûr de savoir comment je pourrais atteindre le résultat souhaité avec PIMPL, mais merci de la suggestion.


L'idée de PIMPL est la définition de la classe est uniquement exposée aux fichiers qui ont besoin de la définition. Tout le monde ne peut que voir et gérer une référence. Ils ne peuvent pas interagir avec l'objet lui-même. Donc, un Public Accès chromosome permet à Direct, non ami accès à ses internes à ceux qui ont une définition des internes et chacun d'autre < Code> Chromosome pourrait aussi bien être entièrement privé dès le bas aux constructeurs et aux fonctions des membres spéciaux.


Merci pour l'exemple complet - cela semble très approprié. Une petite question: si j'ai quelques méthodes publiques pour exposer par exemple. Chromosome :: getgenecount (), où puis-je faire cela sans casser le motif pimpl?


J'ai compris comment procéder à cela en utilisant votre exemple ci-dessus sur la base du motif PIMPL et c'est exactement ce que je cherchais. MERCI. J'ai utilisé une variable chromosomeimpl chromosomeimpl dans le fichier d'en-tête. Ensuite, j'ai effectué les membres chromosomeimpl au public dans le fichier source afin que seules les sous-classes de crossoverstrateggie vous accèdent. Fonctionne magnifiquement!



2
votes

La première option évidente est de déterminer si les membres de chromosome doivent ou non être public . Étant donné que vous souhaitez qu'un nombre arbitraire de classes aient accès à ses données, une option évidente est de rendre ces données public .

Une deuxième option est pour chromosome à Fournissez un getter public et un réglage pour les données concernées, telles que; xxx

alors tout crossoverstrategy et ses classes dérivées doivent faire, étant donné des pointeurs valides à chromosome S, demande les gènes, faire tout ce qui est nécessaire, et (lors de la fin) fournit un nouvel ensemble de gènes à la sélection de chromosomes .

Encapsulation de chromosome est préservé par diverses mesures, car le seul moyen de modifier les gènes est via une fonction de membre de chromosome , c'est-à-dire qu'il n'y a aucun moyen de changer de gènes dans un chromosome à l'extérieur Contrôle de la classe chromosome . Qui permet à chromosome de faire des chèques qu'il aime et rejeter les gènes bad si on le souhaitait.

Il n'y a pas besoin d'une autre classe ou de fonction d'être un ami de Chromosome . Un avantage clé est qu'il n'est pas nécessaire de modifier la classe chromosome crossoverstrategy .

Le compromis est que les gènes sont récupérés et des modifications en copiant l'ensemble complet (succès de la copie de performances potentielles). Mais il évite la nécessité de casser l'encapsulation de la classe chromosome en fournissant, directement ou indirectement, une référence à ses membres privés à toutes les autres classes.

Si la copie de l'ensemble complet de Les chromosomes sont une mauvaise chose, entraînent des fonctions de membre supplémentaires de chromosome qui permettent à l'appelant de demander des modifications partielles (par exemple, de mettre à jour des gènes particuliers, insérer un ensemble de gènes dans un endroit spécifié dans le vecteur des gènes, etc). Ces fonctions supplémentaires doivent fonctionner sur le même principe: tous les changements de gènes dans un chromosome GO via des fonctions de membre de chromosome , et il n'y a pas de mécanisme "porte arrière" pour les autres code pour se faufiler à travers.

Si vous voulez vraiment, vous pouvez faire le SETTER et Getter privé membres de chromosome et faire uniquement la classe de base CrossOverStrategy A ami . Alors tous crossoverstrategy doit fournir est fournir des aides protégés qui n'appellent que les aides privés de chromosome . xxx

De cette façon, seules les classes dérivées de crossoverstrategy peuvent accéder aux aides protégés . Si le fonctionnement de chromosome change, la seule classe à adapter dans ce cas est la base crossoverstrategy classe - car ses classes dérivées n'accordent pas (directement) accès chromosome du tout.


1 commentaires

Peter merci pour cette réponse solide, il semble être le moyen le plus "correct" et certainement pas aussi laid que les 2 options que j'ai suggérées. J'attendrai de voir s'il y a une autre idée brillante avant d'accepter cela comme réponse.



0
votes

Votre idée est fondamentalement défectueuse.

D'une part, vous dites que vous ne voulez pas juste n'importe qui pour être capable de jouer avec le vecteur de gènes.

D'autre part, vous voulez tout descendant de crossoverstrategy pour être capable de gâcher avec le vecteur de gènes.

Mais il y a une contradiction. "Tout descendant" d'une classe est "juste n'importe qui". Tout utilisateur est capable d'hériter d'une classe et de faire ce qu'ils veulent avec votre vecteur de gènes. Ils n'ont besoin que de passer par un petit inconvénient à une fois d'héritage de crossoverstrategy .

C'est la raison pour laquelle l'amitié en C ++ n'est pas héritée. Si c'était le cas, le contrôle d'accès serait inutile en présence de classes d'amis.

Bien sûr, vous pouvez simuler une amitié héritable en ayant un getter protégé dans crossoverstrategy comme l'une des réponses suggère. Mais cela défait le but du contrôle d'accès. Il rend les tableaux de gènes aussi bons que public.


2 commentaires

Bien que je conviens que de faire des variables privées, le public doit être évité, sauf dans des circonstances spécifiques, l'encapsulation n'est pas binaire mais un spectre. Être capable d'accéder aux variables des membres en sous-classement, un ami est une étape supérieure à 100% d'accès public. Je ne suis donc pas d'accord pour que cela vaincra entièrement le but du contrôle d'accès - le concept a un certain mérite ci-dessus, ce qui rend tout public.


@Cridgit Je ne vois pas le spectre. L'accès est ouvert ou fermé. Il n'y a pas de demi-ouvert ou presque fermé.