Question

accesseurs sont souvent critiqués comme étant pas bon OO. D'autre part la plupart du code OO Je l'ai vu a de vastes accesseurs.

Quand les accesseurs justifiés? Essayez-vous d'éviter de les utiliser? Sont-ils galvaudé en général?

Si votre langue préférée a des propriétés (Le mien), alors ces choses sont également considérés comme des accesseurs pour cette question. Ils sont même du point de vue de la méthodologie orientée objet. Ils ont juste plus belle syntaxe.

Sources de critique Getter / Setter (dont certains ont été des commentaires pour leur donner une meilleure visibilité):

Pour indiquer la critique simplement: accesseurs vous permettent de manipuler l'état interne des objets à l'extérieur de l'objet. Cela constitue une violation encapsulation. Seul l'objet lui-même devrait se soucier de son état interne.

Et un exemple Version de procédure de code.

struct Fridge
{
    int cheese;
}

void go_shopping(Fridge fridge)
{
     fridge.cheese += 5;
}

Version mutateur de code:

class Fridge
{
     int cheese;

     void set_cheese(int _cheese) { cheese = _cheese; }
     int get_cheese() { return cheese; }
 }

void go_shopping(Fridge fridge)
{
     fridge.set_cheese(fridge.get_cheese() + 5);        
}

Les accesseurs ont fait le code beaucoup plus compliqué sans donner l'encapsulation appropriée. Parce que l'état interne est accessible à d'autres objets que nous ne gagnons pas beaucoup en ajoutant ces accesseurs.

La question a été discuté précédemment sur Stack Overflow:

Était-ce utile?

La solution

Avoir des accesseurs n'a pas en soi briser l'encapsulation. Qu'est-ce que l'encapsulation de rupture ajoute automatiquement un getter et un setter pour chaque membre de données (tous les champ , en java lingo), sans lui donner aucune pensée. Bien que ce soit mieux que de faire tous les membres de données publiques, il est seulement un petit pas loin.

Le point d'encapsulation est pas que vous ne devriez pas être en mesure de savoir ou de changer l'état de l'objet à partir de l'extérieur de l'objet, mais que vous devrait avoir une politique raisonnable pour le faire.

  • Certains membres de données peut être tout à fait interne à l'objet, et devrait ont ni getters ni setters.

  • Certains membres de données doivent être en lecture seule, donc ils peuvent avoir besoin, mais getters pas setters.

  • Certains membres de données peuvent avoir besoin de rester cohérents entre eux. Dans un tel cas, vous ne seriez pas fournir un setter pour chacun d'eux, mais un seul méthode pour les mettre en même temps, de sorte que vous pouvez vérifier la les valeurs de cohérence.

  • Certains membres de données ne peut devoir être modifié d'une certaine manière, par exemple comme incrémentée ou décrémentée d'une valeur fixe. Dans ce cas, vous fournirait une increment() et / ou méthode decrement() plutôt qu'un poseur.

  • D'autres encore peut réellement besoin d'être en lecture-écriture, et aurait à la fois un getter et un poseur.

Prenons un exemple d'un class Person. Disons qu'une personne a un nom, un numéro de sécurité sociale, et un âge. Disons que nous ne permettons pas aux gens de changer jamais leur nom ou un numéro de sécurité sociale. Cependant, devrait être augmenté l'âge de la personne 1 chaque année. Dans ce cas, vous fournir un constructeur qui initialiser le nom et le SSN aux valeurs données, et qui serait initialiser l'âge à 0. Vous fournirez également une méthode incrementAge(), ce qui augmenterait l'âge de 1. Vous aimerez aussi fournir getters pour tous les trois. Aucun setters sont nécessaires dans ce cas.

Dans cette conception vous permettez à l'état de l'objet à contrôler à l'extérieur de la classe, et vous permettre d'être changé de l'extérieur de la classe. Cependant, vous ne laissez pas l'état à modifier de façon arbitraire. Il y a une politique qui stipule effectivement que le nom et le SSN ne peuvent pas être modifiés à tout, et que l'âge peut être incrémenté de 1 an à la fois.

