I was recently asked a general question on OO (object orientation). The question was: "I have a class with some members that I don't need in a particular child class. How do I hide the unneeded features?"
There are a couple of answers, which I will cover in this article. Answers that are about good old straight forward OO techniques!
I want to take a minute to encourage everyone about back to basics concepts. One casualty of the current bad economy seems to be sound development practices. You will hear a lot about Web programming, cloud computing, tools and components, but it seems there is little talk about good old object oriented (OO) anything. Modeling, design, UML, patterns, and refactorings are as relevant and useful as ever—actually more so. I wonder whether the layoffs and economic uncertainty hasn't created a kind of "just get the job done" atmosphere where first principles are discarded. I encourage you not to let this happen where you work. Languages come and go, some sticking longer than others. Technical challenges are zooming into the stratosphere of complexity, but clouds and immagical-imaginary, magical-tools won't solve all the problems without thinking being done by human beings. The best part of all of this is that reading, learning, thinking, and honing your technical skills as one of the best economical bangs for your buck available.
Stepping down from soap box, let's get started.
Defining the Problem Space
The problem as defined by my questioner is basically how are members of classes hidden in child classes if they are not needed? You have a potpourri of possibilities:
- Without sounding too flippant, one answer is who cares? If you don't need it don't use it. There is an infinite amount of space in the digital world. (It's like the old joke: "how much does a gigabyte weigh?"
- If you didn't like the first option you'll hate this one even more: OO is hard to produce; the members are probably in the wrong class. (I am not kidding here people these are valid answers)
- You can always add a new member with the same signature in the child class and pretty effectively quash that nasty dead weight (see Listing 1). Hiding members is a bit ugly and polymorphism, reflection, and the DirectCast will actually break this approach
- Or a pretty good answer is to use a faC'ade class. A faC'ade is a class that selectively exposes the elements of another class or classes. (The rest of this article focuses on our friend the FaC'ade pattern.)
Listing 1: Hide an existing member in a parent class by stomping on the signature you'd like to block.
Module Module1 Sub Main() Dim o As HideMyMethod = New IWillHideYou() Dim p As IWillHideYou = New IWillHideYou() o.HideMe() p.HideMe() ' Calls shadowing member in HideMyMethod.HideMe called DirectCast(p, HideMyMethod).HideMe() Console.ReadLine() End Sub End Module Public Class HideMyMethod Public Sub HideMe() Console.WriteLine("HideMyMethod.HideMe Called") End Sub End Class Public Class IWillHideYou Inherits HideMyMethod Public Sub HideMe() Console.WriteLine("IWillHideYou.HideMe Called") End Sub End Class
In Listing 1 o actually calls the base class method—HideMyMethod.HideMe—even though the instance of o is IWillHideYou. The instance p actually calls the child class method, and even though p should call IWillHideYou.HideMe because of the DirectCast HideMyMethod.HideMe is called. And, of course, with reflection the class consumer could circumvent the shadowing of HideMe done by IWillHideYou. (Note: using the same signature in a child class is called shadowing in VB and the Shadows modifier—as in, Public Shadows Sub HideMe()—will eliminate the warning caused by implicit shadowing.
Using the FaC'ade Pattern to Hide Deadweight or Reduce Complexity
Over time classes can end up with too many members to make them easy to use. The general rule of thumb is that a class should have about a half dozen public members and be responsible for one basic kind of solution. What happens in reality is that junk is added to classes over time and eventually those classes become hard to own and hard to use. The next thing that happens is that someone wants to re-write everything. Aside from the fact that one should generally not re-write everything, rather one should refactor things, re-writing is risky business.
|Caveat: Bad OO can become cumbersome to consume even if you produced it (some time ago).|
When code is re-written the likeliest thing to happen is that code that works will be broken. The result is one goes from code that is hard to use and own to code that is just broken. At least with Refactoring code is changed in carefully prescribed ways without changing its observable testable behavior. One such Refactoring is to introduce a faC'ade.
By using a faC'ade you can hide complexity behind the faC'ade making the existing stuff easier to consume while hiding all of the murky complexity. Interestingly enough a faC'ade works with our problem, which is, hiding stuff you don't want to see or use. In Listing 2 another method is added to HideMyMethod. This time a faC'ade is created to expose only the method—the new one—desired.
Listing 2: The faC'ade pattern let's you simplify interfaces by concealing unneeded members.
Module Module1 Sub Main() Dim obj As HideMyMethodFacade = New HideMyMethodFacade() obj.DontHideMe() Console.ReadLine() End Sub End Module Public Class HideMyMethod Public Sub HideMe() Console.WriteLine("HideMyMethod.HideMe Called") End Sub Public Sub DontHideMe() Console.WriteLine("I am available") End Sub End Class Public Class HideMyMethodFacade Private obj As HideMyMethod = New HideMyMethod Public Sub DontHideMe() obj.DontHideMe() End Sub End Class
In Listing 2 HideMyMethod is easier to use because the unneeded methods are hidden behind the HideMyMethodFacade class. The consumer doesn't even have to know HideMyMethod.HideMe exists. By the way WCF services are an implementation of the FaC'ade pattern.
For more on OO design check out my book UML DeMystified from Osborne-McGraw-Hill, check out http://www.dofactory.com, or my blog at http://community.devexpress.com/bloks/paulk.
It is possible to change the exposed public members of a class without breaking the class by using the FaC'ade pattern, which is actually a design pattern and a refactoring. Keep in mind that economic paranoia is no excuse for taking shortcuts or throwing good techniques out the window even though your pointy-headed manager might think so. (I know you aren't taking shortcuts, but someone in the cubicle next to you might be. Tell him or her to quit it.)
I get my ideas for columns from problems I experience or from readers. (I don't generally mention the inspirer unless they provide a good solution.)