公共领域有哪些替代方案?
我正在编写一个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 backPack = new ArrayList(); //This method happens when ever the player dynamically takes damage(ie 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
DAMAGE
和SPEED
不需要改变,它们暂时可以是最终的。 (如果,在游戏的后期,我决定这些值可以“升级”可以这么说,我可以添加setter然后,通过validation,或者只是使用升级后的值制作一个新武器)它们在Weapon's
构造函数中设置。
结论:吸气剂和固定剂都很好,只要它们被巧妙地使用,并且仅在需要时使用。 (然而)
使用getter和setter而不是让其他对象直接更改字段的权限是很常见的。 当你看到99.99%的getter和setter没有做任何事情时,这可能没有任何意义,除了你可以直接访问字段所做的事情。 但是当你决定当一名球员受到超过一分的伤害时,会发生什么呢? 或者你想限制魔法物品可以使用多少背包槽? 您可能需要搜索代码中修改字段的所有位置,或者,如果您使用了getter和setter,则完全在类中进行更改。 这是面向对象编程的核心 – 您已经封装了对象在对象内部所做的“知识”,而不是在与该对象交互的所有对象中进行扩展。
面向对象编程的核心概念之一是封装 – 即从外部隐藏对象的状态(例如,对象中的数据),并让对象处理它自己的状态。
当封装完成时,对象的状态只能通过对象提供的接口来影响外部世界,例如对象具有的方法。
我认为你的代码已经开始使用封装了。
我们来看看代码吧
我们来看看beDamaged
方法。
public void beDamaged(double damage) { this.health -= damage; if (this.health < 0) { this.health = 0; } }
我们可以看到这个方法将被外界调用,玩家的健康状况将受到影响。 它还包含逻辑,因此健康状况不能为负数。 您编写的玩家的beDamaged
方法是将对象的状态保持在您定义为有效状态的参数中。
让我们推断一下玩家的情况
现在,从上面的内容来看,我认为我可以推断出以下关于玩家对象的内容:
玩家的
health
不能是负数。
我们推断总是如此吗?
让我们看看你提供的代码是否总是如此。
啊哈! 我们这里有一点问题:
public double health;
由于health
领域是public
,外部世界可以直接操纵该领域,以便通过以下代码将玩家对象的状态置于可能不需要的状态:
Player player = new Player(); player.health = -100
我猜测玩家不应该处于health
为负数的状态。
我们对于它可以做些什么呢?
怎么可以避免这个? - 将health
领域private
。
现在,影响球员health
的唯一方法是通过beDamaged
和gainHealth
方法,这可能是外界影响球员健康的正确方法。
这也意味着这一点 - 当你将一个字段private
,这并不意味着你应该为该字段制作getter和setter。
私人领域不需要吸气剂和二传手
getter和setter通常是一种直接影响对象所具有的字段的方法,可能需要进行一些validation以防止错误的输入使得对象具有不应该存在的状态,但是有时候对象本身应该是负责影响数据,而不是外部实体。
在Java中, 如果您的类的外部客户端确实需要访问这些字段 ,则建议使用带有getter / setter的私有字段 。
否则将它们保存为私有字段,并且不提供getter / setter。
这是最佳做法的原因有多种:
- 如果客户直接使用你的领域,后来有些事情需要改变,你就会陷入困境。 使用getter,您可以在访问字段之前执行大量操作。
- 有一种叫做JavaBeans规范的东西要求你使用getter / setter。 没有它们,你的类(当时称为bean)将无法与之互操作。 JSP和JSF的EL是需要您的类遵守JavaBeans标准的一个示例。
(ps与您的问题无关,但您最好不要将backPack声明为ArrayList。声明为List;代码接口,而不是实现)
如果你有一个带有方法get()
和方法set()
的私有字段除了检索和赋值之外什么都不做,你应该只将该字段设为public,因为该字段不是真正私有的,并且吸气者和制定者只会伤害表现。 如果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。 否则,只需使用公共字段。
即
坏:
private int width; public int get(){ return width; } public void set(int w){ width = w; }
好:
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;
对这个:
事情是,从我所看到的,(并且似乎合乎逻辑)是使用私有字段,但使用getter和setter访问它们也不好,因为它首先打败了使用私有字段的点。
主要问题是许多开发人员自动为所有私有字段生成getter和setter。 如果你打算这样做,我同意,你也可以把这个领域公之于众(不,公共领域更糟)。
对于您拥有的每个领域,您应该检查:
a)是否需要Getter(其他类需要知道该字段的值)
b)是否需要Setter(其他类需要能够更改此字段的值)
c)或者字段是否必须是不可变的(最终),如果是这样,它必须在定义期间或在构造函数中初始化(并且它显然可以没有setter)
但是你几乎不应该(例外:值对象)假设所有私有字段都有getter和setter,并让IDE生成所有这些字段。
使用getter特别是setter的一个优点是, 调试对字段的写访问要容易得多。
私人田地,制定者和吸气者确实是你最好的方式。
进一步注意,这通常是任何语言的良好代码,因为它可以保证您的安全性和安全性,同时还为您提供了一个更容易调试和维护的结构。 (别忘了记录顺便说一句!)
总而言之,与安装者和吸气剂一起使用,即使你找到了选择,这也是很好的做法。
Getter和setter是您class级公共界面的一部分。 这是类设计人员/开发人员与该类用户之间的合同 。 定义getter和setter时,应该承诺在将来的版本中维护它们。
属性应仅对应于给定版本的类的实现。 通过这种方式,类开发人员可以单方面地改变实现,从而改变字段,而不会破坏他/她维护接口的承诺。
这是一个例子。 考虑一个名为Point的类。 如果您确定Point具有x和y公共属性,那么您可能永远不会更改它。 相反,如果您已经获得/设置了X / Y方法,则该类的后续版本可以使用各种内部表示:直角坐标(x,y),还有极坐标(r,theta)等。所有这些都不需要修改公共接口。
您的方法的较短版本……
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马力。
public void adjustHealth(double adjustHp) { health = Math.max(0, Math.min(maxHealth, health + adjustHp)); }
如果您没有维护任何不变量,那么公共字段就是您的选择。 如果确实需要跨多个成员的不变量,那么您需要私有字段和封装。
但是如果你不能拿出比GetFoo和SetFoo更好的名字来获得这些方法,那么你的获取者和制定者可能毫无价值就是一个很好的线索。
….可怜的内容省略….
编辑
抱歉有点过于可怜 – 必须是药片…其他答案非常相关和好
还没有提到避免公共字段的一个优点:如果没有任何公共字段,可以定义一个包含该类的所有公共特性的接口,让类实现该接口,然后让每个使用该类的地方使用而是界面。 如果这样做,可以稍后设计一个具有完全不同的方法和字段但实现相同接口的类,并将该类与原始类交替使用。 如果这样做,除了构造函数之外让类实现静态工厂方法并使工厂返回接口类型的对象可能是有用的。 这样做会允许更高版本的工厂返回其他类型的对象。 例如,可以提出一个低成本版本的对象,其中许多属性返回常量; 工厂可以看到这样的物体是否合适,如果是这样,则返回一个而不是普通物体。
顺便说一句,在冒险中使用常量和可变对象混合的概念至少可以追溯到1980年。在Warren Robinett的2600的“冒险”盒式磁带中,每个对象都有一些指针存储在ROM中,用于位置和状态等,所以不会移动的物体(例如城堡大门或“签名”)不需要将它们的位置存储在RAM中,而且大多数可抓取的物体(除了它们的位置之外没有任何其他状态) )不需要在RAM中存储状态,但龙和蝙蝠等动画对象可以在RAM中存储状态和位置。 在总共128字节RAM的计算机上,这种节省是至关重要的。