Maintenant, nous allons dire une personne a aussi un salaire. Et les gens peuvent changer d'emploi à volonté, ce qui signifie que leur salaire va aussi changer. Pour modéliser cette situation, nous avons pas d'autre moyen, mais de fournir une méthode de setSalary()! Permettre le salaire à modifier à volonté est une politique tout à fait raisonnable dans ce cas.

Par ailleurs, dans votre exemple, je donnerais la Fridge classe la putCheese() et les méthodes de takeCheese(), au lieu de get_cheese() et set_cheese(). Ensuite, vous avez encore l'encapsulation.


public class Fridge {
  private List objects;
  private Date warranty;

  /** How the warranty is stored internally is a detail. */
  public Fridge( Date warranty ) {
    // The Fridge can set its internal warranty, but it is not re-exposed.
    setWarranty( warranty );
  }

  /** Doesn't expose how the fridge knows it is empty. */
  public boolean isEmpty() {
    return getObjects().isEmpty();
  }

  /** When the fridge has no more room... */
  public boolean isFull() {
  }

  /** Answers whether the given object will fit. */
  public boolean canStore( Object o ) {
    boolean result = false;

    // Clients may not ask how much room remains in the fridge.
    if( o instanceof PhysicalObject ) {
      PhysicalObject po = (PhysicalObject)o;

      // How the fridge determines its remaining usable volume is a detail.
      // How a physical object determines whether it fits within a specified
      // volume is also a detail.
      result = po.isEnclosedBy( getUsableVolume() );
    }

     return result;
  }

  /** Doesn't expose how the fridge knows its warranty has expired. */
  public boolean isPastWarranty() {
    return getWarranty().before( new Date() );
  }

  /** Doesn't expose how objects are stored in the fridge. */
  public synchronized void store( Object o ) {
    validateExpiration( o );

    // Can the object fit?
    if( canStore( o ) ) {
      getObjects().add( o );
    }
    else {
      throw FridgeFullException( o );
    }
  }

  /** Doesn't expose how objects are removed from the fridge. */
  public synchronized void remove( Object o ) {
    if( !getObjects().contains( o ) ) {
      throw new ObjectNotFoundException( o );
    }

    getObjects().remove( o );

    validateExpiration( o );
  }

  /** Lazily initialized list, an implementation detail. */
  private synchronized List getObjects() {
    if( this.list == null ) { this.list = new List(); }
    return this.list;
  }

  /** How object expiration is determined is also a detail. */
  private void validateExpiration( Object o ) {
    // Objects can answer whether they have gone past a given
    // expiration date. How each object "knows" it has expired
    // is a detail. The Fridge might use a scanner and
    // items might have embedded RFID chips. It's a detail hidden
    // by proper encapsulation.
    if( o implements Expires && ((Expires)o).expiresBefore( today ) ) {
      throw new ExpiredObjectException( o );
    }
  }

  /** This creates a copy of the warranty for immutability purposes. */
  private void setWarranty( Date warranty ) {
    assert warranty != null;
    this.warranty = new Date( warranty.getTime() )
  }
}

Autres conseils

La raison fondamentale pour accesseurs en Java est très simple:

  • Vous ne pouvez spécifier méthodes , pas les champs, dans une interface.

Par conséquent, si vous voulez laisser un champ de passer à travers l'interface, vous aurez besoin d'un lecteur et une méthode écrivain. Ceux-ci sont traditionnellement appelés getX et setX pour le x sur le terrain.

De http://www.adam-bien.com/roller/abien/entry/encapsulation_violation_with_getters_and

le style JavaBean:

connection.setUser("dukie");
connection.setPwd("duke");
connection.initialize();

style OO:

connection.connect("dukie","duke");

Eh bien, je préfère clairement cette dernière approche; il ne les détails de mise en œuvre ne saignait pas, il est plus simple et plus concis, et toutes les informations nécessaires sont inclus dans l'appel de méthode, il est donc plus facile de l'obtenir droit. Je préfère également mettre les membres privés en utilisant des paramètres dans le constructeur, chaque fois que possible.

Votre question est, quand est un getter / setter justifié? Peut-être quand un changement de mode est nécessaire, ou vous avez besoin d'interroger un objet pour des informations.

