3 votes

Passage d'objets et meilleures pratiques de conception orientée objet

Je suis un étudiant universitaire qui apprend la programmation. Pour m'entraîner, j'écris un programme de blackjack. J'utilise C++ et j'adopte une approche orientée objet.

J'ai conçu une classe Deck qui, en gros, construit et mélange un jeu de cartes. Le jeu généré est composé d'un tableau de 52 objets de classe Card. C'est ce que j'ai fait jusqu'à présent.

J'ai l'intention de faire en sorte qu'un objet Donneur, qui a un jeu de 52 cartes, distribue une carte à un deuxième objet Joueur, puis distribue à la main du Donneur.

Ma première question est la suivante : Est-ce une mauvaise pratique de rendre le tableau d'objets Carte public dans la classe Deck ?

Je pose cette question car je considère le tableau comme un attribut et on m'a appris que la plupart des attributs doivent être privés. Je ne veux pas commencer à utiliser de mauvaises pratiques ou des pratiques paresseuses dans mes projets et je veux le faire de la bonne façon.

Une autre question : Comment les objets, tels que l'objet Carte utilisé dans mon programme de blackjack, sont-ils généralement déplacés de l'intérieur d'un objet - comme le croupier - vers un second objet comme un joueur ?

3voto

Michael Aaron Safyan Points 45071

Ma première question est la suivante : est-ce une mauvaise pratique de rendre le tableau d'objets Carte public dans la classe Deck ?

Oui. En général, les membres des données doivent toujours être privés. Dans le cadre de la POO, il est bon de créer une interface sans données associées qui définit les opérations pouvant être effectuées sur l'objet, puis de fournir une classe concrète qui implémente cette interface. Les données sont un détail d'implémentation qui ne doit pas être visible dans l'interface ou même dans la classe entièrement concrète. À titre d'exemple, vous pouvez implémenter votre classe en utilisant un tableau d'objets Carte pour le moment, mais vous déciderez peut-être plus tard d'utiliser un jeu de bits où un seul bit indique si la carte est ou n'est pas présente dans le jeu. Si vous rendez votre objet tableau de cartes public, le fait de modifier la représentation de cette manière perturberait les autres utilisateurs de votre classe ; en revanche, si vous le gardez privé, vous pouvez effectuer ce changement sans que cela ait un impact sur les utilisateurs de votre classe.

Une autre question : Comment les objets, tels que l'objet Carte utilisé dans mon programme de blackjack, sont-ils généralement déplacés de l'intérieur d'un objet - comme le croupier - vers un second objet comme un joueur ?

Cela dépend si l'autre objet a besoin d'accéder à l'objet carte original, si l'autre objet conservera l'objet original pendant une longue ou une courte période, ou si l'autre objet ne peut manipuler qu'une copie de la carte. Cela dépend également du fait que la carte soit une classe concrète ou un type polymorphe, car les objets polymorphes ne peuvent être transmis que par pointeur ou par référence (car transmettre des objets polymorphes par valeur entraînera un découpage du code). Avec les objets concrets, vous avez la possibilité de transmettre une copie, sauf si vous devez modifier ou accéder à l'objet original, auquel cas une référence est nécessaire. Le choix de la bonne façon de transmettre des objets est quelque peu compliqué, mais nous espérons que cela vous éclairera :

Passez par valeur si :

C'est un type primitif ou un petit type concret non polymorphe qui n'a pas besoin d'être modifié.

Passer par référence constante -- c'est-à-dire const T& pour un certain type T -- si :

  1. Il n'est pas nécessaire de modifier l'objet original.
  2. Il n'est pas nécessaire de lire l'objet original en dehors de la portée de la fonction.
  3. Il n'est pas nécessaire de lire l'objet au-delà de la portée de la fonction, ou du type est non polymorphe et peu coûteux à copier, vous pouvez donc créer une copie si vous avez besoin de vous y accrocher. l'objet.

