Question

Disons que je suis confié la tâche de coder une sorte d'un RPG. Cela signifie que, par exemple, je veux suivre un Character GameCharacter et statistiques de ceux-ci, comme l'intelligence, des bonus de dégâts ou de points de vie.

Je suis positivement peur que d'ici la fin du projet, je peux finir avec la manipulation très un grand nombre de domaines - et pour chaque je dois vous assurer qu'ils suivent un ensemble très similaire de la contrainte et les comportements (pour par exemple, je veux qu'ils soient bornés entre un minimum et un maximum, je veux être en mesure de faire la distinction entre une « valeur de base » et une « prime temporaire », je veux être en mesure d'augmenter et diminuer à la fois, sans passer par un setters et getters). Tout à coup, pour tous les domaines je besoin d'un (deux?) Getter et quatre poseurs et peut-être quelques resetters aussi! Même pour les 10 champs qui veut dire beaucoup de méthodes, tous les mêmes EEK.

Pour siccité j'ai commencé encapsulant la logique de déconner avec ces statistiques dans les classes de Field, afin que je puisse écrire un code tel que intelligence.applyBonus(10) ou hitpoints.get() (qui prend en charge la valeur retournée est à portée), etc. Je suis même allé une telle longueur pour créer des classes pour regrouper ces champs ensemble, mais ce n'est pas le point en ce moment.

Maintenant, je touché à ce problème tout en « branchant » Field dans GameCharacter: la plupart des manuels Java dire que chaque classe doit avoir des champs privés avec getters publics et setters. Cela sonne bien en théorie, et je l'ai déjà construit toute une classe autour d'un int; cependant, l'idée ne semble pas aussi solide lorsque vous vous trouvez appeler un getter pour obtenir ... un getter:

thisCharacter.getIntelligence().get() //eeek

J'accès beaucoup plutôt directement sur le terrain. Peut-être qu'il est mon Python / VB [1] « fond », mais pour moi il est plus propre, plus claire et plus simple:

thisCharacter.intelligence.get()

Le problème (théorique) avec des champs publics est que je renonce à tout contrôle sur elle; par exemple à un autre point dans le code de base, par malheur, ce qui suit peut se produire:

thisCharacter.intelligence = somethingThatReallyIsNull;

Sons comme un bug subtil ... mais ... je veux dire, dois-je vraiment vous inquiéter à ce sujet? Pour ma part, jamais l'intention d'affecter le Field directement [2], je l'ai documenté dans le Javadoc que ce n'est pas quelque chose qui devrait être fait, mais je suis nouveau ici, donc je suis un peu déchiré.

Je voudrais savoir ce que votre opinion sur ce sujet. Les avantages de l'encapsulation si massive que je devrais aller de l'avant et ont getters getter et setter getters et ainsi de suite ... ou devrais-je prendre l'encapsulation des mesures saines et laisser le Field comme champ public?


[1] Oui, je sais. J'ai essayé d'oublier. Mais nous avons tout récemment vu aussi un peu de C # et l'homme, ne sont pas des propriétés douces. Eh bien.

[2], sauf dans Constructors! Et un getter ne me sauvera pas d'un constructeur défectueux.

Était-ce utile?

La solution

Mon expérience est que dans les situations où vous avez besoin d'une beaucoup des champs, le nombre, la nature, la dénomination, et les types de champs sont si souples et susceptibles de changer tout au long de la durée de vie de votre projet que vous aurait probablement besoin d'une sorte de carte au lieu des champs.

Par exemple une carte d'avoir attribut de clés à des valeurs.

