Last month I did a blog on composition over abstraction (click here to read); this is more about making lemonade (working with what I got) then a preferred pattern at least to this C# developer. With C++/CX your classes have to be sealed unless then derive from an existing unsealed class, currently the recommended class to use is the UI framework’s DependencyObject. Not my first choice for creating a baseclass for a business or data access layer (and it has issues) but once again – I’ll make lemonade. This is not to say that there was not a lot of truth to what I wrote – the key is to find the balance of composition AND abstraction, fortunately C# developers have this choice.
So why do we C# developers love our inheritance? Because it simplifies our code and prevents us from having to do copy/paste development. Take for example our C# VS2010 demo application that accompanies this blog, below I click on the [Woof] button and it reveals content relevant to a dog; each button represents an animal.
Source code: http://www.global-webnet.com/blogfiles/InheritanceExample.zip
All of the animals have something in common, a name, color, leg count, and they make a sound. An animal for these attributes could be described by the following class (note that the Initialize method will initialize my base to common values). The Speak() method results will be displayed to the status bar upon button clicks.
The AnimalBase has an IAnimal interface which looks as follows:
This is not to say that we are confined to just these properties, perhaps our Cat and Dog have additional properties that are not common to either (or any) animal. For example our Dog class will want to have a means to designate its best friend as well as provide a message (reference figure 1 and below).
Because of inheritance we can easily create our AnimalDog class by simply deriving from AnimalBase; effectively giving us access to all of its properties and methods without us having to duplicate them in our AnimalDog class.
To initialize my dog class I simply have to set the properties for the attributes required for my usage, in this case a black dog that barks and has a best friend of man. When I ask this class to speak it reveals this information (reference figure 1 status bar).
What if we want to override what the animal says when it speaks? This is our case for our AnimalCat as shown below where we wanted to add more information to our status bar:
Below our AnimalCat class “overrides” the Speak() method (below lines 30-35). In order to do this the baseclass has to support it, in our case (figure 2 line 61) we define Speak as a “virtual” method, this tells the application that this code can be overridden.
Since we want the original message (we simply want to append to it) we call the base.Speak() method on line 32 below. We then append the additional information our cat class provides. Note that we initialize our cat values as applicable for our business requirements.
In the case of our Cow and Pig (image below) we’ll reap the greatest benefits of inheritance as we only have a couple of things that differ for our purposes – so the code for each of those classes only have Initialize methods:
Since this is a MvpVm application our business logic will reside in the MainPresenter (see folder MvpVm in figure 8 above (you won’t find business logic in code-behind or view models – this promotes reuse) . Below we show the MainView which also resides in this folder.
How it works? In a nutshell, all of our buttons of our main menu will be bound to the “ButtonCommand” (XAML line 20 of image below) of our MainViewModel (shown in figure 9), they each send a CommandParameter (line 22) of “self” which effectively provides the ButtonCommand an instance of the button clicked. We then add all of our buttons (lines 26 through 30).
The Presenter instantiates the View (line 21) and the ViewModel (line 22) and then wires up the ButtonCommand – when the button is clicked it will be handled by the code between lines 28 and 43.
When a button is clicked line 31 will remove the “btn” from the name, i.e., BtnCat will become “Cat”. This is passed into the factory on line 37 (reference figure 10 for AnimalFactory code) and a concrete instance of animal is returned, i.e., an instance of AnimalCat would be returned.
The instance returned from the factory is inserted (line 41 right panel) into the XAML contentControl (line 34 left panel). We then update the ViewModel.StatusBarMessage with the instance Speak() results.
We effectively stuffed a instance of a class into a XAML content control, this is where the power of XAML shines because the first thing it will do is check to see if there is a DataTemplate for layout instructions. In this case there is because we updated our App.xaml page to point to our Resources/EntityTemplate.xaml resource dictionary (reference figure 11). Before we move our discussion to figure 11 make a note that our Presenter on line 45 below (right panel) handles button clicks for any button that binds to AnimalCommand. Let’s jump to figure 11 now (below).
Note that the factory will call the Initialize() method on the applicable class (effectively setting its values) and then will replace the .Name property of the class with the key value, i.e., “Cat”, “Dog”, etc.
Because we pushed our instance into a ContentControl that instance now becomes its DataContext. For this example we’ll assume the [Woof] button was clicked and we are looking at an AnimalDog instance (lines 22-40).
We display the Name value and have the MyBestFriend value in a TextBox. The Button binds to AnimalCommand (to handle it’s button clicks) but note we have to do something extra; because we changed the DataContext to the instance, and our AnimalCommand (which we want to bind to) resides on the ViewModel, we have to tell the Command to go to the MainView and use its DataContext.AnimalCommand for binding - we do this via the RelativeSource attribute.
When the Button is clicked, the Presenter (figure 8 line 46) will be passed in a reference of the button clicked (because of the CommandParameter binding) which can then be interrogated to see if it implements ICat or IDog and if so will go to its respective process. In the example of IDog we handle the button click and update the Message property of AnimalDog as applicable.
IMPORTANT NOTE: Where you don’t have to code to interfaces (you could just as easily code to AnimalBase or specific implementation such as AnimalDog this does hamper the power of the Factory pattern, i.e., if I wanted to add a AnimalBullDog that implements IDog then all I would have to do is create the class, update my Factory (above) and add the data template below – no plumbing would have to be updated.
Where AnimalCat and AnimalDog will use its defined data templates above, any undefined instances (such as AnimalPig, AnimalCow, and AnimalBase) will default to the “AnimalBase” template. If an instance cannot find itself in a data template then only the classes full namespace is shown as a string.