Passer par référence -- c'est-à-dire T& pour un certain type T -- si :

  1. Vous devez modifier l'objet original.
  2. Il n'est pas nécessaire de lire/écrire l'objet original en dehors de la portée de la fonction.
  3. Vous n'avez pas besoin de lire l'objet au-delà de la portée de la fonction, ou du type est non polymorphe et peu coûteux à copier, vous pouvez donc créer une copie si vous avez besoin de vous y accrocher. l'objet.

Passer par constante pointeur intelligent vers une constante -- c'est-à-dire const shared_ptr<const T>& pour un certain type T -- si :

  1. Vous devez lire l'objet original à la fois dans la portée de la fonction et au-delà.
  2. Vous devez lire l'objet à la fois dans la portée de la fonction et au-delà, et le type est non polymorphe de sorte qu'il n'est pas possible d'en créer une copie en toute sécurité.

Passer par un pointeur intelligent constant -- c'est-à-dire const shared_ptr<T>& pour un certain type T -- si :

  1. Vous devez lire et écrire l'objet original à la fois dans le cadre de la fonction et au-delà.

J'ai donné chacun des éléments ci-dessus dans un ordre délibéré ; vous devez essayer le premier qui vous convient, et ne passer au suivant que si le précédent ne suffit pas. Je dois également ajouter que boost::call_traits<T>::param_type peut vous aider à choisir entre le passage par valeur et le passage par référence constante dans le cas de types concrets non polymorphes (il peut déterminer, en fonction de la taille de l'objet, si le passage par valeur ou le passage par référence constante est préférable).

1voto

Jerry Coffin Points 237758

Au moins, à mon avis, vous essayez d'en faire trop. En réalité, un jeu de cartes n'a pas de comportement - c'est juste un tas de cartes. Vous n'avez pas un croupier qui dit au jeu de cartes de se mélanger lui-même ; vous avez un croupier qui mélange le jeu. Je ferais la même chose dans un programme - le jeu de cartes serait juste un jeu de cartes. std::vector<card> qui appartient au concessionnaire (et qui devrait presque certainement être privé).

Pour la négociation, chaque joueur aurait son propre std::vector<card> pour sa main. Le croupier passe ensuite à chaque joueur une carte à la fois en appelant la carte du joueur. deal (ou autre).

0voto

In silico Points 30778

1) En général, oui. D'un point de vue conceptuel, les instances du joueur ne manipulent pas les cartes appartenant au croupier, elles devraient donc être privées.

2) Une façon de le faire :

struct Card
{
    Suit suit;
    Rank rank;
};

class Player
{
private:
    void AddCard(Card card);
    friend class Dealer;
};

class Dealer : public Player
{
public:
    void DealTo(Player& player);
};

Dealer dealer;
Player player2;
dealer.DealTo(player2);

0voto

Loki Astari Points 116129

Ma première question est la suivante : est-ce une mauvaise pratique de rendre le tableau d'objets Carte public dans la classe Deck ?

Ça dépend. Mais il est généralement mauvais d'exposer des données publiquement.
En effet, les éléments publics font partie de l'interface et doivent donc être gérés.

Il serait préférable de faire du tableau un membre privé puis d'exposer les actions via l'interface publique. Cela vous permettra de modifier les données privées ultérieurement (par exemple, lorsque vous apprendrez à utiliser un vecteur, vous pourrez remplacer le tableau par un vecteur). Si le tableau était public, vous ne pourriez pas changer le type sans affecter tous les autres types qui utilisent le fait que c'est un tableau).

Principe : cacher les détails de la mise en œuvre.
Cela conduit à un couplage plus lâche entre les types.

Une autre question : Comment les objets, tels que l'objet Carte utilisé dans mon programme de blackjack, sont-ils généralement déplacés de l'intérieur d'un objet - comme le croupier - vers un second objet comme un joueur ?

Retirez-le du tableau dans un objet (et réduisez le tableau pour montrer qu'il a moins de cartes (vous pouvez donc vouloir un type de conteneur qui peut changer de taille)). Ensuite, placez-le dans un autre tableau (conteneur) dans l'objet de destination.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X