Should protected attributes always be banned?

2019-03-18 10:32发布

问题:

I seldom use inheritance, but when I do, I never use protected attributes because I think it breaks the encapsulation of the inherited classes.

Do you use protected attributes ? what do you use them for ?

回答1:

In this interview on Design by Bill Venners, Joshua Bloch, the author of Effective Java says:

Trusting Subclasses

Bill Venners: Should I trust subclasses more intimately than non-subclasses? For example, do I make it easier for a subclass implementation to break me than I would for a non-subclass? In particular, how do you feel about protected data?

Josh Bloch: To write something that is both subclassable and robust against a malicious subclass is actually a pretty tough thing to do, assuming you give the subclass access to your internal data structures. If the subclass does not have access to anything that an ordinary user doesn't, then it's harder for the subclass to do damage. But unless you make all your methods final, the subclass can still break your contracts by just doing the wrong things in response to method invocation. That's precisely why the security critical classes like String are final. Otherwise someone could write a subclass that makes Strings appear mutable, which would be sufficient to break security. So you must trust your subclasses. If you don't trust them, then you can't allow them, because subclasses can so easily cause a class to violate its contracts.

As far as protected data in general, it's a necessary evil. It should be kept to a minimum. Most protected data and protected methods amount to committing to an implementation detail. A protected field is an implementation detail that you are making visible to subclasses. Even a protected method is a piece of internal structure that you are making visible to subclasses.

The reason you make it visible is that it's often necessary in order to allow subclasses to do their job, or to do it efficiently. But once you've done it, you're committed to it. It is now something that you are not allowed to change, even if you later find a more efficient implementation that no longer involves the use of a particular field or method.

So all other things being equal, you shouldn't have any protected members at all. But that said, if you have too few, then your class may not be usable as a super class, or at least not as an efficient super class. Often you find out after the fact. My philosophy is to have as few protected members as possible when you first write the class. Then try to subclass it. You may find out that without a particular protected method, all subclasses will have to do some bad thing.

As an example, if you look at AbstractList, you'll find that there is a protected method to delete a range of the list in one shot (removeRange). Why is that in there? Because the normal idiom to remove a range, based on the public API, is to call subList to get a sub-List, and then call clear on that sub-List. Without this particular protected method, however, the only thing that clear could do is repeatedly remove individual elements.

Think about it. If you have an array representation, what will it do? It will repeatedly collapse the array, doing order N work N times. So it will take a quadratic amount of work, instead of the linear amount of work that it should. By providing this protected method, we allow any implementation that can efficiently delete an entire range to do so. And any reasonable List implementation can delete a range more efficiently all at once.

That we would need this protected method is something you would have to be way smarter than me to know up front. Basically, I implemented the thing. Then, as we started to subclass it, we realized that range delete was quadratic. We couldn't afford that, so I put in the protected method. I think that's the best approach with protected methods. Put in as few as possible, and then add more as needed. Protected methods represent commitments to designs that you may want to change. You can always add protected methods, but you can't take them out.

Bill Venners: And protected data?

Josh Bloch: The same thing, but even more. Protected data is even more dangerous in terms of messing up your data invariants. If you give someone else access to some internal data, they have free reign over it.

Short version: it breaks encapsulation but it's a necessary evil that should be kept to a minimum.



回答2:

C#:

I use protected for abstract or virtual methods that I want base classes to override. I also make a method protected if it may be called by base classes, but I don't want it called outside the class hierarchy.



回答3:

You may need them for static (or 'global') attribute you want your subclasses or classes from same package (if it is about java) to benefit from.

Those static final attributes representing some kind of 'constant value' have seldom a getter function, so a protected static final attribute might make sense in that case.



回答4:

Scott Meyers says don't use protected attributes in Effective C++ (3rd ed.):

Item 22: Declare data members private.

The reason is the same you give: it breaks encapsulations. The consequence is that otherwise local changes to the layout of the class might break dependent types and result in changes in many other places.



回答5:

There are never any good reasons to have protected attributes. A base class must be able to depend on state, which means restricting access to data through accessor methods. You can't give anyone access to your private data, even children.



回答6:

The protected keyword is a conceptual error and language design botch, and several modern languages, such as Nim and Ceylon (see http://ceylon-lang.org/documentation/faq/language-design/#no_protected_modifier), that have been carefully designed rather than just copying common mistakes, don't have such a keyword.

It's not protected members that breaks encapsulation, it's exposing members that shouldn't be exposed that breaks encapsulation ... it doesn't matter whether they are protected or public. The problem with protected is that it is wrongheaded and misleading ... declaring members protected (rather than private) doesn't protect them, it does the opposite, exactly as public does. A protected member, being accessible outside the class, is exposed to the world and so its semantics must be maintained forever, just as is the case for public. The whole idea of "protected" is nonsense ... encapsulation is not security, and the keyword just furthers the confusion between the two. You can help a little by avoiding all uses of protected in your own classes -- if something is an internal part of the implementation, isn't part of the class's semantics, and may change in the future, then make it private or internal to your package, module, assembly, etc. If it is an unchangeable part of the class semantics, then make it public, and then you won't annoy users of your class who can see that there's a useful member in the documentation but can't use it, unless they are creating their own instances and can get at it by subclassing.



回答7:

I don't use protected attributes in Java because they are only package protected there. But in C++, I'll use them in abstract classes, allowing the inheriting class to inherit them directly.



回答8:

In general, no you really don't want to use protected data members. This is doubly true if your writing an API. Once someone inherits from your class you can never really do maintenance and not somehow break them in a weird and sometimes wild way.



回答9:

I think protected attributes are a bad idea. I use CheckStyle to enforce that rule with my Java development teams.



回答10:

I recently worked on a project were the "protected" member was a very good idea. The class hiearchy was something like:

[+] Base
 |
 +--[+] BaseMap
 |   |
 |   +--[+] Map
 |   |
 |   +--[+] HashMap
 |
 +--[+] // something else ?

The Base implemented a std::list but nothing else. The direct access to the list was forbidden to the user, but as the Base class was incomplete, it relied anyway on derived classes to implement the indirection to the list.

The indirection could come from at least two flavors: std::map and stdext::hash_map. Both maps will behave the same way but for the fact the hash_map needs the Key to be hashable (in VC2003, castable to size_t).

So BaseMap implemented a TMap as a templated type that was a map-like container.

Map and HashMap were two derived classes of BaseMap, one specializing BaseMap on std::map, and the other on stdext::hash_map.

So:

  • Base was not usable as such (no public accessors !) and only provided common features and code

  • BaseMap needed easy read/write to a std::list

  • Map and HashMap needed easy read/write access to the TMap defined in BaseMap.

For me, the only solution was to use protected for the std::list and the TMap member variables. There was no way I would put those "private" because I would anyway expose all or almost all of their features through read/write accessors anyway.

In the end, I guess that if you en up dividing your class into multiple objects, each derivation adding needed features to its mother class, and only the most derived class being really usable, then protected is the way to go. The fact the "protected member" was a class, and so, was almost impossible to "break", helped.

But otherwise, protected should be avoided as much as possible (i.e.: Use private by default, and public when you must expose the method).



回答11:

I use them. In short, it's a good way, if you want to have some attributes shared. Granted, you could write set/get functions for them, but if there is no validation, then what's the point? It's also faster.

Consider this: you have a class which is your base class. It has quite a few attributes you wan't to use in the child objects. You could write a get/set function for each, or you can just set them.

My typical example is a file/stream handler. You want to access the handler (i.e. file descriptor), but you want to hide it from other classes. It's way easier than writing a set/get function for it.



回答12:

In general, yes. A protected method is usually better.

In use, there is a level of simplicity given by using a protected final variable for an object that is shared by all the children of a class. I'd always advise against using it with primitives or collections since the contracts are impossible to define for those types.

Lately I've come to separate stuff you do with primitives and raw collections from stuff you do with well-formed classes. Primitives and collections should ALWAYS be private.

Also, I've started occasionally exposing public member variables when they are declaired final and are well-formed classes that are not too flexible (again, not primitives or collections).

This isn't some stupid shortcut, I thought it out pretty seriously and decided there is absolutely no difference between a public final variable exposing an object and a getter.



回答13:

It depends on what you want. If you want a fast class then data should be protected and use protected and public methods. Because I think you should assume that your users who derive from your class know your class quite well or at least they have read your manual at the function they going to override.

If your users mess with your class it is not your problem. Every malicious user can add the following lines when overriding one of your virtuals:

(C#)

static Random rnd=new Random();
//...
if (rnd.Next()%1000==0) throw new Exception("My base class sucks! HAHAHAHA! xD");
//...

You can't seal every class to prevent this.

Of course if you want a constraint on some of your fields then use accessor functions or properties or something you want and make that field private because there is no other solution...

But I personally don't like to stick to the oop principles at all costs. Especially making properties with the only purpose to make data members private.

(C#):

private _foo;
public foo
{
   get {return _foo;}
   set {_foo=value;}
}

This was my personal opinion.

But do what your boss require (if he wants private fields than do that.)



回答14:

I use protected variables/attributes within base classes that I know I don't plan on changing into methods. That way, subclasses have full access to their inherited variables, and don't have the (artificially created) overhead of going through getters/setters to access them. An example is a class using an underlying I/O stream; there is little reason not to allow subclasses direct access to the underlying stream.

This is fine for member variables that are used in direct simple ways within the base class and all subclasses. But for a variable that has a more complicated use (e.g., accessing it causes side effects in other members within the class), a directly accessible variable is not appropriate. In this case, it can be made private and public/protected getters/setters can be provided instead. An example is an internal buffering mechanism provided by the base class, where accessing the buffers directly from a subclass would compromise the integrity of the algorithms used by the base class to manage them.

It's a design judgment decision, based on how simple the member variable is, and how it is expected to be so in future versions.

Encapsulation is great, but it can be taken too far. I've seen classes whose own private methods accessed its member variables using only getter/setter methods. This is overkill, since if a class can't trust its own private methods with its own private data, who can it trust?



标签: oop protected