Fournir des appels publics pour obtenir et définir les attributs, mais ne laissez pas les utiliser tout le monde (ou assurez-vous qu'ils ne le font pas). Au lieu de cela, créer des classes pour représenter chaque attribut qui vous intéresse, et cette classe fournit toutes les fonctions de manipulation de cet attribut. Par exemple, si vous avez la force, vous pourriez avoir une classe « StrengthManipulation » qui est initialisé à un objet de joueur spécifique, et fournit ensuite getters, setters (toutes avec validation et exceptions appropriées), et peut-être des choses comme le calcul de force avec des bonus, etc. .

Un avantage de ceci est que vous découpler l'utilisation de vos attributs de votre classe de joueur. Donc, si vous ajoutez maintenant un attribut Intelligence, vous ne devez pas traiter et recompiler tout ce qui manipule seulement la force.

En ce qui concerne l'accès aux champs directement, il est une mauvaise idée. Lorsque vous accédez à un champ en VB (au moins dans les anciens VBS), vous appelez habituellement un getter de la propriété et régleurs et VB cache simplement le () appel pour vous. Mon opinion est que vous devez adapter aux conventions de la langue que vous utilisez. En C, C ++, Java et comme vous avez des champs et vous avez des méthodes. L'appel d'une méthode doit toujours avoir le () pour préciser qu'il est un appel et d'autres choses peut se produire (par exemple, vous pourriez obtenir une exception). De toute façon, l'un des avantages de Java est une syntaxe plus précise et le style.

VB à Java ou C ++ est comme des textos pour leur diplôme écriture scientifique école.

BTW: Certaines recherches d'utilisabilité montre qu'il est préférable de ne pas avoir des paramètres aux constructeurs et plutôt construire et appeler si vous avez besoin d'eux tous les setters.

Autres conseils

On dirait que vous pensez en termes de if(player.dexterity > monster.dexterity) attacker = player. Vous devez penser comme if(player.quickerThan(monster)) monster.suffersAttackFrom(player.getCurrentWeapon()). Ne pas déranger les statistiques nues, exprimez votre intention réelle et concevoir vos cours autour de ce qu'ils sont censés faire. Les statistiques sont une dérobade de toute façon; ce que vous tenez vraiment à savoir si un joueur peut ou ne peut pas faire une action, ou leur capacité / capacité par rapport à une référence. Pensez en termes de « est le joueur caractère assez fort » (player.canOpen(trapDoor)) au lieu de « Le personnage a au moins 50 force ».

Steve Yegge a eu un très intéressant (si long) billet de blog qui a couvert ces questions: Le modèle de conception universelle .

Pour moi, il semble que « thisCharacter » pourrait bien avoir un objet « de renseignement » pour faire face à l'intelligence dans les coulisses, mais je me demande si cela devrait être public tout. Vous devriez juste exposer à la place thisCharacter.applyInt et thisCharacter.getInt de l'objet qui traite avec lui. Ne pas exposer votre mise en œuvre comme ça.

Gardez vos champs privés! Vous ne voulez jamais exposer trop de votre API. Vous pouvez toujours faire quelque chose qui était public et privé, mais pas l'inverse, dans les versions ultérieures.

Pensez comme si vous allez faire cela dans le prochain MMORPG. Vous auriez beaucoup de possibilités pour les bugs, les erreurs et le mal inutile. Assurez-vous des propriétés immuables sont finales.

Pensez à un lecteur de DVD, avec son interface miniamilistic (lecture, arrêt, menu), mais cette technicité à l'intérieur. Vous voulez tout cacher non essentiel dans votre programme.

Il semble que votre plainte principale est pas tant avec l'abstraction des méthodes setter / getter, mais avec la syntaxe du langage pour les utiliser. -À-dire, vous préféreriez quelque chose comme des propriétés de style C #.

Si tel est le cas, le langage Java a relativement peu à vous offrir. accès direct sur le terrain est très bien, jusqu'à ce que vous devez passer à un getter ou setter, et alors vous aurez refactoring à faire (peut-être ok, si vous contrôlez l'ensemble codebase).

Bien sûr, si la plate-forme Java est une exigence, mais la langue est pas alors il existe d'autres alternatives. Scala a une très belle syntaxe de propriété, par exemple, ainsi que beaucoup d'autres caractéristiques qui pourraient être utiles pour un tel projet. Et le meilleur de tous, il fonctionne sur la machine virtuelle Java, de sorte que vous obtenez toujours la même portabilité que vous obtiendriez en écrivant dans le langage Java. :)

Qu'est-ce que vous semblez avoir ici est une couche unique d'un modèle composite.

Vous pouvez ajouter des méthodes qui ajoutent des abstractions au modèle, plutôt que d'avoir simplement comme un ensemble de modèles de niveau inférieur.

Les champs doivent être finale, même si vous avez fait les rendre publics vous pourriez ne pas céder accidentellement null à eux.

Le préfixe « get » est présent pour tous les apporteurs, il est donc probablement plus l'aspect initial d'un nouveau problème en tant que tel.

  

sont les avantages de l'encapsulation si massive que je devrais aller de l'avant et avoir getter getters et getter setter et ainsi de suite ... ou devrais-je prendre l'encapsulation des mesures saines et laisser le champ comme un champ public?

l'OMI, l'encapsulation n'a rien à voir avec envelopper un getter / setter autour d'un champ privé. A petites doses, ou lors de l'écriture des bibliothèques à usage général, le compromis est acceptable. Mais quand rien ne dans un système comme celui que vous décrivez, est un un antimodèle.

Le problème avec les getters / setters est qu'ils créent un couplage trop étroit entre l'objet avec ces méthodes et le reste du système.

L'un des avantages de l'encapsulation réelle est qu'il réduit la nécessité d'accesseurs, découplage votre objet du reste du système dans le processus.

Au lieu d'exposer la mise en œuvre de GameCharacter avec setIntelligence, pourquoi ne pas donner GameCharacter une interface qui reflète mieux son rôle dans le système de jeu?

