Frage

Lassen Sie uns sagen, dass ich mit Codierung eine Art von einem RPG beauftragt werde. Dies bedeutet, dass zum Beispiel werde ich ein Character GameCharacter und Statistiken davon verfolgen möge, wie Intelligenz, Schadensboni oder Hitpoints.

Ich bin positiv Angst, dass bis zum Ende des Projektes kann ich mit dem Umgang mit sehr hohen Anzahl von Feldern am Ende - und für jeden würde ich sicherstellen, dass sie einen sehr ähnlichen Satz von Zwang und Verhaltensweisen folgen (für Beispiel möchte ich sie zwischen min und max begrenzt werden, ich will zwischen einem „Grundwert“ unterscheiden zu können und einem „temporären Bonus“, ich will beide erhöhen zu können und verringern ohne Setter gehen durch und Getter). Plötzlich wird für jedes Feld würde ich ein (zwei?) Müssen Getter und vier Setter und vielleicht ein paar Resetter auch! Auch für 10 Felder, die alle gleich eine Menge von Methoden bedeuten, eek.

Für Trockenheit Ich habe begonnen, die Logik Einkapseln von Messing mit diesen Statistiken in Field Klassen, so dass ich Code schreiben könnte wie intelligence.applyBonus(10) oder hitpoints.get() (die den Wert sorgt dafür zurück ist in Reichweite), etc. Ich habe sogar gegangen eine solche Länge diese Felder zusammen Klassen Gruppe zu erstellen, aber das ist nicht der Punkt jetzt.

Nun, schlug ich in diesem Problem während Field in GameCharacter „Verstopfen“: Die meisten Java Lehrbücher sagen, dass jede Klasse private Felder mit öffentlichen Getter und Setter haben sollte. Das klingt in der Theorie gut, und ich habe schon eine ganze Klasse um einen int gebaut; Allerdings ist die Idee nicht so fest klingen, wenn man sich den Aufruf einer Getter finden zu bekommen ... einen Getter:

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

Ich würde viel lieber das Feld direkt zugreifen. Vielleicht ist es mein Python / VB [1] „Hintergrund“, aber für mich ist es sauberer, klarer und einfacher:

thisCharacter.intelligence.get()

Die (theoretische) Problem mit den öffentlichen Bereichen ist, dass ich auf sie alle Kontrolle Aufgeben bin; beispielsweise an einer anderen Stelle in der Codebasis, durch Unglück, könnte folgende passieren:

thisCharacter.intelligence = somethingThatReallyIsNull;

Klingt wie ein subtiler Fehler ... aber ... ich meine, sollte ich mir Sorgen machen wirklich darüber? Ich für einen Plan nie die Field direkt zuweisen [2], die ich in der Javadoc dokumentiert habe, dass dies nicht etwas, das getan werden soll, aber immer noch ich bin neu hier, damit ich ein bisschen zerrissen bin.

So würde Ich mag hören, was halten Sie von diesem Thema. Sind die Vorteile der Kapselung so massiv, dass ich weitermachen sollte und Getter Getter und Setter-Getter haben und so weiter ... oder sollte ich Verkapselung bei gesunden Maßnahmen und die Field als public Feld verlassen?


[1] Ja, ich weiß. Ich habe versucht, zu vergessen. Aber wir haben erst vor kurzem auch ein wenig C # und ein Mann zu sehen ist, sind keine Eigenschaften süß. Na gut.

[2] außer in Konstrukteurs! Und ein Getter wird mich nicht von einem fehlerhaften Konstruktor speichern.

War es hilfreich?

Lösung

Meine Erfahrung ist, dass in Situationen, in denen Sie ein Los von Feldern, die Anzahl, Art, Benennen und Typen von Feldern sind so flexibel, und wahrscheinlich während der gesamten Lebensdauer des Projektes ändern, die Sie würde wahrscheinlich eine Art von Karte statt Felder müssen.

Zum Beispiel ein Attribut Karte von Werten Schlüsseln haben.

Bereitstellung öffentlicher Anrufe für das Abrufen und die Attribute einstellen, aber alle nicht, sie lassen (oder sicherstellen, dass sie nicht). Stattdessen erstellen Klassen jeweils darstellen Attribut, das Sie interessiert sind, und diese Klasse bietet alle Funktionen für das Attribut zu manipulieren. Zum Beispiel, wenn Sie Kraft haben, können Sie eine „StrengthManipulation“ Klasse, die zu einem bestimmten Player-Objekt initialisiert wird, und dann liefern Getter, Setter (alle mit entsprechender Validierung und Ausnahmen), und vielleicht Dinge wie Stärke Berechnung mit Boni, usw. .

Ein Vorteil ist, dass Sie die Verwendung Ihrer Attribute von Ihrem Player Klasse entkoppeln. Also, wenn Sie jetzt eine Intelligenz Attribut hinzufügen möchten, müssen Sie sich nicht beschäftigen und neu kompilieren alles, was nur Kraft manipuliert.

