公共领域有哪些替代方案?

发布于 2024-10-10 13:12:52 字数 1737 浏览 11 评论 0原文

我正在用 java 编写一个游戏,正如问题标题建议的那样,我在类中使用公共字段。 (暂时)

从我所看到的公共领域很糟糕,我有一些理解为什么。 (但如果有人能澄清为什么你不应该使用它们,那将不胜感激)

事情是,从我所看到的来看,(这似乎合乎逻辑)是使用私有字段,但使用 getter 和 setter 来访问它们是也不好,因为它首先就违背了使用私有字段的目的。

所以,我的问题是,有哪些替代方案?或者我真的必须使用具有 getter 和 setter 的私有字段吗?

这里供参考的是我的一个类及其一些方法。

如果需要的话我会详细说明。

public double health;
//The player's fields.
public String name;
public double goldCount;
public double maxWeight;
public double currentWeight;
public double maxBackPckSlts;
public double usedBackPckSlts; // The current back pack slots in use
public double maxHealth; // Maximum amount of health
public ArrayList<String> backPack = new ArrayList<String>();

//This method happens when ever the player dynamically takes damage(i.e. when it is not scripted for the player to take damage.
//Parameters will be added to make it dynamic so the player can take any spread of damage.
public void beDamaged(double damage)
{
    this.health -= damage;
    if (this.health < 0)
    {
        this.health = 0;
    }
}

编辑:出于检查目的,这就是我的 Weapon 类现在的样子:(代码示例由于某种原因无法工作,所以它看起来不正确。)

private final double DAMAGE;
private final double SPEED;

public Weapon(double initialDmg,double initialSpd,String startName,double initialWg)
{
    DAMAGE = initialDmg;
    SPEED = initialSpd;
    setItemName(startName);
    setItemWeight(initialWg);
}

public double getSpeed() 
{
    return SPEED;
}


public double getDamage()
{
    return DAMAGE;
}

如您所见,作为 武器的DAMAGESPEED不需要改变,暂时可以是最终的。 (如果在游戏后期,我决定这些值可以“升级”,可以这么说,我可以添加设置器,然后进行验证,或者只是使用升级后的值制作一个新武器)它们在武器的构造函数。

结论:getter 和 setter 都很好,只要巧妙使用它们,并且只在需要时使用。 (然而)

I am programming a game in java, and as the question title suggestions i am using public fields in my classes. (for the time being)

From what i have seen public fields are bad and i have some understanding why. (but if someone could clarify why you should not use them, that would be appreciated)

The thing is that also from what i have seen, (and it seems logical) is that using private fields, but using getters and setters to access them is also not good as it defeats the point of using private fields in the first place.

So, my question is, what are the alternatives? or do i really have to use private fields with getters and setters?

For reference here is one of my classes, and some of its methods.

I will elaborate more if needs be.

public double health;
//The player's fields.
public String name;
public double goldCount;
public double maxWeight;
public double currentWeight;
public double maxBackPckSlts;
public double usedBackPckSlts; // The current back pack slots in use
public double maxHealth; // Maximum amount of health
public ArrayList<String> backPack = new ArrayList<String>();

//This method happens when ever the player dynamically takes damage(i.e. when it is not scripted for the player to take damage.
//Parameters will be added to make it dynamic so the player can take any spread of damage.
public void beDamaged(double damage)
{
    this.health -= damage;
    if (this.health < 0)
    {
        this.health = 0;
    }
}

EDIT: For checking purposes, this is what my Weapon class looks like now: (Code sample is not working for some reason, so it does not look right.)

private final double DAMAGE;
private final double SPEED;

public Weapon(double initialDmg,double initialSpd,String startName,double initialWg)
{
    DAMAGE = initialDmg;
    SPEED = initialSpd;
    setItemName(startName);
    setItemWeight(initialWg);
}

public double getSpeed() 
{
    return SPEED;
}


public double getDamage()
{
    return DAMAGE;
}

As you can see, as the Weapon's DAMAGE and SPEED do not need to be changed, they can be final's for the time being. (if, later in the game, i decided these values can be "Upgraded" so to speak, i may add setters then , with validation, or just make a new weapon with the upgraded values) They get set in the Weapon's constructor.

Conclusion: getters and setters are fine, as long as they are used smartly, and only used when needed. (however)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

