4

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?

11
  • Swift has removed the "protected" feature. But then they have two more classes: "fileprivate" is accessible within the same source file, and there is visibility within a module / outside the module. Instead of "protected", you can put baseclass and subclasses into the same source file and make it "fileprivate", which works fine in many cases. Arguably if baseclass and subclass are so strongly connected that the subclass can access otherwise private members, it's not obviously unreasonable to put them into the same source file.
    – gnasher729
    Feb 5 at 23:40
  • @gnasher729 I think Swift's primary motivation was around how protected would interact with extensions, which isn't an issue that comes up with most languages which have "closed" classes
    – Alexander
    Feb 6 at 0:09
  • 1
    If you trust the people writing the subclasses, and your code isn't super security related, and your code is reasonably robust to new behavior, all big "ifs", then protected is extremely useful, for both implementation and documentation. "Here are things a subclass can use, carefully, without major hassles". Making everything private is, IMO, a bit of a bandaid for paranoid engineers.
    – user949300
    Feb 6 at 2:24
  • 4
    Protected members are so that you can expose a more narrower interface to clients, and a wider interface to extenders (e.g., some virtual methods methods that perform internal steps that make no sense on their own to client code can be made protected so that derived classes can override them or make use of them). That's assuming that your intent is to allow for an extension point in your class. Access modifiers aren't a mechanism for security or establishing trust boundaries (private does not give you that) - they have to do with design, extensibility, and reusability. Feb 6 at 4:43
  • 1
    If we were free to redesign every existing legacy code from scratch, we could probably avoid using protected visibility overall. Unfortunately, most of us have dead lines.
    – Doc Brown
    Feb 6 at 5:08

3 Answers 3

7

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.

4
  • Never ever have I encountered any serious LSP drawback in reasonable code It's a terribly useless design principle.
    – user949300
    Feb 6 at 6:45
  • Looking into the book now, Stroustrup does not provide any concrete example or other argumentation in favour of protected member functions. It’s just a bald assertion. Feb 6 at 15:06
  • 1
    @user3840170 The book is about language design and it's already quite long. Adding a lot of examples would have made it even longer. I think he did not develop it further since he recommended private/public, and pointed out the risks with giving too much visibility to internals if protected was over-used. But he debunked his own arguments in all objectively regarding functions. His book "The C++ programming language" provides an example for a GUI toolkit meant to be extended further. Unfortunately it's quite complex and combined with protected inheritance.
    – Christophe
    Feb 6 at 16:50
  • @user949300 That's your opinion and it's your right to have it. Out of curriosity: would you be able to demonstrate it's validity, or is it just a bald assertion? Remark: the point about LSP only confirms that there are more problematic uses of protected than just the question about visibility. THis does not affect the legitimate cases, i.e. providing internal functions to facilitate extending classes of a framework without having to reinvent the wheel each time ;-)
    – Christophe
    Feb 6 at 16:54
1

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.

-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.

5
  • 2
    This is Template Method, which is a classic reason to use protected visibility. Feb 6 at 3:04
  • You don’t end up using the protected methods anywhere in your example. Feb 6 at 6:45
  • As an aside, this example is fundamentally based on how a piggy bank works with coinage, not how a real bank works with money. The "special bank account" which appears to alter its own balance independently, would immediately throw the whole system out of balance.
    – Steve
    Feb 6 at 11:23
  • ill add some more code to explain
    – Ewan
    Feb 7 at 0:10
  • @Steve its always difficult to choose good example classes in OOP, imagine its something else
    – Ewan
    Feb 7 at 0:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.