Wie für die Felder direkt zugreifen, es ist eine schlechte Idee. Wenn Sie ein Feld in VB (zumindest in der alten VBs) zuzugreifen, rufen Sie in der Regel eine Eigenschaft Getter und Setter und VB einfach versteckt den () Anruf für Sie. Meine Ansicht ist, dass Sie zu den Konventionen der Sprache anzupassen, die Sie verwenden. In C, C ++, Java und dergleichen Sie haben Felder, und Sie haben Methoden. Aufruf eine Methode sollte immer die () muß es, dass es deutlich zu machen, ist ein Aufruf und andere Dinge passieren können (zum Beispiel könnte man eine Ausnahme erhält). So oder so, ist einer der Vorteile von Java ist die präzisere Syntax und Stil.

VB auf Java oder C ++ ist wie Texting Schule wissenschaftliches Schreiben zu absolvieren.

BTW: Einige Usability-Forschung zeigt, dass es besser ist, Parameter zu Konstruktoren nicht haben und eher zu konstruieren und alle Setter nennen, wenn Sie sie benötigen.

Andere Tipps

Klingt wie Sie in Bezug auf if(player.dexterity > monster.dexterity) attacker = player denken. Sie brauchen mehr wie if(player.quickerThan(monster)) monster.suffersAttackFrom(player.getCurrentWeapon()) zu denken. Verwirren Sie nicht um mit bloßen Statistiken, äußern Sie Ihre tatsächliche Absicht und dann gestalten Sie Ihre Klassen um, was sie tun soll. Statistiken sind ein Cop-out sowieso; was Sie wirklich interessiert, ist, ob ein Spieler kann oder nicht, eine Aktion tun, oder ihre Fähigkeit / Kapazität im Vergleich zu einigen Referenz. Denken Sie an „ist der Spieler-Charakter stark genug“ (player.canOpen(trapDoor)) statt „hat der Charakter mindestens 50 Stärke“.

Steve Yegge hatte eine sehr interessante (wenn lange) Blog-Post, die diese Themen behandelt: Das Universal Design Pattern .

Für mich ist es wie ‚thisCharacter‘ sieht vielleicht gut eine ‚Intelligenz‘ zum Gegenstand haben für den Umgang mit Intelligenz hinter den Kulissen, aber ich frage mich, ob das überhaupt öffentlich sein sollte. Sie sollten nur thisCharacter.applyInt und thisCharacter.getInt statt das Objekt aussetzen, die mit ihm beschäftigt. belichten Sie nicht Ihre Implementierung so.

Halten Sie Ihre Felder privat! Sie wollen nie zu viel von Ihrer API belichten. Man kann immer etwas, das Private öffentlich, aber nicht umgekehrt, in zukünftigen Versionen.

Denken Sie, als ob Sie, dass in das nächste MMORPG machen werden. Sie würden viel Spielraum für Fehler, Fehler haben, und unnötige Übel. Stellen Sie sicher, unveränderliche Eigenschaften sind endgültig.

Denken Sie an einen DVD-Player, mit seiner miniamilistic Schnittstelle (Play, Stop, Menü), und doch so Technisierung im Inneren. Sie finden alles devitalen in Ihrem Programm ausblenden möchten.

Es klingt wie Ihre Hauptbeschwerde nicht so sehr mit der Abstraktion von Setter / Getter-Methoden ist, aber mit der Sprachsyntax für sie zu benutzen. Das heißt, Sie so etwas wie C # Stil Eigenschaften bevorzugen würde.

Wenn dies der Fall ist, dann muss die Java-Sprache relativ wenig zu bieten. Direkte Feld Zugang ist in Ordnung, bis Sie zu einem Getter oder Setter wechseln müssen, und dann werden Sie einige Refactoring zu tun haben (möglicherweise in Ordnung, wenn Sie die ganze Code-Basis steuern).

Natürlich, wenn die Java-Plattform ist eine Anforderung, aber die Sprache ist nicht, dann gibt es andere Alternativen. Scala hat eine sehr schöne Eigenschaft Syntax, zum Beispiel, zusammen mit vielen anderen Funktionen, die für ein solches Projekt nützlich sein könnte. Und das Beste ist, es läuft auf der JVM, so dass Sie immer noch die gleiche Beweglichkeit erhalten, die Sie durch das Schreiben in der Java-Sprache bekommen würden. :)

Was Sie scheinen hier zu haben, ist eine einzelne Schicht aus einem Verbundmodell.

Sie möchten vielleicht Methoden hinzufügen, die Abstraktionen zum Modell hinzufügen, und nicht nur sie als eine Reihe von niedrigeren Level-Modelle haben.

Die Felder sollten endgültig sein, also auch wenn man sie öffentlich machen haben könnten Sie nicht versehentlich null ihnen zuweisen.

Die „get“ Präfix für alle Getter vorhanden ist, so ist es wahrscheinlich mehr die ursprüngliche Aussehen als ein neues Problem als solches.

  