ζ澈沫 2024-10-17 13:12:52

通常使用 getter 和 setter,而不是授予其他对象直接更改字段的权限。当您看到 99.99% 的 getter 和 setter 除了直接访问字段可以做的事情之外不做任何事情时,这可能没有任何意义。但是,当您决定当一名玩家受到超过一定程度的伤害时,他会掉落一半的物品栏,会发生什么?或者你想限制魔法物品可以使用多少个背包槽位?您要么必须查找代码中修改字段的所有位置,要么,如果您使用 getter 和 setter,则完全在类中进行更改。这是面向对象编程的核心——您将对象所做的“知识”封装在对象本身内,而不是将其分散到与该对象交互的所有对象中。

It's common to use getters and setters instead of giving other objects permission to change your fields directly. That might not make any sense when you see that 99.99% of your getters and setters don't do anything except what you could have done with direct access to the fields. But what happens when you decide that when a player is damaged beyond a point, he drops half his inventory? Or you want to restrict how many backpack slots can be used by magical items? You either have to hunt down all the places in your code where you modify the fields, or, if you used getters and setters, you make the changes entirely in the class. That's the heart of object oriented programming - that you've encapsulated "knowledge" of what an object does within the object itself, not spread it out among all the objects that interact with that object.

一片旧的回忆 2024-10-17 13:12:52

面向对象编程的核心概念之一是封装——即从外部隐藏对象的状态(例如对象中的数据) ,并让对象处理它自己的状态。

当封装做得好时,外界只能通过对象提供的接口(例如对象具有的方法)来影响对象的状态。

我认为您的代码已经开始使用封装。

让我们看一下代码

让我们看一下beDamaged 方法。

public void beDamaged(double damage)
{
    this.health -= damage;

    if (this.health < 0)
    {
        this.health = 0;
    }
}

这里我们可以看到,这个方法会被外界调用,玩家的健康会受到影响。它还包含逻辑,因此健康不能为负数。您编写的播放器的 beDamaged 方法将对象的状态保持在您定义为有效状态的参数内。

让我们推断出有关玩家的一些信息

现在,从上面的内容中,我想我可以推断出有关玩家对象的以下信息:

玩家的生命值不能为负数。

我们推断的总是正确的吗?

让我们看看根据您提供的代码这是否总是正确的。

啊哈!我们这里有一个小问题:

public double health;

由于 health 字段是 public,外界可以直接操作该字段,以便将玩家对象的状态置于可能不是的状态。所需的,通过如下代码:

Player player = new Player();
player.health = -100

我猜测玩家不应该处于 health 为负数的状态。

我们能做些什么?

如何避免这种情况? -- 通过拥有 health 字段 private

现在,影响玩家health的唯一方法是通过beDamagedgainHealth方法,这对于外界来说可能是正确的方法影响玩家的健康。

这也意味着——当您将字段设置为私有时,并不自动意味着您应该为该字段创建 getter 和 setter。

私有字段不需要 getters 和 setters

getters 和 setters 通常是直接影响对象拥有的字段的一种方式,也许需要进行一些验证,以防止错误的输入使对象处于不应有的状态不是,但有时对象本身应该负责影响数据,而不是外部实体。

One of the core concepts of object-oriented programming is encapsulation -- that is, hiding an object's state (for example, the data in the object) from the outside, and letting the object handle it's own state.

When encapsulation is done well, the object's state can only be affected from the outside world through the interfaces provided by the object, such as methods the object has.

I think your code is already starting to use encapsulation.

Let's take a look at the code

Let's take a look at the beDamaged method.

public void beDamaged(double damage)
{
    this.health -= damage;

    if (this.health < 0)
    {
        this.health = 0;
    }
}

Here's we can see that this method will be called by the outside world, and the player's health will be affected. It also contains logic, so the health cannot be a negative number. The player's beDamaged method that you wrote is keeping the state of the object within the parameters that you defined as being the valid state.

Let's infer something about the player

Now, from the above, I think I can infer the following about the player object:

A player's health cannot be a negative number.

Is what we inferred always true?

Let's see if this can always be true from the code you've provided.

Aha! We have a little problem here:

public double health;

With the health field being public, the outside world can directly manipulate the field in order to place the player object's state into one that is probably not desired, by some code like the following:

Player player = new Player();
player.health = -100

I'm going to guess that the player shouldn't be in a state where the health is a negative number.

What can we do about it?

How could that have been avoided? -- by having the health field private.

Now, the only way to affect the player's health would be through the beDamaged and gainHealth methods, and that's probably the right way for the outside world to affect your player's health.

Which also means this -- when you make a field private, that does not automatically mean that you should make getters and setters for the field.

Private fields does not necessitate getters and setters

Getters and setters are usually a way to directly affect a field that an object has, maybe with some validation to prevent bad input from making your object have a state that it shouldn't, but there are going to be times where the object itself should be in charge of affecting the data, rather than an outside entity.

2024-10-17 13:12:52

在 Java 中,建议将私有字段与 getter/setter 一起使用,假设类的外部客户端确实需要访问这些字段

否则,将它们保留为私有字段,并且不提供 getter/setter。

这是最佳实践的原因有多种:

  1. 如果客户直接使用您的领域,然后需要对此进行一些更改,那么您就会陷入困境。使用 getter,您可以在访问字段之前做很多事情。
  2. 有一个称为 JavaBeans 规范的东西要求您使用 getter/setter。没有它们,您的类(然后称为 bean)将无法与之互操作。 JSP 和 JSF 的 EL 是要求您的类符合 JavaBeans 标准的示例之一。

<子>
(ps与您的问题无关,但您最好不要将 backPack 声明为 ArrayList。声明为 List;代码到接口,而不是实现)

In Java, using private fields with getters/setters is the recommend practice, provided external clients of your class really need access to those fields.

Otherwise keep them as private fields and simply don't provide a getter/setter.

There are various reasons why this is a best practice:

  1. If clients are using your field directly and later something needs to change regarding that, you're stuck. With a getter you can do a whole lot of things before the field is accessed.
  2. There is something called the JavaBeans specification that requires you to use getter/setters. Without them your class (then called bean) won't interoperate with that. JSP and JSF's EL is one example of something that required your class to comply with JavaBeans standards.


(p.s. unrelated to your question, but you'd better not declare backPack as an ArrayList. Declare as List; code to interface, not to implementation)

爱你是孤单的心事 2024-10-17 13:12:52

如果您有一个带有方法 get() 和方法 set() 的私有字段,除了检索和分配值之外不执行任何操作,那么您应该公共领域,因为该领域并不是真正的私有领域,并且 getter 和 setter 只会损害性能。如果 getter 和 setter 检查正在设置的值或者是否允许检索该值,则继续使用 getter 和 setter。例如,如果您有一个变量 private int width; 并且有人尝试使用 setter 放入 -1,并且 setter 确保它不是负值,那么那就是很好用。例如:

private int width;
public int get(){
    return width;
}
public void set(int w){
    if (w < 0) throw new RuntimeException();
    else width = w;
}

这将是 getter 和 setter 的一个很好的用途。否则,如果他们所做的唯一事情就是分配或获取值而没有其他任何事情,那么他们会损害你的性能。

长话短说:

在执行除检索或分配值之外的任何操作时,请使用 getter 和 setter。否则,只需使用公共字段。

BAD:

private int width;
public int get(){
    return width;
}
public void set(int w){
    width = w;
}

GOOD:

private int width;
public int get(){
    return width;
}
public void set(int w){
    if (w < 0) throw new RuntimeException();
    else width = w;
}

GOOD 如果您除了获取或设置之外不需要任何其他内容:

public int width;

If you have a private field with a method get() and a method set() that don't do anything other than retrieve and assign the value, you should just make the field public, as the field isn't really private, and the getters and setters only hurt performance. If the getters and setters check the value being set or if the value is allowed to retrieve, then go ahead and use getters and setters. e.g. If you have a variable private int width; and someone tries to put in -1 with a setter, and the setter makes sure it isn't negative, then that is a good use. For example:

private int width;
public int get(){
    return width;
}
public void set(int w){
    if (w < 0) throw new RuntimeException();
    else width = w;
}

This would be a good use of getters and setters. Otherwise, they hurt your performance if the only thing they do is assign or get the value without anything else.

So to make a long story short:

Use getters and setters when doing anything other than retrieving or assigning a value. Else, just use public fields.

i.e.

BAD:

private int width;
public int get(){
    return width;
}
public void set(int w){
    width = w;
}

GOOD:

private int width;
public int get(){
    return width;
}
public void set(int w){
    if (w < 0) throw new RuntimeException();
    else width = w;
}

GOOD if you don't want anything other than getting or setting:

public int width;
鹤舞 2024-10-17 13:12:52

关于此:

问题是,从我所看到的来看,(这似乎是合乎逻辑的)是使用私有字段,但是使用 getter 和 setter 来访问它们也不好,因为它破坏了首先使用私有字段的意义地点。

主要问题是许多开发人员自动为所有私有字段生成 getter 和 setter。如果你打算这样做,我同意,你最好保持该字段公开(不,公共字段更糟糕)。

对于您拥有的每个字段,您应该检查:

a)它是否需要 Getter(其他类是否需要知道该字段的值)
b) 是否需要一个Setter(其他类是否需要能够更改该字段的值)
c) 或者该字段是否需要是不可变的(最终的),如果是这样,它必须在定义期间或在构造函数中初始化(并且它显然可以没有设置器)

