Why it is recommended to declare instance variable

2019-03-29 08:34发布

问题:

My question relates to Java, but it also can be applied to C#. I was wondering why everybody recommends making the instance variables private as opposed to making them protected.

Let's just think about it. Private variables are not seen by the subclass, so if I need to access or change the variables of the superclass in my subclass I am forced to use some accessor and mutators method like getMyPrivateVariable or setMyPrivateVariable. But when you extend some class and inherit its members, this works as if you have declared them in the subclass directly. So logically this means that the subclass should also have direct access to the instance variables and makes a case for designing the class with protected variables. I understand that such a practice would break encapsulation but this seems irrelevant in the case of inheritance, because, again, in this case everything works as if the members of the superclass were declared in the sublcass, so the sublcass has a "natural right" for being able to access its members directly, no matter if they were inherited or not. Encapsulation, in my opinion, is more important for interfacing with the object by other objects that are outside of the object's inheritance tree.

So, my question is why everyone recommends declaring instance variables of the class as private as opposed to protected?

回答1:

You answer it yourself - encapsulation.

For example you have an abstract Cat class. This defines the member variable speed - i.e. how fast it's running.

The abstract class also defines a final method of run which obviously updates speed.

Now - if subclasses of Cat - e.g. Moggy or Tabby could access and modifiy "speed" directly then it could break the run method.

So best to keep it tied up where it begins. You can also declare it locally if you need to.



回答2:

if I need to access or change the inherited variables in my subclass I am forced to use some accessor [...]

There you have it wrong: private members are not inherited. You may be confusing the meaning of "inherit" with the fact that the instance of the subclass possesses all the members of its superclasses.

The best way to see what it means that private members are not inherited is observing that the subclass may declare its own private member, and this can never cause a clash with a superclass's private member of the same name. The two peacefully coexist.

Members are declared protected only when designing a class specfically to be used by subclassing: in this case the protected members are in fact a part of its public API. Most classes are not designed for public extension, but declaring them final is often not an option. If such classes had all their members protected, it would be tantamount to making them public since any client code could trivially make its dummy subclass, not adding anything to the parent, but exposing its internals.



回答3:

Consider the following "rule of thumb": anything that is visible from outside your class becomes part of a contract with the outside world. In order to keep your freedom to modify your code as you wish with minimum impact, your goal is to restrict the visibility to the minimum possible -- people usually don't limit their freedom unless they're forced to.

In other words, you must have a reason to declare a member something other than private. Once you have a strong reason, only raise the visibility to the minimum possible extent (the first choice should be package private / default visibility). That's because you are supposed to be in full control (or at least have some control) of the code in your package, so changes there can be performed in isolation. As a side note, "package private" is the only visibility level (other than public) that can be applied to top level classes themselves. Therefore, you should consider removing the public modifier from classes that are not used outside their package.

The protected visibility "escapes" your range of control, since any client may rely on that particular variable or method.

As for public, a good practice would be to only use it for methods declared in the interfaces that you implement.



回答4:

If you make your instance variables protected and it's possible to subclass your class (as it's not final) then everybody can subclass your class and change your instance variables when you don't expect it. This raises the chance tremendously that your functionality does not behave the way you want it. You have to deal with a big variety of how your class can be used which makes it very hard for you to make sure that every possible change of your instance variables produces the expected result at any possible point in time where it is changed.



回答5:

IMHO it's a philosophical question. But I guess it's a matter of adding a safety layer to instance variable access.

By exposing them through getters and setters, you guarantee they will be accessed correctly (no out of range values for example) regarding the role of your class. You are also preparing for future interventions, by isolating responsibilities (for example if at some point you realize some processing should be done systematically over a particular variable, then you only have one method to refine, theoretically not touching anything else and more importantly not breaking the functionality of the whole program). You have as well the liberty of defining some of them read-only, by providing only a getter method.

So basically it adds a layer of safety and gives you more control over your variables.

Moreover, since they're called instance variables, it makes sense to me that only the actual instance could access them directly.



回答6:

Essence of OOP is sending message, meaning favorising behavior over data.

The reason to avoid exposing your variables is to make your software the most flexible as possible.

Clients should not take some data from everywhere and implement logic on their own (logic that isn't part of their responsibility), they should Tell! Don't Ask!. They shouldn't master the algorithm needed, that should be the role of the targeted class.

By letting your variable protected in a base class (or final class..), you allow subclasses to alter them, and very very often, subclasses may not have the knowledge nor the responsibility to do that. It would often lead to some mysterious bugs, like assigning an unexpected value to a field's base class from subclasses. Of course, protected is needed for some cases, but only when subclasses is concerned by the very same responsibility.

Thus, always make your variable/method scope as restrictive as you can. Your code will be thus, protected, flexible, easily unit-testable (since concentrated on a single place) and exempt from many usual bugs when exposing instance variables to client. And especially, your class could change their internals without breaking client code.

Besides, avoid getters/setters when you don't deal with data structure. Indeed, this will promote behavioral methods.

Indeed, in 95% of cases, getter/setter are very poor (don't implement valuable logic), and are just an end run to make your variable public/protected.