Sind die Vorteile der Kapselung so massiv, dass ich weitermachen sollte und Getter Getter und Setter-Getter haben und so weiter ... oder sollte ich Verkapselung bei gesunden Maßnahmen und das Feld als öffentliches Feld verlassen?

IMO, Verkapselung hat nichts mit Umwickeln eines Getter / Setter um einen privaten Bereich zu tun. In kleinen Dosen, oder wenn Allzweck- Bibliotheken zu schreiben, ist der Kompromiss akzeptabel. Aber wenn in einem System wie dem nicht Einhalt geboten Sie beschreiben, es ist ein ein Antipattern .

Das Problem mit Getter / Setter ist, dass sie zwischen dem Objekt mit den Methoden und dem Rest des Systems zu enge Kopplung schaffen.

Einer der Vorteile der realen Kapselung ist, dass es die Notwendigkeit für Getter und Setter reduziert, Ihr Objekt aus dem Rest des Systems in dem Prozess zu entkoppeln.

Anstatt die Implementierung von Gamecharacter mit setIntelligence auszusetzen, warum nicht geben Gamecharacter eine Schnittstelle, ist es besser Rolle in dem Spielsystem widerspiegelt?

Zum Beispiel statt:

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

  public Intelligence getIntelligence()
  {
    return intelligence
  }

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

, warum dies nicht versuchen:

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

  public int getIntelligence()
  {
    // TODO
  }
}

oder noch besser:

// 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
}

Mit anderen Worten, ein Gamecharacter kann Gameobjects greifen. Die Wirkung jeder Gamecharacter die gleiche Gameobject greifen kann (und sollte) variieren, aber die Details sind in jeder Implementierung Gamecharacter vollständig gekapselt.

Beachten Sie, wie die Gamecharacter ist jetzt verantwortlich seine eigene Intelligenz Update (Bereich Prüfung, usw.) der Handhabung, was passieren kann, wie es Gameobjects, zum Beispiel packt. Der Setter (und die Komplikationen beachten Sie, mit, die es) sind verschwunden. Sie könnten zusammen mit dem getIntelligence Verfahren verzichtet werden können, je nach Situation. Alle Holub nimmt diese Idee zum logischen Schlussfolgerung , aber doch dieser Ansatz scheint nicht sehr verbreitet zu sein.

Neben Uri Antwort (die ich total unterstützen), würde Ich mag Ihnen vorschlagen, betrachten die Definition der Attributabbildung in Daten. Dadurch wird Ihr Programm äußerst flexibel machen und wird eine Menge Code ausklammern Sie müssen Sie nicht einmal erkennen, dass nicht brauchen.

Zum Beispiel kann ein Attribut wissen, was Feld, um es auf dem Bildschirm bindet, was Feld bindet es an in der Datenbank, wird eine Liste von Aktionen auszuführen, wenn die Attributänderungen (a neu zu berechnen getroffen% sowohl angewandt werden könnte, um Stärke und dex ...)

es auf diese Weise tut, haben Sie keinen Code pro Attribut eine Klasse aus an die DB zu schreiben oder sie auf dem Bildschirm angezeigt werden soll. Sie einfach über die Attribute durchlaufen und die Informationen innerhalb gespeichert sind.

Die gleiche Sache Fähigkeiten anwenden kann - in der Tat, Attribute und Fähigkeiten würden wahrscheinlich aus der gleichen Basisklasse ableiten

.

Nachdem Sie diesen Weg gehen, werden Sie wahrscheinlich selbst finden mit einer ziemlich schweren Textdatei Attribute und Fähigkeiten zu definieren, aber eine neue Fertigkeit Zugabe wäre so einfach wie:

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

An einem gewissen Punkt, das Hinzufügen einer Fertigkeit wie dies würde überhaupt keine Codeänderung erfordern, könnte es alles ziemlich leicht in Daten durchgeführt werden, und alle Daten in einem einzigen Skill-Objekt gespeichert an eine Klasse. Sie könnten natürlich Aktionen definieren, die gleiche Art und Weise:

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

Obwohl dieses Beispiel ziemlich fortgeschritten ist, sollten Sie in der Lage sein, sich vorzustellen, wie es mit absolut kein Code ausgeführt werden würde. Stellen Sie sich vor, wie schwierig es wäre, etwas zu tun wie das ohne Code, wenn Ihre „Attribute / Fähigkeiten“ waren tatsächlich Variablen Mitglied.

Betrachten wir eine „Modellierung“ Rahmen, wie Eclipse EMF verwenden. Dabei geht es um Ihre Objekte zu definieren, mit so etwas wie Eclipse EMF Editor, oder in einfachen XML oder in Rational Rose. Es ist nicht so schwierig, wie es klingt. Sie definieren, Attribute, Einschränkungen usw. Dann wird der Rahmen erzeugt der Code für Sie. Es fügt @generated Tags Teile hinzugefügt wie Getter und Setter-Methoden. Sie können den Code, anpassen und später bearbeiten es entweder von Hand oder über einen GUI und regeneriert die Java-Dateien.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top