开发者

Why would an OO language really need a PROTECTED access modifier?

I can understand why there is public and private access modifier, these two are also found in almost any language. I can even understand why there might be a package modifier, as you may want to have your classes (those that tightly belong together) interact with each other in a way, that is not suitable for public interaction (e.g. as it depends upon knowledge of class internals, maybe because it would reveal a secret, or maybe because it may change at any time and relying on it will break all existing code, and so on). However, why would I want to have a protected identifier? Don't get me wrong, I know what protected means, but why would I want subclasses of my classes to access certain instance variables or use certain methods, just because they are subclasses and even if开发者_StackOverflow中文版 they are part of a different package? What is a real world use case for protected?

(And performance as an argument for instance variables does not count, since a JIT compiler can always inline accessor methods, decreasing their call overhead to zero)


Public methods are part of the public interface. Private methods are internals. Protected methods are points of extension.

With protected you can redefine the functioning of a class by overriding it without making this method part of the public interface.

Another thing - protected methods are common methods that can be reused by subclasses, but again don't need to be part of the public interface.

For example, in the java collection framework, there is the the AbstractList class. It has protected modCount field and a protected removeRange method:

  • the modCount field is used (incremented) by all subclasses to count the number of modifications. The Iterator returned by AbstractList makes use of that field

  • the removeRange method can be reused by subclasses, instead of having them define it again.

See this related presentation by Josh Bloch on API design.

As noted in the comments, and in the presentation by Bloch - document your class well. And if it is meant for inheritance - make extra effort.


The most frequent use I see is actually to let a superclass use a subclass's internals. Consider this:

class Foo
{
    private int[] array = new int[] { 4, 3, 2, 1 };

    public void processAllElements()
    {
        for (int i = 0; i < array.length; i++)
            processElement(array[i]);
    }

    protected abstract void processElement(int i);
}

class Bar
{
    protected void processElement(int element)
    {
        System.out.println(element);
    }
}

In this case, it's Foo that needs to use the protected element of Bar, and not the opposite. If you want your superclass to access the logic of a subclass, but don't want it to be publicly exposed, you have no choice but the protected modifier. This is called the template method pattern, and it's often used. (Sorry for not providing a real-world example. Head to Wikipedia if you want some.)


I see them as a shortcut when you don't fully know the mind of possible extenders. Say your base class has 5 or 6 attributes. You certainly don't want those attributes (or their set methods) public. But you can see that extenders might want to write functions that would change the values of those attributes. So you make them (or better, their sets) protected. There will always be reliance-on-design that might be ok for an extender but isn't ok for just any old consuming code.

That said, I tell my students "protected is a to-do list". Because if you change anything protected, you have to go looking to find who relied on it. So be really sure about something before you expose it to the unknown extenders of the future.


The fact that languages exist which do not have a protected access modifier, and even languages that do not have access modifiers at all where all methods are public, and those languages are generally considered to be some of the "most pure" OO languages, shows that you do, in fact, not "really need" a protected access modifier.


A public member in a public inheritable class constitutes a contract with the entire world which is binding upon all well-behaved subclasses. A protected member constitutes a contract with any immediate subclasses, which is only binding upon the class which is exposes it. Subclasses are under no obligation to expose any protected members to their own subclasses (indeed, there should be a convention for denying such exposure, as well as a means of specifying that a particular protected member should by default only be available to immediate subclasses unless those subclasses specify that it should be available to their subclasses as well).

Some people may chafe at the idea of a class not allowing access to protected members exposed by its parent, but such behavior is not in any way a violation of the Liskov Substitution Principle. The LSP states that if code may receive a derived-type object when it expects a base-type object, the derived-type object should be able to do anything the base-type object can. Thus, if Moe publicly supports a feature and a type Larry, which derives from Moe doesn't, code which accepts a parameter of type Moe and tries to use that feature would fail if it was given a Larry; such failure would constitute a violation of the LSP.

Suppose, however, that Moe includes a protected feature which Larry does not support. The only classes that would be allowed to use that feature would be either those that directly derive from Moe, or from descendants of Moe that support it. If Curly derives from Moe, it can use that feature without having to worry about whether all subtypes of Moe support it, because Curly's base won't be some arbitrary object that's either an instance of Moe or a derivative (and might or might not support the feature)--it will be a Moe, period.

It is possible for protected fields to introduce some LSP-related issues if derived types use those fields in a manner which breaks public members of the base which use them. On the other hand, one needn't use protected variables for that. If a subtype implements virtual members in a way which differs from the expected behavior of the base type, that may break public members of the base type, even without touching any base-class protected members.


I personally utilize the protected modifier when I want to a) eliminate noise in the public contract yet b) also share functionality with derived classes while in-turn c) writing code that is DRY and it also mindful of the single responsibility principle. Sounds typical I'm sure but let me provide an example.

Here we have a basic query handler interface:

public interface IQueryHandler<TCommand, TEntity>
{
    IEnumerable<TEntity> Execute(TCommand command);
}

This interface is implemented by many different query handlers in the application. Let's say then later that I have a need to cache the query results from many different query handlers. This is really just an implementation detail though. The consumers of any concrete query handler class aren't, generally speaking, concerned about that. My solution then is to create an implementation that takes care of the caching but defers the actual querying responsibility to any derived classes.

public abstract class CachedQueryHandler<TCommand, TEntity> 
    : IQueryHandler<TCommand, TEntity>
{
    public IEnumerable<TEntity> Execute(TCommand command)
    {
        IEnumerable<TEntity> resultSet = this.CacheManager
            .GetCachedResults<TEntity>(command);

        if (resultSet != null)
            return resultSet;

        resultSet = this.ExecuteCore(command);
        this.CacheManager.SaveResultSet(command, resultSet);

        return resultSet;
    }

    protected abstract IEnumerable<TEntity> ExecuteCore(TCommand command);
}

The CachedQueryHandler doesn't intend for anyone else to call the ExecuteCore method directly. It also doesn't care how the querying is implemented though. The protected modifier is perfect for a scenario like this.

Also, I don't want to be repeating that same boiler plate type of code in every query handler particularly because it would be a nightmare to refactor if the cache manger interface changed and a real pain to take out if caching was removed completely at this level.

Here is what a concrete widget query handler would look like:

public class DatabaseWidgetQueryHandler : CachedQueryHandler<WidgetCommand, Widget>
{
    protected override IEnumerable<Widget> ExecuteCore(WidgetCommand command)
    {
        return this.GetWidgetsFromDatabase();
    }
}

Now, if the consumers of the widget query handler utilize it via the query handler interface, which if I utilize dependency injection I can certainly enforce, they will never use anything specific to caching that is added to the CachedQueryProvider class. I'm then free to add/remove caching as necessary or change the caching implementation entirely even with a minimal amount of effort.

IQueryHandler<WidgetCommand, Widget> widgetQueryHandler;
var widgets = widgetQueryHandler.Execute(myWidgetCommand);


Probably, there is some information/property which you will share with your children, even if they are married and live in another country. BTW, there are languages which don't have the protected modifier.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