封装。精心设计的班级
今天我读了一本书,作者写道,在设计良好的类中,访问属性的唯一方法是通过该类方法之一。这是一个被广泛接受的想法吗?为什么封装属性如此重要?如果不这样做可能会产生什么后果?我之前在某处读到这可以提高安全性或类似的东西。任何 PHP 或 Java 中的示例都会非常有帮助。
Today I read a book and the author wrote that in a well-designed class the only way to access attributes is through one of that class methods. Is it a widely accepted thought? Why is it so important to encapsulate the attributes? What could be the consequences of not doing it? I read somewhere earlier that this improves security or something like that. Any example in PHP or Java would be very helpful.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
关于如何实现“良好的 OOD”的观点五花八门,而且经验丰富的程序员和设计师往往对设计选择和理念存在分歧。如果你问不同语言背景和范式的人,这可能会引发一场激烈的争论。
是的,理论上理论和实践是相同的,所以语言的选择不应该对高层设计产生太大影响。但实际上它们确实如此,因此好事和坏事都会发生。
让我添加一下:
这取决于。封装(使用支持语言)使您可以控制类的使用方式,因此您可以告诉人们:这是 API,您必须使用它。在其他语言(例如 python)中,官方 API 和非正式(可能会更改)接口之间的区别仅在于命名约定(毕竟,我们都是同意的成年人)
封装不是一项安全功能。
Opinions on how "good OOD" is achieved are dime a dozen, and also very experienced programmers and designers tend to disagree about design choices and philosophies. This could be a flame-war starter, if you ask people across a wide varieties of language background and paradigms.
And yes, in theory are theory and practice the same, so language choice shouldn't influence high level design very much. But in practice they do, and good and bad things happen because of that.
Let me add this:
It depends. Encapsulation (in a supporting language) gives you some control over how you classes are used, so you can tell people: this is the API, and you have to use this. In other languages (e.g. python) the difference between official API and informal (subject to change) interfaces is by naming convention only (after all, we're all consenting adults here)
Encapsulation is not a security feature.
考虑
使用访问器进行封装的另一个想法也可以在未来提供更好的可维护性。在上面 Feanor 的回答中,它可以很好地执行安全检查(假设您的 instvar 是私有的),但它可以有更进一步的好处。
考虑以下场景:
1) 您完成您的应用程序,并将其分发给一组用户(内部、外部等)。
2) BigCustomerA 联系您的团队并希望将审核跟踪添加到产品中。
如果每个人都在代码中使用访问器方法,那么实现起来几乎微不足道。像这样:
MyAPI 版本 1.0
MyAPI V1.1(现在带有审计跟踪)
API 的现有用户无需更改其代码,新功能(审计跟踪)现已推出。
如果不使用访问器进行封装,您将面临巨大的迁移工作。
第一次编码时,看起来工作量很大。键入:
class.varName = Something
与class.setVarName(something);
的速度要快得多,但如果每个人都采取简单的方法,那么从 BigCustomerA 的功能请求中获得报酬将是一个巨大的努力。Another thought to ponder
Encapsulation with accessors also provides much better maintainability in the future. In Feanor's answer above, it works great to enforce security checks (assuming your instvar is private), but it can have much further reaching benifits.
Consider the following scenario:
1) you complete your application, and distribute it to some set of users (internal, external, whatever).
2) BigCustomerA approaches your team and wants an audit trail added to the product.
If everyone is using the accessor methods in their code, this becomes almost trivial to implement. Something like so:
MyAPI Version 1.0
MyAPI V1.1 (now with audit trails)
Existing users of the API make no changes to their code and the new feature (audit trail) is now available.
Without encapsulation using accessors your faced with a huge migration effort.
When coding for the first time, it will seem like a lot of work. Its much faster to type:
class.varName = something
vsclass.setVarName(something);
but if everyone took the easy way out, getting paid for BigCustomerA's feature request would be a huge effort.在面向对象编程中,有一个被称为(http://en.wikipedia.org/wiki/Open/filled_principle)的原则:
概念验证--> 开放和封闭原则。这一原则仍然适用于:良好的类设计应该对可扩展性(继承)开放,但对内部成员的修改(封装)关闭。这意味着您无法在不关心对象的情况下修改它的状态。
因此,新语言仅通过属性(C++ 或 Java 中的 getters 和 setters 方法)修改内部变量(字段)。在 C# 中,属性编译为 MSIL 中的方法。
C#:
C++/Java:
In Object Oriente Programming there is a principle that is known as (http://en.wikipedia.org/wiki/Open/closed_principle):
POC --> Principle of Open and Closed. This principle stays for: a well class design should be opened for extensibility (inheritance) but closed for modification of internal members (encapsulation). It means that you could not be able to modify the state of an object without taking care about it.
So, new languages only modify internal variables (fields) through properties (getters and setters methods in C++ or Java). In C# properties compile to methods in MSIL.
C#:
C++/Java:
借鉴这些想法(来自 Head First C#):
Take theses ideas (from Head First C#):
在面向对象的世界中,是的。
对象旨在成为包含数据和行为的内聚实体,其他对象可以通过公共接口以受控方式访问这些数据和行为。如果一个类不封装其数据和行为,它就不再能够控制所访问的数据,并且无法履行其与公共接口隐含的其他对象的契约。
这样做的一个大问题是,如果一个类必须在内部更改,则公共接口不应该更改。这样它就不会破坏任何代码,并且其他类可以像以前一样继续使用它。
这是一个 Java 示例:
这里的问题是,因为我没有将
importantValue
封装为private
而不是公开,所以任何人都可以绕过我放入的检查setter 来防止对象处于无效状态。importantValue
永远不应该小于 0,但由于缺乏封装性,无法阻止这种情况发生。In the object-oriented world, yes.
Objects are intended to be cohesive entities containing data and behavior that other objects can access in a controlled way through a public interface. If an class does not encapsulate its data and behavior, it no longer has control over the data being accessed and cannot fulfill its contracts with other objects implied by the public interface.
One of the big problems with this is that if a class has to change internally, the public interface shouldn't have to change. That way it doesn't break any code and other classes can continue using it as before.
Here's a Java example:
The problem here is that because I haven't encapsulated
importantValue
by making itprivate
rather than public, anyone can come along and circumvent the check I put in the setter to prevent the object from having an invalid state.importantValue
should never be less than 0, but the lack of encapsulation makes it impossible to prevent it from being so.封装背后的整个思想是,与类(其接口除外)相关的所有知识都位于类本身内。例如,允许直接访问属性会带来确保任何分配在执行分配的代码上有效的责任。如果有效内容的定义发生变化,您必须使用该类仔细检查并审核所有内容,以确保它们符合要求。将规则封装在“setter”方法中意味着您只需在一处更改它,并且任何尝试任何有趣的事情的调用者都可以得到抛出异常作为回报。当属性更改时,您可能还想做很多其他事情,而 setter 就是执行这些操作的地方。
是否允许直接访问没有任何规则绑定的属性(例如,任何适合整数的内容都可以)是一个好的做法是值得商榷的。我认为为了一致性,使用 getter 和 setter 是一个好主意,即,您始终知道可以调用
setFoo()
来更改foo
属性,而无需看看是否可以直接做。它们还允许您为您的类提供面向未来的证明,这样,如果您有其他代码要执行,放置它的位置已经存在。就我个人而言,我认为必须使用 getter 和 setter 看起来很笨拙。我宁愿写
x.foo = 34
而不是x.setFoo(34)
并期待有一天某些语言能提供相当于数据库触发器的功能允许您定义在赋值之前、之后或代替赋值的代码的成员。The whole idea behind encapsulation is that all knowledge of anything related to the class (other than its interface) is within the class itself. For example, allowing direct access to attributes puts the onus of making sure any assignments are valid on the code doing the assigning. If the definition of what's valid changes, you have to go through and audit everything using the class to make sure they conform. Encapsulating the rule in a "setter" method means you only have to change it in one place, and any caller trying anything funny can get an exception thrown at it in return. There are lots of other things you might want to do when an attribute changes, and a setter is the place to do it.
Whether or not allowing direct access for attributes that don't have any rules to bind them (e.g., anything that fits in an integer is okay) is good practice is debatable. I suppose that using getters and setters is a good idea for the sake of consistency, i.e., you always know that you can call
setFoo()
to alter thefoo
attribute without having to look up whether or not you can do it directly. They also allow you to future-proof your class so that if you have additional code to execute, the place to put it is already there.Personally, I think having to use getters and setters is clumsy-looking. I'd much rather write
x.foo = 34
thanx.setFoo(34)
and look forward to the day when some language comes up with the equivalent of database triggers for members that allow you to define code that fires before, after or instead of a assignments.