myObject.GetStatus();
myObject.SomeCapabilitySwitch = true;

Dans la pensée à ce sujet, quand je commencé à coder en C #, je l'ai écrit beaucoup de code dans le style Javabeans illustré ci-dessus. Mais comme je l'ai acquis une expérience dans la langue, j'ai commencé à faire plus de membres dans cadre du constructeur, et en utilisant des méthodes qui avaient l'air plus comme le style OO ci-dessus.

Quand les accesseurs justifiés?

Lorsque le comportement "get" et "set" correspond réellement au comportement de votre modèle, qui est pratiquement jamais .

Toute autre utilisation est juste un tricheur parce que le comportement du domaine des affaires n'est pas clair.

Modifier

Cette réponse aurait pu apparaître comme cavalière, alors laissez-moi développer. Les réponses ci-dessus sont la plupart du temps correct, mais se concentrer sur les paradigmes de programmation de la conception orientée objet et ce que certains ratent la grande image. Dans mon expérience, cela conduit les gens à penser que d'éviter Gettings et setters est une règle académique pour les langages de programmation OO (par exemple les gens pensent remplacer getters propriétés est grand)

En fait, il est une conséquence du processus de conception. Vous ne devez pas entrer dans les interfaces et l'encapsulation et de motifs, en faisant valoir quant à savoir si elle le fait ou ne rompt pas ces paradigmes et ce qui est ou non une bonne programmation orientée objet. Le seul point qui importe en fin de compte est que s'il n'y a rien dans votre domaine qui fonctionne comme ceci en les mettant en vous ne sont pas la modélisation de votre domaine.

La réalité est qu'il est hautement improbable que tout système dans votre espace de domaine a accesseurs. Vous ne pouvez pas marcher jusqu'à l'homme ou la femme qui est responsable de la paie et dire simplement « Définir ce salaire à X » ou « Donne-moi ce salaire » . Un tel comportement n'existe tout simplement pas

Si vous mettez cela dans votre code que vous n'êtes pas concevez votre système pour correspondre au modèle de votre domaine. Oui que les interfaces de ruptures et d'encapsulation, mais ce n'est pas le point. Le point est que vous modélisez quelque chose qui n'existe pas.

Plus sur vous manque probablement une étape importante ou d'un processus, parce qu'il ya probablement une raison que je ne peux pas marcher jusqu'à rouleau de rémunération et dire définir ce salaire à X.

Lorsque les gens utilisent accesseurs ils ont tendance à pousser les règles de ce processus dans le mauvais endroit. Cela se déplace loin de votre domaine encore plus. En utilisant le exemple du monde réel, il est comme salaire en supposant que la personne au hasard qui est entré a la permission d'obtenir ces valeurs sinon il ne serait pas demander pour eux. Non seulement est-ce pas la façon dont le domaine est, il est en fait mentir sur la façon dont le domaine est.

En règle générale, accesseurs sont une mauvaise idée. Si un champ n'est pas logiquement partie de l'interface et vous le rendre privé, c'est très bien. Si elle est logiquement partie de l'interface et vous le rendre public, c'est très bien. Mais si vous le rendre privé puis retourner et faire efficacement à nouveau public en fournissant un getter et setter, vous êtes de retour à l'endroit où vous avez commencé à l'exception de votre code est maintenant plus bavard et obscurcie.

De toute évidence, il y a des exceptions. En Java, vous pourriez avoir besoin d'utiliser des interfaces. La bibliothèque standard Java a des exigences de compatibilité descendante si extrême que l'emportent sur les mesures normales de la qualité du code. Il est même possible que vous pouvez réellement avoir affaire avec le légendaire, mais rares cas où il y a une bonne chance que vous pouvez remplacer ultérieurement un champ stocké avec le calcul de la volée sans casser sinon l'interface. Mais ce sont des exceptions. Accesseurs sont un anti-modèle qui a besoin d'une justification particulière.

si le champ est accesible directement ou par l'intermédiaire de procédé ne sont pas vraiment importante.

