In short
The protected visibility is a legacy that was designed long before Barbara Liskov's LSP (more precisely its history constraint) demonstrated its many drawbacks.
Its only meaningful use is for specifying internal operations that are designed to be used by derived classes. One extreme example is a protected constructor for an abstract class.
But there are other legitimate uses such as a protected changeState()
in the abstract State
class of the variants of the state pattern implementations that assign this responsibility to the states themselves.
Some more arguments
Although some older languages had some kind of visibility that behaved like protected
notion (e.g. Smalltalk had member variables having by default a visibility the corresponds to protected), the explicit distinction between private and protected is a C++ legacy.
Stroustrup explains the reasons why protected
was introduced in the language and why it was not such a good idea in his book The Design and evolution of C++, page 301-302. The main rationale was to make a difference between implementers (who create a framework and are supposed to know what they do) and the general public:
Mark (...) argued very persuasively based on genuine experience and examples from real code that protected data was essential for the design of an efficient and extensible X windows toolkit. The alternative to protected data was claimed to be an unacceptable inefficiency, unmanageable proliferation of inline interface functions, or public data. (...) protected members seemed the lesser evil.
In the early 80's, compilers were indeed less performant at optimizing, and the CPUs were an order of magnitude slower than today. So calling a getter to access a member of the base class could really be a performance issue for intensively-used GUI components.
Interestingly, Stroustrup recalls that the person who initially requested that language feature ended up banning its use in his own teams because of a lot of nasty errors. Stroustrup even recommends not to use it:
Barabara Liskov's OOPSLA keynote (1987) gives a detailed explanation of the theoretical and practical problems with access control on the protected notion. (...) Fortunately you don't have to use protected data in C++
He nevertheless defined certain valid usages:
[After telling why protected data is evil] Note that none of these objections are significant for member functions. I still consider protected a fine way of specifying operations for use in derived classes.
Some legitimate examples
The only meaningful use of protected is for specifying internal operations that are designed to be used by derived classes. So no example with just one class will be relevant you'll need example of a class that is derived/specialized/extended.
An extreme, yet very common example is a protected constructor for any class that is not intended to be instantiated directly:
- An abstract class may have a protected constructor to set up common invariants (including some implemented with private data members). In many languages, the
protected
will not be needed if the class can be declared as abstract
.
- Other classes may be designed for not being instantiated directly, without necessarily being abstract. The protected constructor is the only mean to achieve this result. Some singletons for example are meant to be specialized. Another example is the composite pattern, where the component is meant to be instantiated either as a leaf or as composite, but there are some common invariants.
The bulk of the legitimate use-cases is however to provide access to common internal helper functions for derived classes, but preventing these details to leak:
The bridge pattern has two independent class hierarchies with the goal of letting them evolve independently: 1) for abstractions 2) for the implementation of those abstractions. In this pattern, the abstractions implement their operations using internally the implementation and some sets of its operations. Making these internals public would leak the implementation that the bridge intends to hide. But making them private would then no longer allow the abstractions to be derived into new abstractions, since extension would hide the internals.
Another example is some variants of the command pattern. Typically, you'd have a base command to be specialized into more specific commands. However, if your commands all share some feature in common, such as supporting a do/undo feature, you'd need some protected functions to get it done, without leaking implementation details to other classes, but sill allowing new commands to be derived/extend and reusing the hidden internals.
A last example (but the list could be much longer) is the template method pattern: a public "template" method of the base class defines a general algorithm, but delegates to class internals some details. Some of those internals may be public. But some internals shall not leak. In the latter case, the only possibility is to make the protected: making them private would not allow to override them. But making them public might break the invariants if called by accident.
Another example is a protected changeState()
in the abstract State
class of the variants of the state pattern implementations that assign this responsibility to the states themselves.
protected
would interact with extensions, which isn't an issue that comes up with most languages which have "closed" classesprivate
does not give you that) - they have to do with design, extensibility, and reusability.