Raymond Lewallen

Sponsors

The Lounge

Wicked Cool Jobs

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Refactoring pattern quiz number 1 answered

Here is how to refactor (or at least how I refactor, there are other ways) this example I posted earlier.  The class Foo can pretty much be implemented however you want.  In my case, I wanted to get the number of required tokens for a specific person using an interface.  You can see that I created in interface IPerson that contains GetNumberOfRequiredTokens.  This way I can just pass in an IPerson object type and use the polymorphed, overriden function in the child class, or adult class (each of which inherit from the Person class that implements the IPerson interface).  I implemented a property in the base class that contains the default number of tokens, and then for each derived class, I use that along with custom calculations the derived class to come up with the appropriate number of tokens required.

The refactoring pattern we are targeting here is called “conditional to polymorphism”, along with a few other names.  The goal was to refactor our initial “Select Case” into a base class with derived classes.  This increases usability and scalability greatly over our initial code we started with.

Don’t forget, and I did not demonstrate this, you will ALWAYS want to write a unit test for your initial code (Foo.GetNumberOfRequiredTokens) and make sure it passes all scenarios BEFORE beginning your refactoring process and DURING and AFTER your refactoring process.

To test this, create a new object of type Adult.  Then call Foo.GetNumberOfRequiredTokens(adultObject) and it will return 3.

Refactored: conditional refactored to polymorphism

 

Public Class Foo

 

    Public Shared Function GetNumberOfRequiredTokens(ByVal person As IPerson) As Int32

        Return person.GetNumberOfRequiredTokens()

    End Function

 

End Class

 

Public Interface IPerson

    Function GetNumberOfRequiredTokens() As Int32

End Interface

 

Public MustInherit Class Person : Implements IPerson

    Private baseTokenAmount As Int32 = 1

    Protected ReadOnly Property Tokens() As Int32

        Get

            Return baseTokenAmount

        End Get

    End Property

 

    Public MustOverride Function GetNumberOfRequiredTokens() As Int32 Implements IPerson.GetNumberOfRequiredTokens

End Class

 

Public Class Child : Inherits Person

    Public Overrides Function GetNumberOfRequiredTokens() As Int32

        Return MyBase.Tokens()

    End Function

End Class

 

Public Class Adult : Inherits Person

    Public Overrides Function GetNumberOfRequiredTokens() As Int32

        Return MyBase.Tokens * 3

    End Function

End Class

 

Public Class Infant : Inherits Person

    Public Overrides Function GetNumberOfRequiredTokens() As Int32

        Throw New TooYoungException

    End Function

End Class

 

Public Class TooYoungException : Inherits Exception

 

End Class


Posted Tue, Apr 26 2005 11:16 AM by Raymond Lewallen

[Advertisement]

Comments

John Papa wrote Refactoring - OOP Example from VB.NET to C#
on Tue, Apr 26 2005 7:50 PM
OK, I don't want to leave out the C# developers from this VB.NET OOP example that Raymond posted recently....
Raymond Lewallen wrote re: Refactoring pattern quiz number 1 answered
on Tue, Apr 26 2005 8:06 PM
Somebody asked me, why wouldn't you just call Adult.GetNumberOfRequiredTokens instead of going through Foo? Simple. You have to look at the context in which this piece of code exists. Foo isn't really foo, but maybe its FerrisWheel, which is a derived class of Ride, in which Ride is a derived class of Amusement. Maybe you persist the Person derived object from the point of login and use that to obtain specific information. GetNumberOfRequiredTokens can be made aware of the specific Ride type and that would alter the values. There are all types of reasons for doing it this way and not just calling Adult.Method(). Hope that makes sense. I try to make all my blog post easy for beginners to understand, yet valuable for seasoned .Net developers.
Ben wrote re: Refactoring pattern quiz number 1 answered
on Wed, Apr 27 2005 9:49 AM
Quick thought about this though.

To help with the thought, let's start by renaming Foo to NiceSedateFerrisWheel, and also having another class, ReallyScaryGhostTrain, that also inherits from Ride.

In the original example, each ride would implement it's own version of GetNumberOfRequiredTokens() - this means that for the nice sedate Ferris Wheel, you can say Seniors and Infants can ride it, and cost say 1 token, or however many, while for the Ghost Train, they can't ride it and will throw the exception.

However with your refactored code, the GetNumberOfRequiredTokens() method now resides in each of the Person classes, meaning that there's no way an infant or senior could ever ride on the Ferris Wheel - which probably wasn't what was intended.

I know that's why you'd therefore have the test cases, and you'd realise as soon as you did this that while they still pass for ReallyScaryGhostTrain, they are failing for NiceSedateFerrisWheel.

Just wondered how you'd handle these situations - I guess you'd need to add a flag or some such saying what class of person can ride, but that seems a bit messy to me.
Raymond Lewallen wrote re: Refactoring pattern quiz number 1 answered
on Wed, Apr 27 2005 10:22 AM
Ben,

Yes, there are issues if you start going forward with the implementation of the entire application because I didn't sit and spend the few hours it would take to figure out the overal design just so I could put up 40 or 50 lines of code. However, my intention was not to demonstrate how to create a ticketing application for carnival rides, it was merely to demonstrate how to move switch and select case statements into a more usable model. How you decide to design the overall application is up to you, and yes, doing a flag would be very messy. The design of the classes listed above would most likely be different if we were to actually take the time to develop the application.

In my last comment when I talk about not calling Adult.GetNumberOfRequiredTokens, "GetNumberOfRequiredTokens can be made aware of the specific Ride type and that would alter the values." I'm not saying that is they way it should be done or that I would do it this way. I merely stated that off the top of my head as a design issue to consider, of many, when refactoring code. Again, "There are all types of reasons for doing it this way" and I didn't take time to really figure out how because it doesn't necessarily fit in to the context of the topic of the refactoring pattern.
John Papa wrote Class Diagrams in Visual Studio.NET Beta 2
on Wed, Apr 27 2005 7:47 PM
A few days ago Raymond Lewallen blogged about refactoring some VB.NET code using interfaces, and abstract...
Patrick Steele's .NET Blog wrote The value of unit testing.
on Sun, May 1 2005 9:20 AM

Add a Comment

(required)  
(optional)
(required)  
Remember Me?
Devlicio.us