Reputation: 385
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
Reputation: 9669
DepartmentsService
and EmployeeService
bot inherit from ServerCache
which its self inherits from DatabaseAccessor
.
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(...)
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.
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