classe (invariants ones) est important usefull. Et pour les préserver, nous avons parfois besoin de ne pas être en mesure de changer quelque chose à l'extérieur. Par exemple. si nous avons la classe carrée avec separete largeur et la hauteur, en changeant un d'entre eux fait devenir quelque chose d'autre que carrée. Nous avons donc besoin méthode changeSide Si c'était Rectangle, nous aurions pu setters / domaine public. Mais setter que se vérifier si son supérieur à zéro serait mieux.

Et dans les langues concrètes (par exemple. Java) sont des raisons pour lesquelles nous avons besoin de ces méthodes (interfaces). Et une autre raison de la méthode est la compatibilité (source et binaire). Donc, il est plus facile de les ajouter alors penser si le champ public serait suffisant.

BTW. J'aime utiliser la valeur immuables simples tenant des classes avec des champs finaux publics.

Vous pouvez modifier vos internes à tout en conservant les interfaces même. Si vos interfaces ne varient pas, les code que vous ne cassera pas. Vous pouvez toujours changer vos internes que vous le souhaitez.

Mon approche est la suivante -

Quand je pense à tripoter les données plus tard, un getter / setter est justifiée. En outre, si le changement se produit, je pousse souvent les données dans un getter / setter.

Si c'est une structure POD, je laisse les créneaux horaires disponibles.

Sur un plan plus abstrait, la question est « qui gère les données », et qui dépend du projet.

Si vous utilisez des accesseurs se sent compliqué, le problème est peut-être la langue, pas le concept lui-même.

Voici le code de la secondes par exemple écrit en Ruby:

class Fridge
  attr_accessor :cheese
end

def go_shopping fridge
  fridge.cheese += 5
end

Notez qu'il ressemble beaucoup à la premier par exemple en Java? Lorsque des accesseurs sont traités comme des citoyens de première classe, ils ne sont pas une corvée à utiliser, et la flexibilité peuvent parfois être une véritable aubaine - par exemple, nous pourrions décider de renvoyer une valeur par défaut pour le fromage sur un nouveau réfrigérateur:

class Fridge
  attr_accessor :cheese

  def cheese
    @cheese || 0
  end
end

Bien sûr, il y aura de nombreuses variables qui ne devraient pas être exposés publiquement tout. exposer Inutilement variables internes fera votre code pire, mais on ne peut guère reprocher que sur des accesseurs.

Considérons une classe Size qui encapsule la largeur et la hauteur. Je peux éliminer setters en utilisant le constructeur, mais comment cette aide me dessiner un rectangle avec Size? La largeur et la hauteur sont pas des données internes à la classe; ils sont données partagées qui doivent être disponibles aux consommateurs de taille.

Les objets se composent de comportements et de l'état - ou attributs. S'il n'y a pas d'état exposé alors que les comportements sont publics.

Sans état, comment voulez-vous trier une collection d'objets? Comment voulez-vous rechercher une instance spécifique d'un objet? Si vous utilisez uniquement les constructeurs, ce qui se passe lorsque votre objet a une longue liste d'attributs?

Aucun paramètre de méthode ne devrait jamais être consommé sans validation. Par conséquent, il est la paresse d'écrire:

setMyField(int myField){
    this.myField = myField;
}

Si vous ne l'écrire de cette façon, alors vous avez au moins préparé pour utiliser le setter pour la validation; il vaut mieux qu'un terrain public - mais à peine. Mais au moins vous avez une interface publique cohérente que vous pouvez revenir et mettre dans les règles de validation sans casser le code de vos clients.

accesseurs, setters, propriétés, mutators, appelez ce que vous voulez, mais ils sont nécessaires.

Si accesseurs violent encapsulation et vrai OO, alors je suis sérieusement en difficulté.

je me sentais toujours un objet représente tout le meilleur vient à l'esprit que vous avez besoin de faire.

Je viens de terminer la rédaction d'un programme qui génère Mazes en Java, et je classe qui représente « Carrés Maze ». J'ai données dans cette classe qui représente les coordonnées, les murs et les booléens, etc.

Je dois avoir un moyen de changement / manipuler / accéder à ces données! Que dois-je faire sans accesseurs? Java n'utilise pas de propriétés et la mise en toutes mes données qui est locale à cette classe en public est DEFINITIF une violation de l'encapsulation et OO.

