我目前正在用几种不同模式用Java开发一个简单的游戏。我扩展了Game类的主要内容,以将主要逻辑放入其他类中。尽管如此,主要的游戏类别仍然非常庞大。
快速浏览了我的代码后,大多数是Getters和Setters(60%),而其余部分则是真正需要的。
一些Google搜索声称Getters和Setters是邪恶的,而其他人则声称它们对于良好的OO实践和出色的程序是必需的。 >那我该怎么办?应该是哪个?我应该更改私有变量的Getter和Setters,还是应该坚持使用它们?
#1 楼
还有一种观点认为,在大多数情况下,使用setter仍会通过允许您设置无意义的值来破坏封装。举一个非常明显的例子,如果您在游戏中拥有一个只会不断提高的得分计数器,而不是>
// Game
private int score;
public void setScore(int score) { this.score = score; }
public int getScore() { return score; }
// Usage
game.setScore(game.getScore() + ENEMY_DESTROYED_SCORE);
这也许是一个简单的例子。我要说的是,讨论吸气剂/吸气剂与公共领域的问题通常会掩盖更大的问题,因为对象以亲密的方式操纵彼此的内部状态,因此彼此之间的联系过于紧密。制作直接执行您想做的事情的方法。一个例子就是如何设置敌人的“存活”状态。您可能会想拥有一个setAlive(boolean alive)方法。相反,您应该具有:
// Game
private int score;
public int getScore() { return score; }
public void addScore(int delta) { score += delta; }
// Usage
game.addScore(ENEMY_DESTROYED_SCORE);
原因是,如果更改实现,则事物将不再具有“有效”布尔值,而是“命中点”值,您可以在不破坏您先前编写的两种方法的约定的情况下进行更改:
private boolean alive = true;
public boolean isAlive() { return alive; }
public void kill() { alive = false; }
评论
用c#或actionscript这样的语言处理,get和set是语法的一部分吗? isAlive只能有一个吸气剂,而没有一个二传手。
–伊恩
09年7月7日在11:07
重要的是在此处注意,可以在这些“操作”方法中进行验证。
–dseibert
2011-3-24在21:22
“使用二传手仍然允许您设置无意义的值,从而破坏了封装。”废话,您可以验证要在setter方法中设置的值,从而避免有意义的值
– Onur A.
13年8月2日在6:07
这是1k倍。不要从对象中获取状态数据,在其他地方进行更改,然后再放回去。将完成工作所需的所有数据提供给它。就像OOP 101。
– DanMan
2014年11月25日12:52
正如DanMan所说,OOP 101说:“告诉,不要问。”告诉对象为您执行任务并相信它可以执行。不要使用sets()和gets()对其进行微管理,然后自己完成这项工作,这几乎就像是养狗和吠叫自己一样。
–将
17年8月23日在7:12
#2 楼
非常邪恶:公共领域。
有些邪恶:不需要的Getter和Setters。
优点:仅在真正需要的Getter和Setters上,使类型暴露“较大“恰好使用其状态的行为,而不仅仅是将类型视为要由其他类型操纵的状态存储库。
这实际上取决于情况-有时您确实只想要一个状态哑数据对象。
评论
由于您的“有些邪恶”案例最终并不需要“更大的行为”这一点并不明显,因此我以“好的”方式进行了介绍,以防万一,因此,如果您愿意,不必在任何地方更改代码改变你的想法 !
–贝诺
09年2月19日在12:40
这些值中的每一个都会随着游戏的进行而变化,因此不需要常量。但是,这些变量仅真正用于保存实际游戏的设置(即mode,timeLimit)。这就是为什么我不禁感到它们是不必要的。
– Mike B
09年2月19日在12:45
@Mo-对于一个普通的老pojo对象,有很多话要说:)
–丹尼尔(Daniel Earwicker)
09年4月17日在20:39
只是在尝试将POJO提升为Bean时不要忘记进行YAGNI检查。访问器,序列化等是一种复杂的形式:stackoverflow.com/questions/601721/decoupling-vs-yagni
–克里斯·诺(Chris Noe)
2010年8月24日15:11
@JanezKuhar:我的意思是说,不是让调用者执行多个操作来分多个步骤操作状态,而是基本上将类型保留为毫无意义的状态包,而是公开执行可能操作(或至少使用)有意义操作的方法那些多重状态。
–乔恩·斯基特(Jon Skeet)
17年2月25日在21:33
#3 楼
您已经对此有了很多好的答案,所以我只给我两分钱。吸气剂和吸气剂是非常非常邪恶的。从本质上讲,它们使您假装在大多数情况下都将自己的工作隐藏在冗余代码中,而这些代码对隐藏内部状态没有任何作用。对于简单的POJO,没有理由不能用obj.name =“ Tom”替换getName()和setName()。如果方法调用仅替换赋值,那么通过偏爱方法调用而获得的全部就是代码膨胀。不幸的是,该语言在JavaBeans规范中包含了getter和setter的用法,因此Java程序员被迫使用它们,即使这样做毫无意义。
幸运的是,Eclipse(以及其他IDE)也可以让您自动生成它们。对于一个有趣的项目,我曾经在XSLT中为他们构建了一个代码生成器。但是,如果我要用Java摆脱一件事,那就是过分依赖getter和setter。
评论
我已解决此问题,因此我的反对意见更加明确。
–rtperson
09年2月20日在19:24
ob.name =“ Tom”的工作方式与ob.setName(“ Tom”)相同,但是由于它不允许您稍后在不更改API的情况下添加行为,因此效果较差。这是面向对象的编程101。
–杰西·巴纳姆(Jesse Barnum)
2011-2-25在19:03
@杰西·巴纳姆(Jesse Barnum)-我知道,在撰写本文时,我表达了一个异类意见。但是,随着我们进入一个越来越多的内核世界,越来越多的人发现面向对象模型越来越短。一方面,试图在多个线程之间共享状态绝对是一团糟。由于这些和其他原因,我最近迷上了Haskell之类的语言,其中不变性和功能范例使在更少的空间中完成更多工作变得更加容易。我不是反Java的人,只是因为它的局限性和官僚的冗长而感到沮丧。 YMMV。
–rtperson
2011-2-25在21:49
在我编程的所有岁月中,我从来不需要更改获取器或设置器以进行其他操作。对于已发布的API,getter和setter是。在一个项目中,您可以在IDE中查看该字段的所有用途,而公共字段既好又不那么冗长,并且如果您需要特定的行为,则可以轻松地使用封装字段。
– artbristol
2012-12-16 9:42
@artbristol和C#基本上遵循这种哲学,允许您创建一个公共领域,然后将其更改为属性(尽管不幸的是,这存在一些小问题)。
–LegendLength
16年1月7日,下午2:46
#4 楼
获取器和设置器在面向对象的编程中强制执行封装的概念。对象具有隐藏在外部世界中的状态,因此该对象真正负责其自身,并且不能以任何方式更改这不是故意的。只能通过公开的公共方法(例如getter和setter)来操作对象。
使用getter和setter有一些优点:
1。
使用getter和setter的最大优势之一是,一旦定义了公共方法,就会有一段时间需要底层实现通过将getter和setter作为操作对象的唯一方法,可以进行更改(例如,发现需要修复的错误,使用不同的算法来提高性能等),这将使现有代码不会中断,例如,假设有一个
setValue
方法可以在对象中设置value
私有变量:public void setValue(int value)
{
this.value = value;
}
但随后,有一个新的要求,该要求需要跟踪
value
的更改次数。将设置器放置在适当位置后,更改就变得微不足道了:跟踪更改值的次数。因此,具有getter和setter是“面向未来”的类的一种方法,以便将来进行更改。2。强制执行操作对象的方法。
getter和setter派上用场的另一种方法是强制执行操作对象的方法,因此,对象处于自己的状态控制之下。暴露对象的公共变量,很容易损坏它。
例如,一个
value
对象包含一个名为ImmutableArray
的int
数组。如果数组是一个公共字段,那么它将不会是不变的:写入后将返回其数组的副本:
public void setValue(int value)
{
this.value = value;
count++;
}
即使发生以下情况: />
myArray
确实是一成不变的。公开对象的变量将使它能够以非预期的方式进行操作,而仅公开某些方式(获取器和设置器),就可以以预期的方式对对象进行操作。我想对于将要由其他人使用的API中的类而言,拥有getter和setter方法将更为重要,因为它可以使API保持完整和不变,同时允许对基础实现进行更改。
鉴于getter和setter的所有优点,如果getter仅返回私有变量的值,并且setter仅接受一个值并将其分配给私有变量,则getter和setter似乎是无关紧要的,实际上浪费。如果该类仅供内部使用,而不会被其他人使用,则广泛使用getter和setter可能不如编写公共API那样重要。
评论
不,您无论如何都只能将二传手**设为私有**。不变性是另一个主题,例如,定义为不可修改的是在创建像String这样的值后您无法更改其值。因此,在您的示例中,这是一个错误。查看C#或语言支持属性。
–西迪
2011年7月26日在20:39
@Simo-在示例中,后备数组是不可变的。
– wulfgarpro
2011年9月7日下午0:11
@coobird-即使getter / setter只是简单地返回/设置而没有附加逻辑,当可能需要附加逻辑时,它们是否将来也不会证明API?
– wulfgarpro
2011年9月7日于0:12
@ wulfgar.pro:是的,他们肯定会这样做,这就是使用setter和getter的优点之一。
–coobird
2011年9月7日15:46
获取器和设置器不执行任何操作。 javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html。好的封装==您的API遵循某些实际的业务逻辑,而不遵循行数据。例如,对象Car可能具有以下方法:accellerate(Integer spead){设置速度并执行其他10件事-好的封装}或setSpeed(Integer speed){//只是setter-不良封装}。
– smentek
2012年4月13日19:17
#5 楼
它们绝对是邪恶的。@coobird不幸的是,它们绝对不会“强制执行封装的概念”,它们所做的只是使您认为您实际上是在封装数据,而实际上您是通过带有方法宏大幻想的财产。 getter / setter在公共领域所做的任何事情都会更好。努力使客户改变其价值,例如通过
object.field = value;
而不是在认知上更加紧张
object.setField(value);
客户现在必须检查getter / setter方法是否有副作用。
其次,如果您真的需要在该方法中做其他事情,为什么要承担更多的责任而不是简单地获取或设置方法呢?
要么遵循SRP,要么调用该方法实际上可以告诉您整个方法的功能,例如Zarkonnen提到的示例。 />
public void kill(){
isAlive = false;
removeFromWorld(this);
}
而不是
public void setAlive(boolean isAlive){
this.isAlive = isAlive;
if (isAlive)
addToWorld(this);
else
removeFromWorld(this);
}
setAlive(boolean)方法在哪里告诉客户端,效果会从中删除对象 世界?客户为什么要对isAlive字段有任何了解?再加上将对象重新添加到世界时会发生什么情况,是否应该重新初始化?客户为什么要关心其中的任何一个?
恕我直言,道德上要说出方法的确切名称,遵循SRP并摆脱吸气剂/吸水剂。
如果存在问题没有getters / setter的人,告诉对象在自己的类中做自己的肮脏的工作,而不是尝试在其他类中与他们做事情。 >
评论
顺便说一句,想想一个BankAccount对象,您是否更喜欢setBalance()方法或gainBalance()方法?
–内森
2011年10月8日,11:17
想评论比尔蜥蜴的帖子...
–内森
2011年10月8日20:51
为了进一步补充这一点,认为添加功能(例如“日志记录”或“验证”或“变异”)的最佳位置在设置者上是一个误解。理想情况下,我们将在其他地方进行此类操作。例如,我们更倾向于将日志添加到有意义(例如保存或通过电子邮件发送)的地方,而不是一般的设置者。或者,我们可能需要根据上下文进行验证(即,我们是针对保存进行验证还是针对电子邮件进行验证?)。另外,很多时候您想一起而不是单独地验证所有内容。
– prograhammer
2015年8月3日在16:41
#6 楼
简单的Transfer对象(或Parameter对象)的唯一目的可能是保留某些字段并按需提供其值。但是,即使在这种退化的情况下,也可能会争辩说该对象应该是不可变的-在构造函数中配置并仅公开
get
...方法。控制旋钮”;您的车载收音机的UI可以理解为暴露getVolume
,setVolume
,getChannel
和setChannel
之类的东西,但是其真正的功能是接收信号并发出声音。但是这些旋钮并没有太多实现细节。您从这些接口功能中不知道无线电是晶体管,主要是软件还是真空管。 ,您会要求它做更多的事情,而不是要求它告诉您其内部状态或询问它的数据,以便其他代码可以使用这些值做某事。 >那么……“邪恶”?并不是的。但是每次您倾向于输入一个值并针对该值公开get
...和set
...方法时,都要问自己为什么以及该对象的真正责任是什么。如果您能给自己的唯一答案是“为我保持这一价值”,那么也许除了OO之外,还有其他事情正在发生。#7 楼
如果Game类公开了许多变量,则它可能遵循God对象反模式。 getter和setter没什么错(尽管它们在Java中的冗长有些令人讨厌);在设计良好的应用程序中,每个类都具有明确分开的功能,您将不需要在单个类中包含许多功能。游戏类(我理解您的评论是这样的),那么您可能不需要getter(对于一个类而言,无需使用get方法即可访问其自己的私有变量就很好了),并且您可能会折叠许多setter放入“组设置器”中,该设置器设置了几个在概念上属于在一起的变量。评论
问题在于,尽管该游戏有多种设置,但它们之间的差异很小,通常仅需要在姊妹类中应用几个额外的方法来提供不同的值。
– Mike B
09年2月19日在12:48
谢谢回复。为了回应您的编辑,如果我从每个姊妹班级中调用两个设置器,那么将主Game类中的两个设置器方法都删除,然后将其转换为一组设置器,将是一种很好的OO做法?
– Mike B
09年2月19日在13:05
的确如此-如果这些值始终设置在一起,则最好只设置一个setter,以使这些信息一致。如果有两个以上的类,或者如果有一些仅对它们进行操作并且也可以放入该类的方法,则最好将它们放在一起。
–迈克尔·伯格沃德(Michael Borgwardt)
09年2月19日在13:11
#8 楼
getter和setter方法的存在往往表明(如果您使用的是这种小学语言,则是“气味”),这是一个设计问题。琐碎的获取器和设置器与公共领域几乎没有区别。通常,对数据进行操作的代码将位于不同的类中–封装不完善,并且您希望程序员对OO感到不满意。但是通常,同时具有getter和setter的类型会指示设计问题。吸气剂的工作是不可变性的。设置员为“告诉不问”工作。不变性和“不要问”都是不错的设计选择,只要它们不能以重叠的方式应用即可。评论
普通的获取器/设置器对于非私有领域具有显着的优势-以后可以更改其实现,而不会影响调用者。这允许在需要时进行受控的访问/更改。
–斯科特·斯坦奇菲尔德
09年2月19日在13:09
通常,更改其实现实际上没有任何意义。
– Tom Hawtin-大头钉
09年2月19日在13:30
#9 楼
我的观点是,getter和setter是好的程序的要求。坚持下去,但不要写不必要的getter / setter-不一定总是直接处理所有变量。#10 楼
我真的不认为他们是邪恶的。但是我很想生活在一个除非真正需要就不必使用它们的世界。例如:public void setValue(int value)
{
this.value = value;
}
然后,需求发生变化,您需要跟踪设置值的次数。 />
public void setValue(int value)
{
this.value = value;
count++;
}
这很漂亮。我知道了。但是,在Ruby中,以下内容不能达到相同的目的吗?那么,您不能只覆盖设置器THEN而是仅覆盖THEN吗?我们拥有Java类,看到成千上万的代码行虽然没什么,但是基本的getter / setters却很丑陋和令人讨厌。时间,并且仅在需要时才进行自定义获取/设置程序。像魅力一样工作,它丝毫无损。
评论
如果您使用Lombok,则在Java中可以有相同的东西。 IntelliJ,Eclipse和Emacs对lombok具有IDE支持。
– HaakonLøtveit
16年2月10日在9:40
我认为压倒二传手甚至更邪恶。 API的用户认为,setter只能更改一个变量-而不做复杂的事情。 “正确”的方法是以一种后续更改不会破坏用户期望的方式来重命名设置器。
– CoronA
16年6月6日在4:29
#11 楼
一如既往,唯一的答案是:取决于情况。如果您是唯一接触代码的人,那么您可以做任何自己喜欢的事情,包括使用快捷方式。使用setter的好处之一是只需要在一个位置执行检查在您的代码中。
您可能想更加注意这些方法实际获取和设置的内容。如果使用它们提供对常量值的访问,则最好使用常量。
#12 楼
这取决于所讨论的编程语言。您的问题是在Java上下文中提出的,在该上下文中,通常将getter和setter视为一件好事。相反,在Python世界中,它们通常被认为是不良样式:它们在代码中添加行而没有实际添加功能。当Python程序员需要时,他们可以使用元编程来捕获和/或设置对象属性。
在Java(至少是我十年前学到的Java版本)中,这是不可能的。因此,在Java中,通常最好认真使用getter和setter,以便在需要时可以覆盖对变量的访问。
(这并不一定会使Python优于Java,只是不同。)
#13 楼
仅供参考:除了该主题中的所有出色答案之外,请记住,出于各种原因,您可以提出支持或反对吸气剂/设置员的方法,性能并不是其中之一(有些人可能会相信)。 JVM足够聪明,可以内联琐碎的getter / setter(即使不是final
,只要它们没有被实际覆盖)。#14 楼
您可能需要用值类替换某些类。这将允许您删除吸气剂,并避免在内容从下面更改时发生问题。#15 楼
如果需要外部访问各个字段的值,请使用getter和/或setter。如果没有,那就不要。切勿使用公共领域。就这么简单! (好吧,这从来没有那么简单,但是这是一个很好的经验法则。)通常,您还应该发现提供给setter的频率比获得getter的频率要低得多-特别是在尝试时使您的对象不可变-这是一件好事(但并非总是最好的选择)-即使不是。
#16 楼
我几个月前一直在用Java进行编程,并且我了解到,只有在应用程序需要它时,才应该使用getter和setters。评论
如果您的对象发生更改,以便一个字段需要一个吸气剂和一个塞特气,该怎么办?如果在100个不同的地方使用了该字段,则需要进行100个不同的更改。如果从一开始就使用getter和setter,则只需要在一个地方更改它即可更改对象。
–比尔蜥蜴
09年2月19日在13:45
如果需要将该变量公开到100个不同的位置,请使用getter和setter。但是,让100个不同的位置处理对象的内部数据真的是一种很好的面向对象的做法吗?
–特里·威尔科克斯(Terry Wilcox)
09年2月19日在17:26
@Terry:该对象可以在100个不同的项目中使用,而不仅仅是在一个项目中的100个不同位置使用。
–比尔蜥蜴
09年2月19日在17:45
...此外,我应该说的是“类”而不是“对象”。
–比尔蜥蜴
09年2月19日在17:48
评论
另请参见stackoverflow.com/questions/996179