可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I understand what public, private, and protected do. I know that you are supposed to use them to comply with the concept of Object Oriented Programming, and I know how to implement them in a program using multiple classes.
My question is: Why do we do this? Why shouldn't I have one class modifying the global variables of another class directly? And even if you shouldn't why are the protected, private, and public modifiers even necessary? It's as if programmers don't trust themselves not to do it, even though they are the ones writing the program.
Thanks in advance.
回答1:
You're right, it's because we can't trust ourselves. Mutable state is a major factor in complexity of computer programs, it's too easy to build something that seems ok at first and later grows out of control as the system gets bigger. Restricting access helps to reduce the opportunities for objects' states to change in unpredictable ways. The idea is for objects to communicate with each other through well-defined channels, as opposed to tweaking each others' data directly. That way we have some hope of testing the individual objects and having some confidence in how they'll behave as part of a larger system.
回答2:
Let me give a basic example (this is only for illustration):
class Foo {
void processBar() {
Bar bar = new Bar();
bar.value = 10;
bar.process();
}
}
class Bar {
public int value;
public void process() {
// Say some code
int compute = 10/value;
// Her you have to write some code to handle
// exception
}
}
Every thing looks good and you are happy. Now later you realized that other developers or your other apis that you are using to set the value are setting to 0
and this is leading to exception in your Bar.process()
function.
Now according to above implementation there is no way you can restrain users from setting it to 0. Now look at below implementation.
class Foo {
void processBar() {
Bar bar = new Bar();
bar.setValue(0);
bar.process();
}
}
class Bar {
public int value;
public void setValue(int value) {
if(value == 0)
throw new IllegalArgumentException("value = 0 is not allowed");
this.value = value;
}
public void process() {
// Say some code
int compute = 10/value;
// No need to write exception handling code
// so in theory can give u better performance too
}
}
Not only you can now put check but also give a informative exception which can help figuring errors quickly and at early stage.
This is just one of the examples, the basics of OOP (Encapsulation, Abstraction etc.), help you standardize interface and hide the underneath implementation.
回答3:
Keep in mind that the developer coding a given class may not be the only one using it. Teams of developers write software libraries, which in Java are commonly distributed as JARs, used by completely different teams of developers. If these standards weren't in place, it would be very difficult for others to know, at a minimum, what the intent was of any available variables / methods.
If I have a private/protected instance variable, for example, I may have a public "setter" method that checks for validity, preconditions, and performs other activities - all which would be bypassed if anyone was freely able to modify the instance variable directly.
Another good point is in the Java documentation / tutorial: http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html :
Public fields tend to link you to a particular implementation and
limit your flexibility in changing your code.
回答4:
- Non-local behavior is difficult to reason about.
- Minimizing surface area increases comprehension.
- I don't trust myself to remember all behavioral side-effects.
- I definitely don't trust "you" to understand all behavioral side-effects.
- The less I expose the more flexibility I have to modify and extend.
回答5:
My question is: Why do we do this?
Basically, because by restricting ourselves in this way, we make it easier ... for ourselves, and others who may need to read / modify the code in the future ... to understand the code, and the way that the various parts interact.
Most developers understand code by a mental process of abstraction; i.e. mentally drawing boundaries around bits of code, understanding each bit in isolation, and then understanding how each bit interacts with other bits. If any part of the code could potentially mess around with the "innards" of any other part of the code, then it makes it hard for the typical developer to understand what is going on.
This may not be a problem for you while you are writing the code, because you may be able to keep all of the complex interactions in your head while you create the code. But in a year or two's time, you will have forgotten a lot of the details. And other people never had the details in their heads to start with.
Why shouldn't I have one class modifying the global variables of another class directly?
Because it makes your code harder to understand; see above. The larger your codebase is, the more pronounced the problem will be.
Another point is that if you use over-use globals (statics actually) then you create problems if your code needs to be multi-threaded / reentrant, for unit testing, and if you need to reuse your code in other contexts.
And even if you shouldn't why are the protected, private, and public modifiers even necessary? It's as if programmers don't trust themselves not to do it, even though they are the ones writing the program.
It is not about trust. It is about expressing in the source code where the boundaries are.
If I write a class and declare a method or a field private
, I know that I don't have to consider the problem of what happens if some other class calls it / accesses it / modifies it. If I'm reading someone elses code, I know that I can (initially) ignore the private
parts when mapping the interactions and boundaries. The private
and protected
modifiers and package private just
provide different granularities of boundary.
(Or maybe it is about trust; i.e. not trusting ourselves to remember where the abstraction boundaries in our design are / were.)
回答6:
There are basically two reasons:
1) There are interfaces in Java where security is required. For instance, when running a java applet on your box you want to assure that the applet can't access parts of the file system to which it's not authorized. Without enforcible security, an applet could reach into the Java security layer and modify its own authorities.
2) Even when everyone is "trusted", sometimes expediency trumps common sense, and programmers bypass APIs to access internal interfaces rather than getting the APIs enhanced (which admittedly can often take longer than practical). This creates problems both for stability and for upgrade compatibility.
(There's a legend, deep in the ancient history of computing, of an OS that was treated this way by the application programmers, to the extent that the programmers maintaining the OS were forced to make sure that certain code sections (not entry points, but actual internal code sequences) didn't change physical addresses when the OS was revised.)
Note that these problems existed before the OOP paradigm became common, and are some of the motivation for OOP. OOP isn't an arbitrary religious doctrine invented out of thin air, but is a set of principles that have been filtered through about 6 decades of programming experience.