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

时间:2011-01-01 15:17:36

标签: java c++ class field public

我正在使用java编写游戏,并且作为问题标题建议我在我的课程中使用公共字段。 (暂时)

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

事情是,从我所看到的,(并且看起来合乎逻辑)是使用私有字段,但使用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;
}

正如您所看到的,由于Weapon's DAMAGESPEED不需要更改,因此它们暂时可以是最终版本。 (如果,在游戏后期,我决定这些值可以“升级”可以这么说,我可以添加setter然后进行验证,或者只是使用升级后的值制作新武器)它们在{{1}中设置构造函数。

结论:吸气剂和固定剂都很好,只要它们被巧妙地使用,并且仅在需要时使用。 (然而)

12 个答案:

答案 0 :(得分:21)

通常使用getter和setter而不是让其他对象直接更改字段的权限。当你看到99.99%的getter和setter没有做任何事情时,这可能没有任何意义,除了你可以直接访问字段所做的事情。但是当你决定当一名球员受到超过一分的伤害时,会发生什么呢?或者你想限制魔法物品可以使用多少背包槽?您可能需要搜索代码中修改字段的所有位置,或者,如果您使用了getter和setter,则完全在类中进行更改。这是面向对象编程的核心 - 你已经封装了对象在对象内部所做的“知识”,而不是在与该对象交互的所有对象中进行扩展。

答案 1 :(得分:18)

object-oriented programming的核心概念之一是encapsulation - 也就是说,从外部隐藏对象的状态(例如,对象中的数据),并让对象自己处理它状态。

当封装完成时,对象的状态只能通过对象提供的接口来影响外部世界,例如对象具有的方法。

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

让我们来看看代码

我们来看看beDamaged方法。

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

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

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

让我们推断一下玩家

现在,从上面的内容来看,我认为我可以推断出关于玩家对象的以下内容:

  

玩家health不能为负数。

我们推断的总是如此吗?

让我们看看你提供的代码是否总是如此。

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

public double health;

health字段为public时,外部世界可以直接操作该字段,以便将播放器对象的状态置于可能不需要的状态,如下所示:< / p>

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

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

我们可以做些什么?

怎么可以避免这种情况? - 拥有health字段private

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

这也意味着这一点 - 当你创建一个字段private时,这并不意味着你应该为该字段制作getter和setter。

私人字段不需要getter和setter

getter和setter通常是一种直接影响对象所具有的字段的方法,可能需要进行一些验证以防止错误输入使得对象具有不应该存在的状态,但是有时会出现这种情况。对象本身应该负责影响数据,而不是外部实体。

答案 2 :(得分:8)

在Java中,建议使用带有getter / setter的私有字段,如果您的类的外部客户端确实需要访问这些字段

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

为什么这是最佳做法有多种原因:

  1. 如果客户直接使用您的字段,以后需要更改某些内容,那么您就会陷入困境。使用getter,你可以在访问字段之前做很多事情。
  2. 有一种叫做JavaBeans规范的东西要求你使用getter / setter。没有它们,你的类(当时称为bean)将无法与之互操作。 JSP和JSF的EL是需要您的类遵守JavaBeans标准的一个例子。
  3. <子> (p.s.与您的问题无关,但您最好不要将backPack声明为ArrayList。声明为List;代码接口,而不是实现)

答案 3 :(得分:4)

如果您的私有字段包含方法get()和方法set(),除了检索和分配值之外不执行任何操作,您应该将该字段设为公共字段,如字段并不是真正的私人,吸气者和制定者只会伤害表现。如果getter和setter检查正在设置的值或者是否允许检索该值,那么继续使用getter和setter。例如如果你有一个变量private int width;并且有人试图用{set}放入-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。否则,只需使用公共领域。

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

如果除了获取或设置之外你不需要任何其他内容,那就好了。

public int width;

答案 4 :(得分:3)

关于这个:

  

事情是,从我所看到的,(并且看起来合乎逻辑)是使用私有字段,但使用getter和setter访问它们也不好,因为它在第一次使用私有字段时失败了的地方。

主要问题是许多开发人员会自动为所有私有字段生成getter和setter。如果你打算这样做,我同意,你不妨公开场地(不,公共领域更糟)。

对于您拥有的每个领域,您应该检查:

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

但你几乎不应该(例外:值对象)假设所有私有字段都有getter和setter,并让IDE生成所有这些字段。

答案 5 :(得分:2)

使用getter特别是setter的一个优点是,调试写访问更容易到字段。

答案 6 :(得分:1)

私人领域,制定者和吸气者确实是你最好的选择。

进一步注意,这通常是任何语言的优秀代码,因为它可以保证您的安全性和安全性,同时还为您提供了一个更容易调试和维护的结构。 (别忘了记录顺便说一句!)

总而言之,即使你找到了选择,这也是很好的做法。

答案 7 :(得分:1)

Getters和setter是您班级的公共接口的一部分。这是类设计人员/开发人员与该类用户之间的合同。定义getter和setter时,应该承诺在将来的版本中维护它们。

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

这是一个例子。考虑一个名为Point的类。如果您确定Point具有x和y公共属性,那么您可能永远不会更改它。相反,如果你已经获得/设置了X / Y方法,那么该类的后续版本可能会使用各种内部表示:直角坐标(x,y),还有极坐标(r,theta)等。所有这些都不需要修改公众接口

答案 8 :(得分:1)

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

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));
}

答案 9 :(得分:1)

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

但是如果你不能拿出比GetFoo和SetFoo更好的名字来获得这些方法,那么你的获取者和制定者可能毫无价值就是一个很好的线索。

答案 10 :(得分:0)

....遗漏了可怜的内容......

编辑抱歉有点过于可怜 - 必须是药片......其他答案非常相关且很好

答案 11 :(得分:0)

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

顺便提一下,在冒险中使用常量和可变对象混合的概念至少可以追溯到1980年。在Warren Robinett的2600的“冒险”盒式磁带中,每个对象都有一些存储在ROM中的指针用于位置和状态,所以不会移动的物体(例如城堡大门或“签名”)不需要将它们的位置存储在RAM中,而且大多数可抓取的物体(没有任何其他状态)而不是他们的位置)不需要在RAM中存储状态,但是像龙和蝙蝠这样的动画对象可以在RAM中存储状态和位置。在总计128字节RAM的计算机上,这种节省是至关重要的。