但是您几乎不应该(例外:值对象)假设所有私有字段将具有 getter 和 setter,并让您的 IDE 生成它们。

About this:

The thing is that also from what i have seen, (and it seems logical) is that using private fields, but using getters and setters to access them is also not good as it defeats the point of using private fields in the first place.

The main problem is that many developers automatically generate getters and setters for all private fields. And if you're going to do that, I agree, you might as well keep the field public (no, public fields are even worse).

For every field that you have, you should check:

a) does it need a Getter (do other classes need to know the value of this field)
b) does it need a Setter (do other classes need to be able to change the value of this field)
c) or does the field need to be immutable (final), if so it must be initialized during definition or in the constructor (and it can obviously have no setter)

But you should hardly ever (exception: value objects) assume that all private fields will have getters and setters and let your IDE generate them all.

初心 2024-10-17 13:12:52

使用 getter,尤其是 setter 的一个优点是,调试对字段的写入访问要容易得多。

An advantage of using getters and especially setters is, that it is much easier to debug write access to the fields.

情归归情 2024-10-17 13:12:52

私有字段以及 setter 和 getter 确实是您最好的选择。

还要注意的是,这对于任何语言来说都是很好的代码,因为它可以使您的安全性保持良好和严格,同时还为您提供了一个更容易调试和维护的结构。 (顺便说一句,不要忘记记录!)

总而言之,使用 setter 和 getter,即使您找到了选项,这也是一个很好的实践。

private fields and setters and getters is indeed your best way to go.