Tout d'abord, il y a généralement deux types d'objets que je commenterai, la valeur et les types de services.

types de services ne doit jamais avoir setters, ce qu'ils exigent des dépendances ne devrait pas être gettable. La meilleure façon de passer des dépendances est par un constructeur ou une usine de cette façon tous les cas sont entièrement formés à partir du début, clair et simple.

types La valeur doit aussi être immuable, sur la main Oher il y a des moments où ce n'est pas pratique comme un ORM, ou une autre application telle que d'un widget à un objet. Tous les autres types de valeurs qui se sont passés dans le système d'une couche ou d'une partie à l'autre devrait être immuable et ne devraient pas avoir setters.

A Getter / Setter est justifiée comme tout autre méthode publique est justifiée. La justification est principalement parce que nous voulons fournir une interface au monde extérieur qui définit la façon dont nos classes interagit avec d'autres classes. Nous faisons cela principalement parce que nous voulons réduire le couplage entre des entités distinctes. Réduire le couplage est une bonne chose pour de nombreuses raisons:

  • Je peux utiliser mon code actuel de la même interface de classe même si entre-temps cette classe a ajouté de nouvelles méthodes (mais conservé l'ancienne interface)
  • peut-il changer la représentation interne d'une classe sans affecter les consommateurs de it
  • Réduire les risques de bugs: si vous faites vos données internes que vous pouvez privé savoir avec certitude que le code externe ne sera pas gâcher vos données internes

Oui, accesseurs est un anti-modèle dans la programmation orientée objet et devraient jamais utiliser: http://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html . En un mot, ils ne rentrent pas dans paradigme orienté objet, car ils vous encouragent à traiter un objet comme une structure de données. Ce qui est une idée fausse majeure.

J'espère que tout ce que l'IDE développé par la société qui développe votre génère automatiquement de langage de programmation ne viole pas les « Principes d'orientation objet ».

Cela étant dit, chaque fois que vous avez une variable scope publique, utiliser un getter et un setter et vous êtes en or. Il me semble être l'élément nécessaire du principe objet extrêmement orienté d'encapsulation - même si elle ressemble à beaucoup de code junky.

La raison de les utiliser est que l'encapsulation appropriée peut encore avoir lieu et tant que vous avez la couche Abstraite loin qui laisse la personne Monkeying avec votre objet se sentir votre objet, vous pouvez prendre cet objet loin sans lui même le savoir droite?

Il suffit de dire, une maison divisée ne peut pas se tenir debout, l'un des locataires de OO ne va pas tourner sur elle-même et vous ne pouvez pas servir à la fois l'encapsulation et l'optimisation prématurée.

Je pensais que nous aurions des réponses plus habituelles ici?

accesseurs sont en très POO général (quelqu'un dire quoi que ce soit d'autre tort, simple que cela).

Bien que vous devez vous rappeler de ne pas trop ingénieur, jamais faire un getter ou setter qui n'est pas nécessaire. Toute information que vous ne comptez pas utiliser par une autre classe que vous ne devriez pas avoir getter ou setter pour.

Bien que, la raison pour laquelle vous ne faites pas les champs du public et ont plutôt accesseurs sont la plupart du temps:

  • Il est impossible de faire des tests appropriés en utilisant des champs. Getters / setters sont beaucoup mieux à cet égard.

  • Il est impossible d'utiliser correctement les champs dans un cadre de programmation en temps réel. getters / setters synchronisée est la voie à suivre.

  • Le thème d'interface (déjà expliqué que je vais donc pas).

  • Il est plus facile à utiliser lors de la réutilisation correctement le système.

  • Il est beaucoup plus difficile de savoir quelles classes qui se sert de votre classe et ce qui va se briser si vous changez un champ d'un getter / setter.

la seule fois ainsi tous vous utilisez un champ public au lieu d'un getter / setter est quand vous ne faites pas un projet officiel, mais plutôt faire juste pour le plaisir et ne peut pas embêter avec les getter / setters.

Licencié sous: CC-BY-SA avec attribution
scroll top