o.carltonne
o.carltonne

Reputation: 385

Protected Static Method Visibility

Consider this situation: (Note that types and method bodies have been left out for brevity)

This gets me connected to data when I need to do that.

public abstract class DatabaseAccessor {
    protected static object GetDataFromDatabase(...) {...} 
    protected static object UpdateSomething(...) {...}
}

This manages caching - if I ask ServerCache for a resource and it doesn't have it, then ServerCache can use DatabaseAccessor to get what it needs. I don't provide access to the cached resources directly.

public abstract class ServerCache : DatabaseAccessor {
    private static object CachedResources;
    protected static object GetFromCacheOrGetFresh(...) {...}
    protected static void InvalidateCache(...) {...}
}

This is a service that provides a way to get access to either cached data or fresh data, send updates, invalidate/expire cached resources.

public class DepartmentsService : ServerCache {
    public static object Read() {
        ...
        return ServerCache.GetFromCacheOrGetFresh();
    }
    public static object Update(string id) {
        ...
        DatabaseAccessor.UpdateSomething(...);
        ServerCache.InvalidateCache(...);
        return DepartmentsService.Read().Where(record => record.Id == id);
    }
    public static object Create(...){...}
}

This has been working great until I had the need to create additional services, just like the one above. It went like this:

public class EmployeeService : ServerCache {
    ...
    public static object Create(object creation) {
        //check first service to see if creation is valid 
        //by checking that the DepartmentsService contains creation.Department.
        DepartmentsService.Read().Where(..... problem            

    }
}

I'm able to see all the protected methods of ServerCache when calling DepartmentsService OUTSIDE of DepartmentsService, in a non-derived class.

Further on in the design, Web Api Controllers use both the DepartmentsService and EmployeeService. The controllers do not inherit from ServerCache, and the methods are hidden as expected.

Just because DepartmentsService and EmployeesService both derivce from ServerCache, that shouldn't mean that EmployeesService should be able to call the essentially private methods of DepartmentsService.

What am I missing here about the protected modifier?
Is there some nuance I'm missing when protected is used with static?
Is this when "method hiding" happens?

Upvotes: 2

Views: 4522

Answers (1)

nozzleman
nozzleman

Reputation: 9669

DepartmentsService and EmployeeService bot inherit from ServerCache which its self inherits from DatabaseAccessor.

What can EmployeeService see?

Well, first of all, it can see all public Members of all the other classes, including DepartmentsService. Furthermore, it can see all protected methods of DatabaseAccessor and ServerCache, since it is below them in inheritance hierachy.

This is the list of what it can see:

protected static DatabaseAccessor.GetDataFromDatabase(...)
protected static DatabaseAccessor.UpdateSomething(...)
protected static ServerCache.GetFromCacheOrGetFresh(...)
protected static ServerCache.InvalidateCache(...)
public static DepartmentsService.Read()
public static DepartmentsService.Update(string id)
public static DepartmentsService.Create(...)

What happens to a protected member after it is being inherited?

It stays beeing what it is: a protected member of the base-class. It is executed in the base classes context unless it is beeing overridden with the new-keyword (or virtual and override, which is not allowed in your case because it is all declared static). So it is visible to all child-classes of the parent class.

This is an essential point to notice and one of the drivers for liskov substitution principle, which basically says that one should be able to assign an instance of a child class to a reference its base class, then call the base classes members and it should behave like one would expect a base class instance to behave, not knowing that is is a child class instance in reality.

Let me show you what this means with the given example, assuming the methods would not be static:

DatabaseAcccessor accessor = new ServerCache();
var data = accessor.GetDataFromDatabase(...); 

should behave exactly the same as:

ServerCache accessor = new ServerCache();
var data = accessor.GetDataFromDatabase(...); 

because you can never know, if an variable or parameter references to an derived type instance.

Therefore, always be careful when using new and override, and generally favor composition over inheritance.

To address your Comment

How could you prevent protected methods of DatabaseAccessor to be called on DepartmentService from EmployeeService?

This requires a little bit of reorganisation. I would recommend to make ServerCache Singleton or Monostate. Lets go for Singleton:

public class ServerCache : DatabaseAccessor {
    private object CachedResources;

    private static readonly ServerCache instance;

    private ServerCache(){ /* private ctor to prevent instances from beeing createt anywhere but in this class */ }

    private static ServerCache Instance 
    {
        get
        {
            if(instance==null)
               instance = new ServerCache();
            return instance;
        }
    }

    public object GetFromCacheOrGetFresh(...) {...}
    public void InvalidateCache(...) {...}
}

You can now create ServerCache variables inside DepartmentsService and EmployeeService instead of inheriting from ServerCache. This way the ServerCache methods will not be available as protected methods of DepartementsService for example.

Upvotes: 2

Related Questions