Further note that this is in general good code in any language as it keeps your security nice and tight while also giving you a structure that is far easier to debug and maintain. (Don't forget to document btw!)

All in all, go with setters and getters, it's just good practice even if you find options.

爱她像谁 2024-10-17 13:12:52

getter 和 setter 是类的公共接口的一部分。这是类设计者/开发者和该类的用户之间的合同。当您定义 getter 和 setter 时,您应该致力于在未来版本中维护它们。

属性应该只对应于给定版本的类的实现。通过这种方式,类开发人员可以单方面更改实现,从而更改字段,而不会违反他/她维护接口的承诺。

这是一个例子。考虑一个名为 Point 的类。如果您确定一个点具有 x 和 y 公共属性,那么您可能永远不会更改它。相反,如果您有 get/set X/Y 方法,则该类的后续版本可能会使用各种内部表示形式:直角坐标 (x, y),还有极坐标 (r, theta) 等。所有这些都无需修改 public界面。

Getters and setters are part of the public interface of your class. It's a contract between the class designer/developer and the users of that class. When you define getters and setters, you should be committed to maintain them in future versions.

Attributes should only correspond the implementation of a given version of the class. In this way, the class developer may unilaterally change the implementation, hence the field, without breaking his/her commitment to maintain the interfaces.

Here is an example. Consider a class called Point. If you decide that a Point has x and y public attributes, then you may never change this. In contrast, if you have get/set X/Y methods, subsequent versions of the class may use various internal representations: rectangular coordinates (x, y), but also polar (r, theta), etc. All this without modifying the public interface.

夜灵血窟げ 2024-10-17 13:12:52

你的方法的较短版本...

public void beDamaged(double damage) {
    health = Math.max(0, health-damage);
}

public void gainHealth(double gainedHp) {
    health = Math.min(maxHealth, health + gainedHp);
}

甚至可以调用以下方法,+1 获得增益,-1 损失 1 hp。

public void adjustHealth(double adjustHp) {
    health = Math.max(0, Math.min(maxHealth, health + adjustHp));
}

A shorter version of your methods...

public void beDamaged(double damage) {
    health = Math.max(0, health-damage);
}

public void gainHealth(double gainedHp) {
    health = Math.min(maxHealth, health + gainedHp);
}

or even the following which can be called with +1 to gain, -1 to lose 1 hp.

public void adjustHealth(double adjustHp) {
    health = Math.max(0, Math.min(maxHealth, health + adjustHp));
}
℡Ms空城旧梦 2024-10-17 13:12:52

如果您不维护任何不变量,那么公共字段就是最佳选择。如果您确实需要跨多个成员的不变量,那么您需要私有字段和封装。

但是,如果您无法为这些方法想出比 GetFoo 和 SetFoo 更好的名称,则说明您的 getter 和 setter 可能毫无价值。

If you're not maintaining any invariants, then public fields are the way to go. If you do need an invariant across multiple members, then you need private fields and encapsulation.

But if you can't come up with any better names than GetFoo and SetFoo for the methods, it's a good clue that your getters and setters are probably worthless.

一世旳自豪 2024-10-17 13:12:52

....省略了可悲的内容....

编辑

抱歉有点太可悲了 - 一定是药丸...其他答案非常相关并且很好

.... pathetic content omitted....

EDIT

sorry for beeing a little too pathetic -must be the pills... The other answers are quite relevant and good

薄凉少年不暖心 2024-10-17 13:12:52

避免公共字段的一个优点尚未提及:如果没有任何公共字段,则可以定义一个包含该类的所有公共功能的接口,让该类实现该接口,然后让使用该类的每个地方使用接口代替。如果这样做了,以后可能会设计一个具有完全不同的方法和字段但实现相同接口的类,并且可以与原始类互换使用该类。如果这样做了,那么让类除了构造函数之外还实现静态工厂方法,并让工厂返回接口类型的对象可能会很有用。这样做将允许工厂的更高版本返回某种其他类型的对象。例如,人们可能会想出一种低成本版本的对象,其中许多属性返回常量;工厂可以查看这样的对象是否合适,如果合适,则返回一个对象而不是普通对象。

顺便说一句,在冒险中混合使用常量和可变对象的概念至少可以追溯到 1980 年。在 Warren Robinett 的 2600 的“冒险”卡带中,每个对象都有许多存储在 ROM 中的指针,用于指示位置和状态等信息,因此不会移动的对象(例如城堡大门或“签名”)不需要将其位置存储在 RAM 中,并且大多数可抓取对象(除了位置之外没有任何状态) )不需要在 RAM 中存储状态,但像龙和蝙蝠这样的动画对象可以在 RAM 中存储状态和位置。在 RAM 总量为 128 字节的机器上,这种节省至关重要。

One advantage not yet mentioned for avoiding public fields: if there aren't any public fields, one may define an interface that includes all the public features of the class, have the class implement that interface, and then have everyplace that uses the class use the interface instead. If that is done, one may later design a class which has completely different methods and fields, but which implements the same interface, and use that class interchangeably with the original. If this is done, it may be useful to have the class implement a static factory method in addition to the constructor, and have the factory return an object of the interface type. Doing that would allow later versions of the factory to return an object of some other type. For example, one may come up with a low-cost version of the object in which many properties return constants; the factory could see if such an object would be suitable, and if so return one instead of the normal object.

Incidentally, the concept of using a mixture of constant and mutable objects in an adventure goes back at least to 1980. In Warren Robinett's "Adventure" cartridge for the 2600, each object has a number of pointers stored in ROM for things like position and state, so objects which aren't going to move (such as the castle gates or the "signature") don't need to have their position stored in RAM, and most grabbable objects (which don't have any state other than their position) won't need to store a state in RAM, but animated objects like the dragons and bat can store both state and position in RAM. On a machine with 128 bytes of RAM total, such savings were critical.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文