Par exemple, au lieu de:

// pseudo-encapsulation anti-pattern
public class GameCharacter
{
  private Intelligence intelligence;

  public Intelligence getIntelligence()
  {
    return intelligence
  }

  public void setIntelligence(Intelligence intelligence)
  {
    this.intelligence = intelligence;
  }
}

pourquoi ne pas essayer ceci:

// better encapsulation
public class GameCharacter
{
  public void grabObject(GameObject object)
  {
    // TODO update intelligence, etc.
  }

  public int getIntelligence()
  {
    // TODO
  }
}

ou mieux encore:

// still better
public interface GameCharacter
{
  public void grabObject(GameObject object); // might update intelligence
  public int getIntelligence();
}

public class Ogre implements GameCharacter
{
  // TODO: never increases intelligence after grabbing objects
}

En d'autres termes, un GameCharacter peut saisir GameObjects. L'effet de chaque GameCharacter saisissant le même gameobject peut (et doit) varier, mais les détails sont entièrement encapsulé dans chaque implémentation de GameCharacter.

Remarquez comment l'GameCharacter est maintenant chargé de gérer sa propre mise à jour du renseignement (gamme de vérification, etc.), qui peut se produire comme il saisit GameObjects, par exemple. Le compositeur (et les complications que vous note avec l'avoir) ont disparu. Vous pourriez être en mesure de se passer de la méthode getIntelligence tout à fait, en fonction de la situation. Allen Holub prend cette idée à sa conclusion logique , mais mais cette approche ne semble pas être très commun.

En plus de la réponse d'Uri (que je soutiens totalement), je voudrais vous suggérer d'envisager de définir la carte d'attribut dans les données. Cela rendra votre programme extrêmement flexible et prendra en compte un grand nombre de code que vous ne réalisez même pas que vous n'avez pas besoin.

Par exemple, un attribut peut savoir dans quel domaine il se lie à l'écran, quel domaine il se lie à la base de données, une liste d'actions à exécuter lorsque les changements d'attributs (un recalcul frappé% pourrait appliquer à la fois la force et Dex ...)

En procédant ainsi, vous avez pas de code par attribut pour écrire une classe sur la base de données ou l'afficher sur l'écran. Vous parcourons simplement sur les attributs et utiliser les informations stockées à l'intérieur.

La même chose peut demander aux compétences - en fait, les attributs et les compétences dériverait probablement de la même classe de base

.

Une fois que vous allez dans cette voie, vous allez probablement vous retrouvez avec un fichier texte assez sérieux pour définir les attributs et compétences, mais en ajoutant une nouvelle compétence serait aussi facile que:

Skill: Weaving
  DBName: BasketWeavingSkill
  DisplayLocation: 102, 20  #using coordinates probably isn't the best idea.
  Requires: Int=8
  Requires: Dex=12
  MaxLevel=50

À un certain moment, l'ajout d'une compétence comme cela nécessiterait aucun changement de code que ce soit, cela pourrait se faire dans tous les données assez facilement, et toutes les données sont stockées dans un seul objet de compétences attaché à une classe. Vous pouvez, bien sûr, définir des actions de la même manière:

Action: Weave Basket
  text: You attempt to weave a basket from straw
  materials: Straw
  case Weaving < 1
    test: You don't have the skill!
  case Weaving < 10
    text: You make a lame basket
    subtract 10 straw
    create basket value 8
    improve skill weaving 1%
  case Weaving < 40
    text: You make a decent basket
    subtract 10 straw
    create basket value 30
    improve skill weaving 0.1%
  case Weaving < 50
    text: You make an awesome basket!
    subtract 10 straw
    create basket value 100
    improve skill weaving 0.01%
  case Weaving = 50
    text: OMG, you made the basket of the gods!
    subtract 10 straw
    create basket value 1000

Bien que cet exemple est assez avancée, vous devriez être en mesure de visualiser comment il serait fait absolument pas de code. Imaginez combien il serait difficile de faire quelque chose comme ça sans code si vos « attributs / compétences » étaient en fait des variables membres.

Pensez à utiliser un cadre de « modélisation », comme la FEM d'Eclipse. Cela implique de définir vos objets en utilisant quelque chose comme éditeur Eclipse EMF, ou en XML ordinaire ou dans Rational Rose. Ce n'est pas aussi difficile que cela puisse paraître. Vous définissez les attributs, les contraintes, etc. Ensuite, le cadre génère le code pour vous. Il ajoute des balises @generated aux parties il ajouté, comme méthodes getter et setter. Vous pouvez personnaliser le code, et modifier ultérieurement il soit à la main ou via une interface graphique, et régénérer les fichiers java.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top