9

Protected visibility in languages like C++, Java or PHP is a strange beast: it makes fields and methods accessible in subclasses, but not in code completely outside the class.

It strikes me as somewhat arbitrary to allow subclasses to access a class member, but not any other code: either kind of code is equally ‘foreign’ to the original class. Using protected instead of public visibility suggests there is some code to which the member should not be exposed; but on the other hand, it should be possible/permitted for a subclass to be written in any way the subclass author likes, and a (sufficiently spiteful?) subclass may simply expose the field/method fully to outside code, either directly or via some kind of proxy (wrapper or accessor methods), thus defeating the protected modifier entirely: it might as well have been public all along.

As far as visibility is concerned, there seems to be little difference between:

public class Foo {
    protected int bar = 42;
}

and

public class FooProtected {
    public int bar = 42;
}

public class Foo { private final FooProtected prot;

public Foo() {
    this.prot = new FooProtected();
}

public Foo(FooProtected otherprot) {
    this.prot = otherprot;
}

}

except that to me, the latter seems more clear about the division of responsibilities.

Protected visibility provides neither convenient access from outside the class (public), nor robust assurance that class invariants are maintained (private); it is the worst of both worlds. As such, it strikes me as a rather useless feature, if not an outright anti-pattern of the same kind as call super. It is not helped when I see an answer on this very site that advice to use protected visibility in preference to private is rubbish.

Are there any relatively concrete, real-world use cases where protected visibility is clearly superior, that are hard to address with only private, public and internal (module-/package-/file-private) visibility?

4 Answers4

13

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.

ittays
  • 5
Christophe
  • 81,699
2

You are approaching it as if the purpose of access modifiers is for the benefit of the class itself, it’s not. Encapsulation / access modifiers, aren’t for the benefit of the class, the class doesn’t care if it’s used or abused or works or not.

Encapsulation is for the benefit of everything outside the class. Exposing them to only such information as is beneficial for them to know.

As such, protected makes perfect since — it’s information that only the class and it’s sub classes benefit from knowing.

To give an analogy, I could post my grocery list and the fact that I am low on toilet paper to the internet, but unless I am hoping that someone else will buy it for me, it’s just a waste of everyone’s time. On the other hand, posting it to a private family chat gives the people that will buy things and the people that will use those things a place to say what is needed.

Sometimes there is an activity or piece of data that is useful or essential for implementing the functionality of the class, but outside of the class it’s useless. Don’t expose people to useless things.

jmoreno
  • 11,238
0

Some very interesting answers already, but just to give another perspective on the matter:

The 'legitimate examples' section in @Christophe's answer reveals an important distinction between private and protected that I don't feel has been addressed in all the answers so far: private members are candidates for early binding, while protected members are not.

(In fact, in the template method example the protected 'details' might still be declared abstract in the base class)

The above statement may, of course vary from language to language, and thus might seem to represent a technical detail, but I prefer to imagine a two-level design decision tree, where the first question is: 'Do you want the member to be visible from outside', the second question is 'Do you want to seal the implementation' and, depending on the answers given, you end up with either one of: public, public final, protected, or private.

crizzis
  • 101
-1

So I guess the reason is so you can override public methods and still have access to internal-to-the-class data

class BankAccount
{
    protected int balance;
    virtual DepositMoney(int cash)
    {
        this.balance += cash;
    }
}

class SpecialBankAccount : BankAccount { public override DepositMoney(int cash) { this.balance += cash * 2; //if it was private you wouldn't be able to override this method effectively } }

Or perhaps you have public methods which are compositions of parts

class BankAccount
{
    protected virtual void writeAudit()...
    protected virtual void addBalance()...
    protected virtual void sendFraudAlert()...
public DepositMoney(int cash)
{
     writeAudit();
     addBalance();
     if(??) sendFraudAlert();
} 

}

class SpecialBankAccount : BankAccount { override writeAudit() { log.write("nothing to see here officer") }

 public override DepositMoney(int cash)
 {
     writeAudit();
     addBalance();
     addBalance();
 }

}

Then in derived classes you can change the way fraud alerts or audit work etc and not have to rewrite the whole DepositMoney method out in full for each one.

Now there may be arguably more principled ways of achieving the same thing, and these days inheritance as a whole has got a bit of a bad name. but you can see how they might get overly complicated in some situations and if you want to use inheritance, you want to use inheritance.

Ewan
  • 83,178