This is a rather basic OO question, but one that's been bugging me for some time.
I tend to avoid using the 'private' visibility modifier for my fields and methods in favor of protected
.
This is because, generally, I don't see any use in hiding the implementation between base class and child class, except when I want to set specific guidelines for the extension of my classes (i.e. in frameworks). For the majority of cases I think trying to limit how my class will be extended either by me or by other users is not beneficial.
But, for the majority of people, the private
modifier is usually the default choice when defining a non-public field/method.
So, can you list use cases for private
? Is there a major reason for always using private? Or do you also think it's overused?
Making a variable private "protects" its value when the code runs. At this level, we are not concerned with protecting it from other programmers changing the code itself. The point of so-called "data hiding" is to keep internal data hidden from other classes which use the class.
Fields should be declared private unless there is a good reason for not doing so. One of the guiding principles of lasting value in programming is "Minimize ripple effects by keeping secrets." When a field is private , the caller cannot usually get inappropriate direct access to the field.
Private methods are useful for breaking tasks up into smaller parts, or for preventing duplication of code which is needed often by other methods in a class, but should not be called outside of that class.
Protected methods are a balance between public and private methods. They are similar to private methods in that they cannot be accessed in the public scope. Neither the client nor the program can invoke them. However, objects of the same class can access each other's protected methods.
There is some consensus that one should prefer composition over inheritance in OOP. There are several reasons for this (google if you're interested), but the main part is that:
Therefore, if you choose to make your class inheritable, you should do so conciously and with all the pros and cons in mind.
Hence, it's better not to make the class inheritable and instead make sure it's as flexible as possible (and no more) by using other means.
This is mostly obvious in larger frameworks where your class's usage is beyond your control. For your own little app, you won't notice this as much, but it (inheritance-by-default) will bite you in the behind sooner or later if you're not careful.
Alternatives
Composition means that you'd expose customizability through explicit (fully abstract) interfaces (virtual or template-based).
So, instead of having an Vehicle base class with a virtual drive() function (along with everything else, such as an integer for price, etc.), you'd have a Vehicle class taking a Motor interface object, and that Motor interface only exposes the drive() function. Now you can add and re-use any sort of motor anywhere (more or less. :).
There are two situations where it matters whether a member is protected
or private
:
If one can imagine a realistic scenario where a derived class might benefit from being able to access the member, and cannot imagine a scenario where the base class might benefit from changing its behavior, then the member should be protected
[assuming, of course, that it shouldn't be public]. If one cannot imagine a scenario where a derived class would get much benefit from accessing the member directly, but one can imagine scenarios where a future version of the base class might benefit by changing it, then it should be private
. Those cases are pretty clear and straightforward.
If there isn't any plausible scenario where the base class would benefit from changing the member, I would suggest that one should lean toward making it protected
. Some would say the "YAGNI" (You Ain't Gonna Need It) principle favors private
, but I disagree. If you're is expecting others to inherit the class, making a member private doesn't assume "YAGNI", but rather "HAGNI" (He's Not Gonna Need It). Unless "you" are going to need to change the behavior of the item in a future version of the class, "you" ain't gonna need it to be private
. By contrast, in many cases you'll have no way of predicting what consumers of your class might need. That doesn't mean one should make members protected
without first trying to identify ways one might benefit from changing them, since YAGNI
isn't really applicable to either decision. YAGNI applies in cases where it will be possible to deal with a future need if and when it is encountered, so there's no need to deal with it now. A decision to make a member of a class which is given to other programmers private
or protected
implies a decision as to which type of potential future need will be provided for, and will make it difficult to provide for the other.
Sometimes both scenarios will be plausible, in which case it may be helpful to offer two classes--one of which exposes the members in question and a class derived from that which does not (there's no standard idiomatic was for a derived class to hide members inherited from its parent, though declaring new members which have the same names but no compilable functionality and are marked with an Obsolete
attribute would have that effect). As an example of the trade-offs involved, consider List<T>
. If the type exposed the backing array as a protected member, it would be possible to define a derived type CompareExchangeableList<T> where T:Class
which included a member T CompareExchangeItem(index, T T newValue, T oldvalue)
which would return Interlocked.CompareExchange(_backingArray[index], newValue, oldValue)
; such a type could be used by any code which expected a List<T>
, but code which knew the instance was a CompareExchangeableList<T>
could use the CompareExchangeItem
on it. Unfortunately, because List<T>
does not expose the backing array to derived classes, it is impossible to define a type which allows CompareExchange
on list items but which would still be useable by code expecting a List<T>
.
Still, that's not to imply that exposing the backing array would have been completely without cost; even though all extant implementations of List<T>
use a single backing array, Microsoft might implement future versions to use multiple arrays when a list would otherwise grow beyond 84K, so as to avoid the inefficiencies associated with the Large Object Heap. If the backing array was exposed as protected member, it would be impossible to implement such a change without breaking any code that relied upon that member.
Actually, the ideal thing might have been to balance those interests by providing a protected member which, given a list-item index, will return an array segment which contains the indicated item. If there's only one array, the method would always return a reference to that array, with an offset of zero, a starting subscript of zero, and a length equal to the list length. If a future version of List<T>
split the array into multiple pieces, the method could allow derived classes to efficiently access segments of the array in ways that would not be possible without such access [e.g. using Array.Copy
] but List<T>
could change the way it manages its backing store without breaking properly-written derived classes. Improperly-written derived classes could get broken if the base implementation changes, but that's the fault of the derived class, not the base.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With