使用getter和setter的优点是什么 - 只能获取和设置 - 而不是简单地为这些变量使用公共字段?
如果getter和setter做的不仅仅是简单的获取/设置,我可以非常快速地解决这个问题,但我不是100%明确如何:
public String foo;
更糟糕的是:
private String foo;
public void setFoo(String foo) { this.foo = foo; }
public String getFoo() { return foo; }
前者需要更少的样板代码。
答案 0 :(得分:875)
实际上有很多好的理由考虑使用访问器而不是直接暴露类的字段 - 除了封装的参数以及使未来的更改更容易。
以下是我所知道的一些原因:
答案 1 :(得分:429)
因为从现在起2周(几个月,几年),当您意识到您的二传手需要更多而不仅仅是设置值时,您还会意识到该属性已被直接用于238其他课程: - )
答案 2 :(得分:334)
公共字段并不比除了返回字段并分配给它之外什么都不做的getter / setter对更差。首先,很明显(在大多数语言中)没有功能差异。任何差异必须与其他因素有关,如可维护性或可读性。
getter / setter对经常提到的优点不是。有这样的说法,您可以更改实施,您的客户不必重新编译。据推测,setters允许您稍后添加验证等功能,而您的客户甚至不需要了解它。但是,向setter添加验证是对其前提条件的改变,违反了之前的合同,这很简单,“你可以把任何东西放在这里,你可以在以后得到同样的东西来自吸气器“。
所以,既然您违反了合同,那么更改代码库中的每个文件都是您应该做的事情,而不是避免。如果你避免它,你就会假设所有代码都假设这些方法的合同是不同的。
如果那不应该是合同,那么接口允许客户端将对象置于无效状态。 这与封装完全相反如果该字段从一开始就无法真正设置为任何东西,为什么从一开始就没有验证呢?
这个相同的论点适用于这些传递的getter / setter对的其他假设的优点:如果你以后决定更改设置的值,那么你就违反了合同。如果您覆盖派生类中的默认功能,超出了一些无害的修改(如日志记录或其他不可观察的行为),那么您就违反了基类的约定。这违反了Liskov Substitutability Principle,这被视为OO的原则之一。
如果一个类对每个字段都有这些愚蠢的getter和setter,那么它就是一个没有任何不变量的类,没有契约。这真的是面向对象的设计吗?如果所有课程都是那些吸气者和制定者,那么它只是一个愚蠢的数据持有者,而愚蠢的数据持有者应该看起来像愚蠢的数据持有者:
class Foo {
public:
int DaysLeft;
int ContestantNumber;
};
向这样的类添加传递getter / setter对不会增加任何值。其他类应该提供有意义的操作,而不仅仅是已经提供的字段的操作。这就是你如何定义和维护有用的不变量。
客户:“我该怎么办这个班级的对象?” 设计师:“您可以读写几个变量。”
客户:“哦......很酷,我猜?”
有理由使用getter和setter,但如果这些原因不存在,那么以false封装之神的名义制作getter / setter对并不是一件好事。制作getter或setter的有效理由包括经常提到的事情,因为您可以在以后做出可能的更改,例如验证或不同的内部表示。或者值可能是客户端可读但不可写(例如,读取字典的大小),因此简单的getter是一个不错的选择。但是当你做出选择时,这些原因应该存在,而不仅仅是你以后想要的潜在事物。这是YAGNI的一个实例(你不需要它)。
答案 3 :(得分:84)
很多人都在谈论吸气剂和制定者的优势,但我想扮演魔鬼的拥护者。现在我正在调试一个非常大的程序,程序员决定让所有程序都得到吸气剂和制定者。这看起来不错,但它是一个逆向工程的噩梦。
假设您正在查看数百行代码,并且您遇到了这个问题:
person.name = "Joe";
这是一段非常简单的代码,直到你意识到它是一个二传手。现在,你跟着那个setter,发现它还设置了person.firstName,person.lastName,person.isHuman,person.hasReallyCommonFirstName,并调用了person.update(),它将查询发送到数据库等等。哦,那是发生内存泄漏的地方。
乍看之下理解本地代码是一个重要的特性,具有良好的可读性,吸气剂和制定者往往会破坏。这就是为什么我尽力避免使用它们,并尽量减少使用它们时所做的事情。
答案 4 :(得分:49)
原因很多。我最喜欢的是当你需要改变行为或规范你可以在变量上设置的内容时。例如,假设您有一个setSpeed(int speed)方法。但是你希望你只能设置100的最大速度。你会做类似的事情:
public void setSpeed(int speed) {
if ( speed > 100 ) {
this.speed = 100;
} else {
this.speed = speed;
}
}
现在如果您的代码中使用公共字段,然后您意识到您需要上述要求,该怎么办?尽情享受公共领域的每一次使用,而不仅仅是修改你的二传手。
我的2美分:)
答案 5 :(得分:48)
在纯粹面向对象的世界中,getter和setter是一个可怕的反模式。阅读这篇文章:Getters/Setters. Evil. Period。简而言之,它们鼓励程序员将对象视为数据结构,这种思维方式纯粹是程序性的(如COBOL或C)。在面向对象的语言中,没有数据结构,只有暴露行为的对象(不是属性/属性!)
您可以在Elegant Objects的第3.5节(我关于面向对象编程的书)中找到更多关于它们的信息。
答案 6 :(得分:36)
访问器和更改器的一个优点是您可以执行验证。
例如,如果foo
是公开的,我可以轻松地将其设置为null
,然后其他人可以尝试在对象上调用方法。但它不再存在了!使用setFoo
方法,我可以确保foo
永远不会设置为null
。
访问器和更改器也允许封装 - 如果你不应该在它的集合中看到它的值(可能它在构造函数中设置然后由方法使用,但从不应该被更改),它将永远不会被看到任何人。但是,如果您可以允许其他类查看或更改它,您可以提供适当的访问者和/或更改者。
答案 7 :(得分:28)
取决于您的语言。你已经标记了这个“面向对象”而不是“Java”,所以我想指出ChssPly76的答案是依赖于语言的。例如,在Python中,没有理由使用getter和setter。如果需要更改行为,可以使用属性,该属性围绕基本属性访问包装getter和setter。像这样:
class Simple(object):
def _get_value(self):
return self._value -1
def _set_value(self, new_value):
self._value = new_value + 1
def _del_value(self):
self.old_values.append(self._value)
del self._value
value = property(_get_value, _set_value, _del_value)
答案 8 :(得分:24)
我只想补充一点,即使有时它们对于变量/对象的封装和安全性是必要的,如果我们想要编写真正的面向对象程序,那么我们需要 STOP OVERUSING THE ACCESSORS ,因为有时我们非常依赖它们而不是真的有必要,而且几乎和我们把变量公开一样。
答案 9 :(得分:22)
我知道有点晚了,但我认为有些人对表现很感兴趣。
我做了一点性能测试。我写了一个班" NumberHolder"哪个,持有一个整数。您可以使用getter方法读取该Integer
anInstance.getNumber()
或使用anInstance.number
直接访问该号码。我的程序通过两种方式读取数字1,000,000,000次。该过程重复五次并打印时间。我得到了以下结果:
Time 1: 953ms, Time 2: 741ms
Time 1: 655ms, Time 2: 743ms
Time 1: 656ms, Time 2: 634ms
Time 1: 637ms, Time 2: 629ms
Time 1: 633ms, Time 2: 625ms
(时间1是直接的方式,时间2是吸气剂)
你知道,吸气剂(几乎)总是快一点。然后我尝试了不同数量的周期。而不是100万,我使用了1000万和10万。 结果:
1000万次循环:
Time 1: 6382ms, Time 2: 6351ms
Time 1: 6363ms, Time 2: 6351ms
Time 1: 6350ms, Time 2: 6363ms
Time 1: 6353ms, Time 2: 6357ms
Time 1: 6348ms, Time 2: 6354ms
1000万次循环,时间几乎相同。 这里有10万(10万)次循环:
Time 1: 77ms, Time 2: 73ms
Time 1: 94ms, Time 2: 65ms
Time 1: 67ms, Time 2: 63ms
Time 1: 65ms, Time 2: 65ms
Time 1: 66ms, Time 2: 63ms
对于不同的循环次数,吸气剂比常规方式快一点。我希望这能帮助你。
答案 10 :(得分:22)
谢谢,这真的澄清了我的想法。现在这里(差不多)10个(差不多)好的理由不使用getter和setter:
我刚离开的最后三个(N / A或D / C)......
答案 11 :(得分:16)
除非当前交付需要,否则不要使用吸气剂设定器。如果要在大多数生产应用程序系统中更改其更改请求,请不要过多考虑将来会发生什么。
简单易懂,在需要时增加复杂性。
我不会利用对深度技术知识的企业主的无知,因为我认为这是正确的,或者我喜欢这种方法。
我有大量系统编写,没有getter setter,只有访问修饰符和一些方法来验证n执行biz逻辑。如果你绝对需要的话。使用任何东西。
答案 12 :(得分:15)
它对于延迟加载很有用。假设有问题的对象存储在数据库中,除非您需要,否则您不希望得到它。如果对象是由getter检索的,那么内部对象可以为null,直到有人要求它为止,然后你可以在第一次调用getter时获取它。
我在一个项目中有一个基页类,它从一些不同的Web服务调用中加载了一些数据,但是这些Web服务调用中的数据并不总是在所有子页面中使用。 Web服务,为了所有的好处,开创了“慢”的新定义,因此如果您不需要,您不希望进行Web服务调用。
我从公共字段移动到getter,现在getter检查缓存,如果没有,则调用Web服务。因此,通过一点包装,可以防止大量的Web服务调用。
因此,getter使我无法在每个子页面上弄清楚我需要什么。如果我需要它,我会打电话给吸气器,如果我还没有它,就会找到它。
protected YourType _yourName = null;
public YourType YourName{
get
{
if (_yourName == null)
{
_yourName = new YourType();
return _yourName;
}
}
}
答案 13 :(得分:15)
我花了很长时间来考虑Java案例,我相信真正的原因是:
换句话说,您可以在界面中指定字段的唯一方法是提供用于编写新值的方法和用于读取当前值的方法。
这些方法是臭名昭着的吸气者和制定者......
答案 14 :(得分:14)
Getter和setter方法是访问私有类成员的公共接口。
封装口头禅是将字段设为私有,方法是公共的。
获取方法: 我们可以访问私有变量。
Setter方法: 我们可以修改私有字段。
即使getter和setter方法没有添加新功能,我们也可以改变主意,稍后再回来制作该方法
可以使用值的任何地方,可以添加返回该值的方法。而不是:
int x = 1000 - 500
使用
int x = 1000 - class_name.getValue();
假设我们需要存储此Person
的详细信息。此Person
包含字段name
,age
和sex
。这样做涉及为name
,age
和sex
创建方法。现在,如果我们需要创建另一个人,则有必要重新创建name
,age
,sex
的方法。
我们可以使用getter和setter方法创建一个bean class(Person)
,而不是这样做。所以,明天我们只需要添加一个新人就可以创建这个Bean class(Person class)
的对象(参见图)。因此,我们重用bean类的字段和方法,这要好得多。
答案 15 :(得分:12)
到目前为止我在答案中遗漏的一个方面是访问规范:
答案 16 :(得分:10)
在不支持“属性”(C ++,Java)或需要在将字段更改为属性(C#)时重新编译客户端的语言中,使用get / set方法更容易修改。例如,将验证逻辑添加到setFoo方法不需要更改类的公共接口。
在支持“真实”属性的语言(Python,Ruby,也许是Smalltalk?)中,没有必要获取/设置方法。
答案 17 :(得分:9)
两个主要的是多态性和验证。即使它只是一个愚蠢的数据结构。
我们说我们有这个简单的课程:
public class Bottle {
public int amountOfWaterMl;
public int capacityMl;
}
一个非常简单的类,它包含多少液体,以及它的容量(以毫升为单位)。
当我这样做时会发生什么:
Bottle bot = new Bottle();
bot.amountOfWaterMl = 1500;
bot.capacityMl = 1000;
嗯,你不会期望这样做,对吧? 你希望在那里进行某种形式的理智检查。更糟糕的是,如果我从未指定最大容量怎么办?哦亲爱的,我们有问题。
但也存在另一个问题。如果瓶子只是一种容器怎么办?如果我们有几个容器,所有容器和液体的填充量怎么办?如果我们可以创建一个界面,我们可以让我们程序的其余部分接受该界面,瓶子,塑料桶和各种东西都可以互换。那不是更好吗?由于接口需要方法,这也是一件好事。
我们最终得到的结果是:
public interface LiquidContainer {
public int getAmountMl();
public void setAmountMl(int amountMl);
public int getCapacityMl();
public void setCapcityMl(int capacityMl);
}
大!现在我们只需将Bottle更改为:
public class Bottle extends LiquidContainer {
private int capacityMl;
private int amountFilledMl;
public Bottle(int capacityMl, int amountFilledMl) {
this.capacityMl = capacityMl;
this.amountFilledMl = amountFilledMl;
checkNotOverFlow();
}
public int getAmountMl() {
return amountFilledMl;
}
public void setAmountMl(int amountMl) {
this.amountFilled = amountMl;
checkNotOverFlow();
}
public int getCapacityMl() {
return capacityMl;
public void setCapcityMl(int capacityMl) {
this.capacityMl = capacityMl;
checkNotOverFlow();
}
private void checkNotOverFlow() {
if(amountOfWaterMl > capacityMl) {
throw new BottleOverflowException();
}
}
我将把BottleOverflowException的定义作为练习留给读者。
现在注意这是多么强大。我们现在可以通过接受LiquidContainer而不是Bottle来处理代码中的任何类型的容器。这些瓶子如何处理这些东西都可能有所不同。你可以让瓶子在更改时将状态写入磁盘,或者保存在SQL数据库或GNU上的瓶子知道还有什么。
所有这些都有不同的方式来处理各种各样的事情。 Bottle只是检查它是否溢出它会抛出RuntimeException。但这可能是错误的做法。 (关于错误处理有一个很有用的讨论,但是我在这里有目的地保持它非常简单。评论中的人可能会指出这种简单方法的缺陷。;))
是的,似乎我们从一个非常简单的想法转变为快速获得更好的答案。
还有第三件事并不是每个人都能解决的问题:getter和setter使用方法调用。这意味着它们看起来像其他地方的普通方法。而不是为DTO和东西提供奇怪的特定语法,你在任何地方都有同样的东西。
答案 18 :(得分:6)
OO设计的基本原则之一:封装!
它为您提供了许多好处,其中一个好处是您可以在幕后更改getter / setter的实现,但只要数据类型保持不变,任何具有该值的消费者都将继续工作。
答案 19 :(得分:4)
从面向对象设计的角度来看,两种替代方案都可能通过削弱类的封装来破坏代码的维护。有关讨论,您可以查看这篇优秀的文章:http://typicalprogrammer.com/?p=23
答案 20 :(得分:4)
您应该在以下情况下使用getter和setter:
所以这很少是一般的OO问题;这是一个特定于语言的问题,针对不同的语言(以及不同的用例)有不同的答案。
从OO理论的角度来看,吸气剂和制定者是无用的。你的班级的界面就是它的作用,而不是它的状态。 (如果没有,你写错了类。)在非常简单的情况下,类所做的只是,例如,表示直角坐标中的一个点,*属性是接口的一部分;吸气剂和孵化器只是云。但是在任何非常简单的情况下,属性,getter和setter都不是接口的一部分。
换句话说:如果您认为您班级的消费者甚至不知道您拥有spam
属性,那么可以更不用说改变它,然后给他们{{1}方法是你想要做的最后一件事。
*即使对于这个简单的类,您也可能不一定要允许设置set_spam
和x
值。如果这真的是一个类,它不应该有像y
,translate
等方法吗?如果它只是一个类,因为你的语言没有记录/结构/命名元组,那么这不是OO的问题......
但是没有人做过一般的OO设计。他们用特定的语言进行设计和实施。在某些语言中,吸气剂和制定者远非无用。
如果您的语言没有属性,那么表示概念上属性但实际计算或验证的东西的唯一方法是通过getter和setter。
即使您的语言确实具有属性,也可能存在不足或不合适的情况。例如,如果要允许子类控制属性的语义,则在没有动态访问的语言中,子类不能将计算属性替换为属性。
至于“如果我想稍后改变我的实施怎么办?”问题(在OP的问题和接受的答案中以不同的措辞重复多次):如果它确实是纯粹的实现更改,并且您从属性开始,则可以将其更改为属性而不影响接口。当然,除非您的语言不支持。所以这又是同样的情况。
此外,遵循您正在使用的语言(或框架)的习语非常重要。如果你用C#编写漂亮的Ruby风格的代码,那么除了你以外的任何有经验的C#开发人员都会在阅读它时遇到麻烦,这很糟糕。有些语言围绕其惯例拥有比其他语言更强大的文化.- Java和Python可能并不是巧合,而Java和Python恰好具有两种最强大的文化。
除了人类读者之外,还有一些图书馆和工具可以让您遵守惯例,如果不这样做,会让您的生活更加艰难。挂钩Interface Builder小部件除了ObjC属性之外的任何东西,或者使用某些没有getter的Java模拟库,只会让你的生活变得更加困难。如果这些工具对您很重要,请不要与它们作斗争。
答案 21 :(得分:3)
Getter和setter方法是访问器方法,这意味着它们通常是用于更改私有类成员的公共接口。您使用getter和setter方法来定义属性。您可以将getter和setter方法作为类外的属性进行访问,即使您在类中将它们定义为方法也是如此。类外的那些属性可以与类中的属性名称具有不同的名称。
使用getter和setter方法有一些优点,例如能够让您创建具有可以像属性一样访问的复杂功能的成员。它们还允许您创建只读和只写属性。
即使getter和setter方法很有用,也应该注意不要过度使用它们,因为在某些情况下,它们会使代码维护更加困难。此外,它们还提供对类实现的访问,例如公共成员。 OOP练习不鼓励直接访问课堂内的属性。
当您编写类时,始终鼓励您尽可能多地将实例变量设为私有,并相应地添加getter和setter方法。这是因为有几次您可能不希望让用户更改类中的某些变量。例如,如果您有一个私有静态方法来跟踪为特定类创建的实例数,则您不希望用户使用代码修改该计数器。只有构造函数语句应该在调用时递增该变量。在这种情况下,您可以创建一个私有实例变量并仅为计数器变量提供getter方法,这意味着用户只能使用getter方法检索当前值,并且它们将无法设置新值使用setter方法。在没有setter的情况下创建一个getter是一种简单的方法,可以让你的类中的某些变量成为只读。
答案 22 :(得分:3)
代码进化。当{em>您需要数据成员保护时,private
非常适合。最终所有类都应该是一种“微型程序”,它具有明确定义的接口,你不能只是搞砸的内部。
那就是说,软件开发并不是要设置该类的最终版本,就像你在第一次尝试时按下一些铸铁雕像一样。当你使用它时,代码更像是粘土。 随着您的发展而不断发展,并了解您正在解决的问题域的更多信息。在开发期间,类可能会相互交互,而不是它们应该相互影响(您计划分解的依赖关系),合并在一起或拆分。因此,我认为辩论归结为不想虔诚地写作的人
int getVar() const { return var ; }
所以你有:
doSomething( obj->getVar() ) ;
而不是
doSomething( obj->var ) ;
getVar()
不仅视觉上有噪音,而且还会产生这样一种错觉:gettingVar()
在某种程度上是一个比实际更复杂的过程。你(作为班级作家)如何看待var
的神圣性对你班级的用户来说尤其令人困惑,如果它有一个通道设定者 - 那么看起来你正在建立这些大门以“保护”某些东西你坚持是有价值的,(var
的神圣性),但即使你承认var
的保护也不值得任何人进入的能力set
{{ 1}}无论他们想要什么样的价值,你甚至都不会偷看他们在做什么。
所以我编程如下(假设采用“敏捷”类型的方法 - 即当我编写的代码不知道完全它将要做什么/没有时间或经验来计划精心设计瀑布式接口集):
1)从所有公共成员开始,了解具有数据和行为的基本对象。这就是为什么在我的所有C ++“示例”代码中,您会注意到我在任何地方使用var
而不是struct
。
2)当一个对象的数据成员的内部行为变得足够复杂时(例如,它喜欢以某种顺序保持内部class
),就会写入访问器类型函数。因为我自己编程,所以我并不总是立即设置成员std::list
,但在课程的演变过程中,成员将被“提升”为private
或{{1 }}
3)完全充实并且对其内部有严格规定的类(即他们确切地知道他们正在做什么,而你不应该用他们的内部“操”(技术术语) )被赋予protected
名称,默认私人成员,并且只允许少数几个成员private
。
我发现这种方法可以让我避免坐在那里,并且在课堂进化的早期阶段,当许多数据成员迁出,转移等时,他们会虔诚地写下getter / setter。
答案 23 :(得分:3)
有一个很好的理由考虑使用访问器是没有属性继承。见下一个例子:
public class TestPropertyOverride {
public static class A {
public int i = 0;
public void add() {
i++;
}
public int getI() {
return i;
}
}
public static class B extends A {
public int i = 2;
@Override
public void add() {
i = i + 2;
}
@Override
public int getI() {
return i;
}
}
public static void main(String[] args) {
A a = new B();
System.out.println(a.i);
a.add();
System.out.println(a.i);
System.out.println(a.getI());
}
}
输出:
0
0
4
答案 24 :(得分:2)
Getters 和 setters 用于实现面向对象编程的两个基本方面:
假设我们有一个Employee类:
package com.highmark.productConfig.types;
public class Employee {
private String firstName;
private String middleName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getMiddleName() {
return middleName;
}
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFullName(){
return this.getFirstName() + this.getMiddleName() + this.getLastName();
}
}
此处,全名的实现细节对用户是隐藏的,并且与公共属性不同,不能直接向用户访问。
答案 25 :(得分:2)
在面向对象语言中,方法及其访问修饰符声明该对象的接口。在构造函数和访问器以及mutator方法之间,开发人员可以控制对对象内部状态的访问。如果简单地将变量声明为公共,则无法管理该访问。 当我们使用setter时,我们可以限制用户输入我们需要的输入。意味着该变量的馈送将通过适当的通道,并且通道由我们预定义。因此使用setter更安全。
答案 26 :(得分:2)
另一种用法(在支持属性的语言中)是setter和getter可能意味着操作非常重要。通常,您希望避免在属性中执行任何计算成本高昂的事情。
答案 27 :(得分:2)
getter / setter的一个相对现代的优点是可以更轻松地浏览标记(索引)代码编辑器中的代码。例如。如果要查看谁设置了成员,可以打开setter的调用层次结构。
另一方面,如果该成员是公开的,则这些工具不能过滤对该成员的读/写访问权限。所以,你必须跋涉这个成员的所有用途。
答案 28 :(得分:1)
我可以想到你不会只想把所有东西都公之于众的一个原因。
例如,即使是通过链变量访问(即object.item.origin.x)直接访问,也可以访问您从未打算在类之外使用的变量。
通过将大部分内容都设置为私有,并且只将您想要扩展的内容和可能在子类中引用为保护的内容,并且通常只将静态最终对象作为公共内容,那么您可以控制其他程序员和程序可以在API中使用的内容它可以访问什么以及它不能通过使用setter和getter来访问你想要程序的东西,或者实际上可能正在使用你的代码的其他程序员,可以在你的程序中进行修改。
答案 29 :(得分:1)
Getters和setter来自数据隐藏。数据隐藏意味着我们 隐藏来自外人的数据或外部人/物无法访问 我们的数据。这是OOP中的一个有用功能。
作为一个例子:
如果创建公共变量,则可以访问该变量并在任何位置(任何类)更改值。但是如果你创建为private,那么变量在除了声明的类之外的任何类中都看不到/访问。
function oddOrEven(){ var val = document.getElementById('number').value; var number = parseInt(val, 10); // if it's not a valid number, you'll have NaN here which is falsy if (number) { var lastDigit = val[val.length-1]; document.getElementById('demo').innerHTML = (parseInt(lastDigit, 10) % 2) ? "Even" : "Odd"; } }
和public
是访问修饰符。
那么我们如何在外面访问该变量:
这是来自 getters 和 setters 的地方。您可以将变量声明为私有,然后可以为该变量实现getter和setter。
实施例(爪哇):
private
<强>优势:强>
当有人想要访问或更改/设置值为private String name;
public String getName(){
return this.name;
}
public void setName(String name){
this.name= name;
}
变量时,他/她必须有权利。
balance
您也可以在构造函数中设置值,但稍后在您需要时设置 要更新/更改值,您必须实现setter方法。
答案 30 :(得分:1)
我想抛出注释的想法:@getter和@setter。使用@getter,您应该能够obj = class.field但不能使用class.field = obj。使用@setter,反之亦然。使用@getter和@setter,你应该能够做到这两点。这将通过不在运行时调用简单的方法来保留封装并减少时间。
答案 31 :(得分:1)
DataStructure 和 Object 之间存在差异。
数据结构应该暴露其内部结构而不是行为。
一个物体不应该暴露它的内脏,而应该暴露它的行为,这也被称为得墨忒耳法则
大多数 DTO 被认为更像是一种数据结构,而不是对象。他们应该只公开他们的数据而不是行为。在 DataStructure 中使用 Setter/Getter 将公开行为而不是其中的数据。这进一步增加了违反迪米特法则的可能性。
鲍勃叔叔在他的《清洁代码》一书中解释了得墨忒耳定律。
<块引用>有一个著名的启发式方法叫做得墨忒耳定律,它说 模块不应该知道它的对象的内部 操纵。正如我们在上一节中看到的,对象隐藏了它们的数据 并公开操作。这意味着一个对象不应该暴露它的 通过访问器的内部结构,因为这样做是为了暴露, 而不是隐藏它的内部结构。
更准确地说,得墨忒耳定律说,C 类的方法 f 应该只调用这些方法:
该方法不应调用由任何允许的函数返回的对象的方法。 换句话说,与朋友交谈,而不是与陌生人交谈。
因此,根据此,LoD 违规示例是:
final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
这里,函数应该调用它的直接朋友的方法,这里是ctxt,它不应该调用它的直接朋友的朋友的方法。但这条规则不适用于数据结构。所以这里如果 ctxt、option、scratchDir 是数据结构,那么为什么要用一些行为来包装它们的内部数据并违反 LoD。
相反,我们可以这样做。
final String outputDir = ctxt.options.scratchDir.absolutePath;
这满足了我们的需求,甚至不违反 LoD。
灵感来自 Robert C. Martin(鲍勃叔叔)的简洁代码
答案 32 :(得分:1)
此外,这是为了“面向未来”的课程。特别是,从一个字段更改为一个属性是一个ABI中断,所以如果你以后确定你需要更多的逻辑而不仅仅是“设置/获取字段”,那么你需要打破ABI,这当然会产生任何问题其他已经针对你的班级编译。
答案 33 :(得分:0)
根据我的经验,理想的情况是将变量设置为私有,并为每个变量提供访问器和修饰符。
这样,您可以根据需要创建只读变量或只写变量。
以下实现显示了只写变量。
private String foo;
public void setFoo(String foo) { this.foo = foo; }
private String getFoo() { return foo; }
下面显示了一个只读变量。
private String foo;
private void setFoo(String foo) { this.foo = foo; }
public String getFoo() { return foo; }
答案 34 :(得分:0)
如果您不需要任何验证,甚至不需要维护状态,即一个属性依赖于另一个属性,那么我们需要在更改时维护状态。您可以通过公开字段而不使用 getter 和 setter 来保持简单。
我认为随着程序的增长,OOP 会使事情变得复杂,它成为开发人员扩展的噩梦。
一个简单的例子;我们从 xml 生成 c++ 头文件。标头包含不需要任何验证的简单字段。但仍然像在 OOPS 访问器中一样,我们按如下方式生成它们。
const Filed& getfield() const
Field& getField()
void setfield(const Field& field){...}
这是非常冗长的,不是必需的。一个简单的
struct
{
Field field;
};
足够并且可读。 函数式编程没有数据隐藏的概念,它们甚至不需要它,因为它们不会改变数据。
答案 35 :(得分:0)
尽管getter和setter不常见,但这些方法的使用也可用于AOP /代理模式使用。 例如,对于审计变量,您可以使用AOP来审计任何值的更新。 没有getter / setter,除了在任何地方更改代码之外,它是不可能的。 Personaly我从未使用AOP,但它显示了使用getter / setter的另一个优点。
答案 36 :(得分:0)
getter / setter的一个相对现代的优点是可以更轻松地浏览标记(索引)代码编辑器中的代码。例如。如果要查看谁设置了成员,可以打开setter的调用层次结构。
另一方面,如果该成员是公共的,则这些工具无法过滤对该成员的读/写访问权限。所以,你必须跋涉这个成员的所有用途。
答案 37 :(得分:0)
如果您想要一个只读变量但不希望客户端必须更改它们访问它的方式,请尝试这个模板化的类:
template<typename MemberOfWhichClass, typename primative>
class ReadOnly {
friend MemberOfWhichClass;
public:
template<typename number> inline bool operator==(const number& y) const { return x == y; }
template<typename number> inline number operator+ (const number& y) const { return x + y; }
template<typename number> inline number operator- (const number& y) const { return x - y; }
template<typename number> inline number operator* (const number& y) const { return x * y; }
template<typename number> inline number operator/ (const number& y) const { return x / y; }
template<typename number> inline number operator<<(const number& y) const { return x << y; }
template<typename number> inline number operator^(const number& y) const { return x^y; }
template<typename number> inline number operator~() const { return ~x; }
template<typename number> inline operator number() const { return x; }
protected:
template<typename number> inline number operator= (const number& y) { return x = y; }
template<typename number> inline number operator+=(const number& y) { return x += y; }
template<typename number> inline number operator-=(const number& y) { return x -= y; }
template<typename number> inline number operator*=(const number& y) { return x *= y; }
template<typename number> inline number operator/=(const number& y) { return x /= y; }
primative x;
};
使用示例:
class Foo {
public:
ReadOnly<Foo, int> cantChangeMe;
};
请记住,您还需要添加按位和一元运算符!这只是为了让你开始
答案 38 :(得分:0)
我会让代码说明一切:
Mesh mesh = new Mesh();
BoundingVolume vol = new BoundingVolume();
mesh.boundingVolume = vol;
vol.mesh = mesh;
vol.compute();
你喜欢它吗?以下是制定者:
Mesh mesh = new Mesh();
BoundingVolume vol = new BoundingVolume();
mesh.setBoundingVolume(vol);
答案 39 :(得分:0)
我想发布一个我刚刚完成的真实世界的例子:
后台 - 我使用hibernate工具为我的数据库生成映射,这是我开发时正在改变的数据库。我更改了数据库模式,推送更改然后运行hibernate工具来生成java代码。在我想要向这些映射实体添加方法之前,一切都很好。如果我修改生成的文件,每次我对数据库进行更改时都会覆盖它们。所以我像这样扩展生成的类:
package com.foo.entities.custom
class User extends com.foo.entities.User{
public Integer getSomething(){
return super.getSomething();
}
public void setSomething(Integer something){
something+=1;
super.setSomething(something);
}
}
我上面所做的是使用我的新功能(某些东西+ 1)覆盖超类上的现有方法,而不会触及基类。同样的情况,如果你在一年前写了一个类,并希望在不更改基类(测试噩梦)的情况下转到版本2。希望有所帮助。
答案 40 :(得分:-2)
这是一个很好的问题,有更好的答案。由于它们很多,我会在那里再多一点 这个例子基于C#,只是一段有用的代码。已经解释了括号中的验证数据。
public class foo
{
public int f1 { get; set; } // A classic GS
public int f2 { get; private set; } // A GS with public read access, the write is only on the private level
public int f3 { private get; set; } // A GS where "You can set, but you can't get" outside the class
public int f4 { get; set; } = 10; // A GS with default value, this is a NEW feature of C# 6.0 / .NET 4.6
}