Creed, Cult, and Code

Thursday, February 5, 2009

Announcing: the Dale Smith Valued Professional (DSVP) Program

UPDATE: Scott Bellware has just made the grade as well.  Welcome to the program, Scott!

UPDATE: The DSVP program welcomes Louis Salin.  Way to go Louis!

Karl Seguin (among others) is a bit miffed about Microsoft's Most Valuable Professional (MVP) program.  Or at least mystified.  Seems that the rules of induction to the program have become a bit obscure.  Why does Microsoft have the MVP program?  According to Microsoft:

We present the MVP Award to thank individuals for their exceptional contributions to technical communities worldwide. When a community participant sees an MVP in a technical community, whether in a newsgroup, as a user group host, a conference speaker, or a respondent in forums, that community participant can be confident that the information shared by the MVP will be of the highest caliber and will help every user make the most of the technology.

So what kind of community contributions does Microsoft consider "exceptional"?  What kind of information shared by MVPs in various forums does Microsoft consider to be "of the highest caliber"?  I don't know - they don't really spell it out.  But Microsoft is a publicly traded company, so I'm sure their primary goal is providing value for their shareholders.  If I were in Microsoft's shoes I would consider information that benefits me in achieving that goal to be "exceptional" and "of the highest caliber".

I'm not in Microsoft's shoes.  In fact, I'm not wearing shoes at all (I'm from Mississippi).  And I have my own goals, chief among which is providing value for my employer and my customers so that I can earn a paycheck (hey, a guy's gotta eat).  As it turns out, Karl Seguin has helped me achieve my goals over and over again by his activity in the community, as have many others.  Those folks deserve an award!

So I hereby institute: <insert fanfare here> the DALE SMITH VALUED PROFESSIONAL (DSVP) PROGRAM.  Why do I have the DSVP program?

Dale Smith presents the DSVP Award to thank individuals for their exceptional contributions to technical communities in which Dale Smith participates or trolls. When a community participant sees a DSVP in a technical community, whether in a newsgroup, as a user group host, a conference speaker, or a respondent in forums, that community participant can be confident that the information shared by the DSVP will be that which Dale Smith considers to be of the highest caliber and will help Dale Smith make the most of the technology.

Here are some questions you're probably asking yourself:

How do I become a Certified DSVP?

Well that's really kind of up to me, Dale Smith (see above).  If you would like to be considered for the program, feel free to apply via the comment box below.  We will consider your application carefully, and my legal team will contact you when a decision has been reached.  I guess the main thing to remember here is this: some are born great, some achieve greatness, and others have greatness thrust upon them.

What does being a Certified DSVP get me? 

First of all: Once a DSVP, always a DSVP.  None of this you-have-to-renew-once-a-year stuff.  That's just the kind of guy I am.  (To be honest with you, this one is more about me avoiding paperwork than anything else.)

Second: I'll give you a warm smile and a hearty handshake if I ever run into you at a conference or something.

Third: You will have the right to use the letters D, S, V, and P after your name, like so -

Dale Smith, DSVP

See?  Pretty spiffy, huh?  If you use this baby after your name, you better believe that folks will know that when they see you in a technical community, whether in a newsgroup, as a user group host, a conference speaker, or a respondent in forums, that Dale Smith thinks you are one righteous dude.

Last, but not least:  You're welcome to use this nifty badge on your blog, business cards, and any other self-promotional marketing materials you desire:

So why is the badge a cassette tape?

Microsoft is a lot cooler than I am.  No, seriously, they really are.  Granted, they sometimes exhibit some bewildering behavior in the way that they engage the development community.  But the fact is, their achievements both in business and technology far outshine mine.  So receiving an award from them, compared to an award from me, would be like getting the entire recorded output of the Replacements on pristine vinyl, still in the original unopened shrink wrap, plus all Paul Westerberg's and Chris Mars's solo material, records from Tommy Stinson's post-Mats bands Bash-n-Pop, Perfect, and even the new one from Guns 'n' Roses.  While we're at it, let's throw in a high-quality bootleg of whatever you can find from Bob Stinson's post-Mats band Static Taxi.

In contrast, getting an award from me is like getting a self-made cassette from a local unsigned band.  Nothing earth-shattering, mind you, but certainly earnest, sincere, charming.  Definitely has its moments.  And twenty years from now you'll probably run across it again in a box of long-forgotten tapes, and you'll throw it in the deck and think, "Oh yeah!  I remember this!  This was actually pretty cool!"

 

And now I'm going to induct Karl Seguin as the very first Certified DSVP.  So here goes:

Yea and verily, be it known henceforth and forevermore that Karl Seguin is hereby Certified as a Dale Smith Valued Professional (DSVP) in the region of Ottawa, Ontario.  Oh, what the heck!  Let's make Karl's jurisdiction all of Canada!  Congrats, Karl!

Share this post :

Monday, October 13, 2008

Refactoring Legacy ASP.NET Code-Behind with ReSharper

I've been on a refactoring kick lately.  And with the state of the legacy code at my office, I'll be refactoring for a very long time to come.  This is a write-up of a refactor I did at work, altered enough so that I don't violate my NDA.  But I think there's enough of the real story left to illustrate what I was talking about last time: you can indeed beautify even the ugliest code.  A couple of items to note: 1) we're still on .NET 2.0 (no we haven't migrated to 3.5 yet - don't ask!), and 2) I'm not going to touch on testing much in this post.  I really want to focus on the fact that you can positively affect some seriously messed up code with some pretty simple steps.

ParticipantDetails Refactor

We have a legacy ASP.NET web app that has some very tangled code-behind pages which need to be addressed.  There are lots of items on the legacy app development wish list.  Near the top, we have a strong desire to pull .NET 1.1 custom components out of our legacy app and replace them with our .NET 2.0 custom components.  It would be wonderful if we only had to switch references to affect this change, but there are too many barriers in our code to a solution that simple.  Another wish list item is testability, which means our code has to be restructured in such a way as to make it easily testable.  If we tried to do all of this at one time we would most assuredly fail.  Instead, we have to approach the problem with a series of smaller steps with an eye toward refactoring large pages over multiple iterations.

The ParticipantDetails.aspx.cs file is a good example of a code-behind file that has grown large and cumbersome: it has 3384 lines of code in it.  It has many long methods in it, the longest of which is GetParticipant() at nearly 900 lines.  This method essentially does three things:

  1) fetches participant info from the database,

  2) formats that data for display, and

  3) assigns formatted data to the appropriate controls on the page. 

To deal with this page, and particularly long methods like GetParticipant(), I approached it with three goals for this iteration:

  1) make the methods much smaller, each focused on a single task, and therefore more easily readable and maintainable,

  3) move the persistence coupling entirely out of this page, and instead couple the page to a set of Data Transfer Objects, and

  2) apply the MVP pattern to this page to move as much code as possible into more testable classes.

I used ReSharper extensively for this refactor, particularly the Extract Method feature.

Small Methods

At 900 lines, GetParticipant() is extremely difficult to read, and even more difficult to modify correctly.  It's easy to believe that the primary purpose of writing code is to allow the developer to give operational commands to a computer.  We have to change our thinking here: that's the secondary purpose.  The code we write is a living document that contains business information.  The primary purpose I have when writing code is to create a document that allows me and you and everyone of our developers to communicate about how our company does business.  A 900 line method conceals that information by adding noise.  We reduce that noise and strengthen the real message of our code by separating concerns into multiple methods and classes.  (By the way, this has the nice side effect of breaking dependencies and reducing coupling, which will make our code much easier to change in the future.)

Granted, we will end up with "class explosion".  But the alternative is what we have now: "method bloat".  A code base with many small classes containing small methods is much easier to maintain than a few huge classes with very long methods.  How long is too long?  I like to be able to see every line of a method on one screen.  I have a rule of thumb called the "head rule": if I can't fit the whole method in my head, it's too long.  How much can I fit in my head?  Well, if I'm looking at the method on my screen and I hold my head next to it, if the method is longer than my head then it's too long. J

Data Transfer Object (DTO)

A Data Transfer Object (DTO), as the pattern is described by Martin Fowler, is "an object that carries data between processes in order to reduce the number of method calls".  A DTO contains nothing but simple properties, no business logic inside the getters and setters for each property, and no methods at all.  I used ReSharper to help me create a DTO called ParticipantDetailsDTO. 

I started by declaring an ParticipantDetailsDTO variable at the top of the method.  ReSharper immediately notices that I don’t have a class called ParticipantDetailsDTO in my solution and turns the new class name red.  When put my cursor on the class name and click Alt+Enter, a menu appears that offers to create a new ParticipantDetailsDTO class for me.

 

I lucked out a bit when it was time to fill out the DTO.  Most of the controls on the page are hydrated from private variables rather than directly from the Business 1.1 objects.  My next step was to delete the declarations of those private variables.  When I did that, ReSharper turned the variable names red wherever they appeared in the method.

.

.

.

Then, I prefixed every bad variable with “participantDetailsDTO”.  Resharper asked me if I wanted to create a new property in ParticipantDetailsDTO called “strAsstEmail”.  I took Resharper’s advice, and I worked my way through the whole method this way, filling out the ParticipantDetailsDTO class with new properties as I needed them.  I also changed the property names to make them a bit more readable: e.g., strAsstEmail became AssistantEmail.  Since the participant concept contains collections of other things (like Groups, Jobs, Specialties, etc.), I ended up needing several other DTOs.  ParticipantDetailsDTO now contains List<T> properties for those collections.  At the end of this process, all the controls on the page were dependent on DTOs rather than individual private variables.  I had created a seam - I'm sure I'm not using that term as strictly as Michael Feathers intended, but I think the idea is similar: a specific place in my class meant to be moved or otherwise modified without affecting the rest of the class.

From there, still in the page code-behind, I started pulling similar code together: anything that was concerned with hydrating participantDetailsDTO got moved into a separate method.  Same with all the other DTOs: each got its own method.  This was a bit tricky.  Sometimes I ran into code that decided how certain property values should be set.  I had to be careful to preserve this code.  But at the end of it, I had a number of methods that were all about hydrating the participantDetailsDTO and its collections.  So I put them all into a class called ParticipantDetailsDTORepository and moved that class into the Vega.Common assembly.  I also created an interface for it called IParticipantDetailsDTORepository – more about that in a minute.  I ended up with something like this:

 

This is just a quick sketch, but I think you can see the bones of the design.  This implementation of the IParticipantDetailsDTORepository works only with the .NET 1.1 components.  IParticipantDetailsDTORepository contains a method called GetParticipantDetailsDTO(int participantId).  When the time comes to switch this page over to the .NET 2.0 Business Components, we’ll write a new implementation of this repository interface that pulls participant info from the BC components.  That should be a goal in our next iteration.

Model View Presenter (MVP)

So we’ve moved our participant fetch logic out of the code-behind.  And obviously the code that assigns values to page controls has to stay in the code-behind (or does it?).  But I see no reason for the data formatting code to remain here.  This is where the model view presenter (MVP) pattern comes in.  I declared an empty IParticipantDetailsView and made the page inherit from it.  I also created a ParticipantDetailsPresenter class in Vega.Common – this is where I put all the formatting logic, and pretty much any other logic that wasn’t directly concerned with hydrating page controls.  I made heavy use of Resharper’s Extract Method feature here.  So now we also have something like this:

It is actually possible, and even desirable, to move control-hydration code into the presenter, but I didn’t go that far.  I would love to go that direction and show it all under unit tests, but I didn’t have time in this post. 

We're not done yet.  There is still much more we can do to simplify this page.  But the result so far is that the persistence-related code has been moved out into testable classes, the ParticipantDetails code-behind is now about 1200 lines instead of more than 3300, the code is much easier to read and understand, and we have an upgrade path to more easily switch out our persistence mechanism when we're ready.  Next time, I'll talk about testing to detect change to ensure that the DTO built from the new persistence mechanism is the same as from the original mechanism.

Share this post :

Saturday, September 27, 2008

NDepend, or a Young .NET Programmer's Illustrated Primer

Bootstraps

I have said before that I have no axe to grind against Microsoft, and I stand by that.  They set out long ago to have a computer on every desktop and Microsoft software running on every computer.  They have gone a long way towards achieving that goal, and in the process they have brought value to more people than I can count.  Since their inception they have produced operating systems, languages, and development tools that have allowed people without computer science backgrounds to learn how to program computers to do actual stuff. 

Now, granted, Microsoft has promoted development techniques that are much more in line with their own marketing plans than with sound design principles.  But I cannot think of a time during Microsoft's existence when information about sound design principles was unavailable, or when Microsoft has ever actively sought to squelch that information.  And I'm old.

I guess my point is this: Microsoft has produced some really nice tools, and it's up to me as a developer to use those tools wisely.  But it's also up to me to find other tools and techniques that help me to be more effective as a developer.  I feel a constant call to improve myself as a developer.  I also feel a call to watch old Twilight Zone episodes on YouTube.  But that first call is still there, and I respond to it too.  I certainly benefit from my efforts to learn and grow, and my team benefits from my efforts as well.  And ultimately, so does my company.

Many companies I have worked for have thought of their developers as commodities, particularly those who use Microsoft technologies.  Approaching hiring, training, and project management practices from a commodity perspective has lead to commodity behavior in many developers: one MCP is as good as another; it Microsoft didn't invent it, it can't be good; we're all "human resources" rather than "personnel". 

If you're in this situation, I have good news for you: you can take the red pill.  You don't have to wait for your employer to educate you about your chosen profession.  You can take the grass by the roots and educate yourself.  There are tons of resources out there for you.  One of those resources is a great little tool called NDepend.

 

A Real-World Example

The purpose of NDepend is to give you views into the actual structure of your software, regardless of how you intended to design it, that you would otherwise be unaware of.  By blindly following the herd rather than thinking about long term design consequences, we often end up with some silly, expensive situations.  NDepend can help us pinpoint the details of those situations so that we can address them.

A while back Chad Myers posted about the hazards of having WAAAAAAYYYYYY too many projects in a single solution file. I have wanted to write a post on NDepend for a while now.  Others have already written better overviews than I could produce.  And the NDepend tool itself ships with a whole host of how-to's in its documentation.  Instead, I want to write about how we use NDepend at my office to address problems like those Chad describes, and that will probably take more than one post.

At my office we definitely suffer from the malady Chad describes.  Our problem started innocently enough: years ago, we thought that we should package our API so granularly that we could deploy different pieces of it in an easy and atomic way.  Well, it has proven not to be easy, but it is certainly atomic.  And not in the good way.  As we have added to our codebase over the years, we kept following the idea of many granular assemblies, but we somehow lost sight of the goal of easy deployment, and we ignored our need for maintainability.  We now have a single solution file for assemblies meant to be shared among multiple applications that has nearly 150 projects in it.  NEARLY 150!!!  How do you even start to address a situation like that?  Step 1 is admitting you have a problem.  So first, we'll consolidate our assemblies, and manage our code with namespaces and project folders.  But that's the subject of another post.

Step 2 is finding dead code and eliminating it, and that's the subject of this post.  My problem here is that I'm under an NDA, so I can't show you details of the real situation, although I would love to.  Instead here's a very trivial example to illuminate a couple of concepts: download.  At work, I've got a (HUGE!) set of shared assemblies, and a couple of apps that consume those assemblies.  So my example contains code meant to represent a situation like that: the CouplingExample assembly is the stand-in for the shared assemblies, and the CouplingExample.ConsoleApplication and CouplingExample.UnitTests assemblies represent the apps that depend on our shared assemblies.

NDepend ships with a SQL-like scripting language called Code Query Language (CQL).  CQL allows you to write custom queries about the assemblies you're analyzing based on several different kinds of code metrics.  NDepend ships with a bunch of pre-built CQL queries to give you a good starting place for cracking into your code.  Since I'm trying to kill dead code, the metric I'm particularly interested in is afferent coupling, or actually the lack of it.  For CouplingExample, I want to know which methods in the CouplingExample assembly have no methods that depend directly on them.  I'm interested in non-public methods - it's a pretty safe bet that any non-publicly accessible method in my assembly that has nothing depending on it is dead code, and can therefore be eliminated.  But I'm also interested in public methods with an afferent coupling of zero.  Since I know all the applications that consume my shared assemblies, I can include those app assemblies in the same NDepend project.  I can narrow down the scope of my CQL query just by including an ASSEMBLIES list.  I swiped NDepend's canned "Potentially unused methods" query, and bent it to my own bidding like so:

   1: // <Name>Potentially unused shared assembly methods</Name>
   2: WARN IF Count > 0 IN SELECT TOP 10 METHODS FROM ASSEMBLIES "CouplingExample"  WHERE 
   3:  MethodCa == 0 AND            // Ca=0 -> No Afferent Coupling -> The method is not used in the context of this application.
   4:  !IsEntryPoint AND            // Main() method is not used by-design.
   5:  !IsExplicitInterfaceImpl AND // The IL code never explicitely calls explicit interface methods implementation.
   6:  !IsClassConstructor AND      // The IL code never explicitely calls class constructors.
   7:  !IsFinalizer                 // The IL code never explicitely calls finalizers.
 
My NDepend project contains the shared assembly and the application assemblies that consume it.  But this query only looks at methods in the CouplingExample assembly.  When I run the query, NDepend paints a pretty picture of my code for me.  Here's a context map showing all the assemblies I analyzed, and highlighting the methods in the CouplingExample assembly that have no other methods depending on them, either internally inside CouplingExample, or externally from the UnitTests and ConsoleApplication assemblies:
 
coupling_example_ndepend
 
 
You can easily see which methods have no other methods depending on them, the ones in bright blue (and conveniently named "Public_no_afferent_coupling()" and "Private_no_afferent_coupling()"). Back in real life, I used the query above with all 140+ assembly names in it pointed at my shared assemblies and the consuming app assemblies.  Here's a scrubbed version of the resulting context map:
 
unused_methods_NDA
 
Look at that!  Tangled up in blue!  Literally THOUSANDS of lines of code we can just drop.  What a great feeling.  I ran this picture by people in our group who, unlike me, actually have authority to buy stuff.  Almost immediately, they put a line item in the budget for 10 NDepend licenses.  This thing sells itself.  And I feel confident that once more of our developers have begun to use it, we'll end up buying a license for everyone in the group.
 
This one little query for dead code is just the very top snowflake on the tip of the iceberg.  NDepend gives you a myriad of insights into how your code actually works, empowering you to make it better, and educating you on how to create better designs in the future.  A really cool feature of NDepend is its integration with Visual Studio: if you include the .pdb files in the NDepend project, you can just double-click on a method to bring it up in Visual Studio.  I'm a ReSharper nut - I think Patrick Smacchia hit the nail on the head when he said that what ReSharper does for you on a micro-level, NDepend does for you on a macro-level.  But the code metric analysis NDepend offers also puts it in a different realm than a refactoring tool like ReSharper.  I think these two tools make a very powerful team together.  Next time, I'll talk a bit about how we are beginning to use ReSharper to address situations like this one that NDepend illuminated for us.
 
We are now (finally!) giving attention to cleaning up messy old code.  As we move along, I hope to write about other examples of how we change our code after examining it with NDepend, so stay tuned.  This kind of stuff isn't only for the uber-geeks, it's for the day-to-day programmer who wants to improve skills, improve code, and deliver better products.
 
Share this post :
Technorati Tags: ,,,,

Saturday, August 16, 2008

Boba Fett, Greedo, and StructureMap

"Austin, Texas. You will never find a more wretched hive of scum and villainy. We must be cautious."

These words were uttered by an inhabitant of the the wastelands around Austin.  Shrouded in mystery, most people think he's nothing more than a crazy old wizard.  He keeps to himself mostly, occasionally seen associating with the lowest sort of riff-raff, and forever spouting mumbo-jumbo about some bizarre religion he follows.  But there are those who know his true identity, that he is in fact the legendary Jedi Obi-Wan Kejeremy.

Ahhh, forget it.  I just can't keep up the extended metaphor anymore.  In keeping with my last post, I was going to go into some long, labored Star-Warsy exposition about Jeremy's new padawans, Chad and Josh, and the work they've done with him on a secret weapon called StructureMap.  But it's late, I'm tired, and I want to finish this post tonight, so let's just skip right to the point, mmmmm-kay?  I took the code I wrote for the last post and used StructureMap to decouple things a bit further after reading through Chad's articles (here and here) as well as a bunch of stuff Jeremy has written about StructureMap.  Since there are still those among us who are fashionably late to the .NET 3.5 party, I used StructureMap 2.0.  I also thought a bit more about the design and changed some things around after looking at Josh's code (and, let's be honest, outright stealing some of his code).  And no, I didn't write tests for this stuff, and yes, I know that makes me a lazy jerk.  See sentence four in this paragraph.

So here's how the Program class changed:

   1: class Program
   2: {
   3:     private static void Main(string[] args)
   4:     {
   5:         Configure();
   6:  
   7:         IAppEngine appEngine = ObjectFactory.GetInstance<IAppEngine>();
   8:         appEngine.Run();
   9:     }
  10:  
  11:     private static void Configure()
  12:     {
  13:         StructureMapConfiguration.UseDefaultStructureMapConfigFile = false;
  14:         StructureMapConfiguration.BuildInstancesOf<IAppEngine>().TheDefaultIsConcreteType<AppEngine>();
  15:         StructureMapConfiguration.BuildInstancesOf<IOutputDisplay>().TheDefaultIsConcreteType<ConsoleOutputDisplay>();
  16:         StructureMapConfiguration.BuildInstancesOf<IStuffStrategy>().TheDefaultIsConcreteType<LitterOfKittens>();
  17:         StructureMapConfiguration.BuildInstancesOf<IDrinkingEstablishment>()
  18:             .TheDefaultIs(
  19:             Registry.Instance<IDrinkingEstablishment>()
  20:                 .UsingConcreteType<Cantina>()
  21:                 .WithProperty("name").EqualTo("Mos Eisley Cantina")
  22:             );
  23:     }
  24: }

I love it!  That Configure() method allowed me to break almost every dependency.  The only time I'm actually using the new keyword is in the AppEngine (lines 15 and 22), and if I weren't falling asleep I could probably figure out a way to get rid of it there too.

   1: public class AppEngine : IAppEngine
   2: {
   3:     private IDrinkingEstablishment _drinkingEstablishment;
   4:     private IOutputDisplay _outputDisplay;
   5:  
   6:     public AppEngine(IDrinkingEstablishment drinkingEstablishment, IOutputDisplay outputDisplay)
   7:     {
   8:         _drinkingEstablishment = drinkingEstablishment;
   9:         _outputDisplay = outputDisplay;
  10:     }
  11:  
  12:     public void Run()
  13:     {
  14:         GetANewPatron("Boba Fett");
  15:         _drinkingEstablishment.SetStuffStrategy(200, new ConfusionOfWeasels());
  16:         GetANewPatron("Greedo");
  17:         _outputDisplay.Get();
  18:     }
  19:  
  20:     private void GetANewPatron(string name)
  21:     {
  22:         IPatron patron = new BountyHunter(name);
  23:         _drinkingEstablishment.WelcomePatron(patron);
  24:         foreach (IStuff thing in patron.Basket)
  25:             _outputDisplay.Put(thing.DoSomething(patron));
  26:     }
  27: }

So, without further adieu, you can download my little experiment and peruse it to your heart's content if you just click right here.  Laugh at it all you want to.  I'll be asleep.

Share this post :

Boba Fett, Greedo, and the Strategy Pattern

bobafett Bounty hunting is a difficult line of work.  Think about it:  1) Jobs are generally consulting gigs rather than full-time employment.   2) You're brought in at the last minute, usually to clean up someone else's mess, under a lot of pressure to save the day.  3) There are long hours, difficult problems to solve, and any benefits or vacation days come out of your own back pocket.  So give Boba Fett and Greedo a little respect.  They're willing to go flying off to the seedy backwaters of the galaxy, at great personal expense, mind you, turning over every stone until they find the Rebel scum they've been told to bring in.  All because the megalomaniacal creeps they work for run huge bureaucratic organizations filled with nothing but boot-licking sycophants and lackeys who can't seem to catch one measly smuggler and his smelly walking-carpet sidekick.  And if they should happen to run across this smuggler, who knows what kind of lethal stunt this half-witted, scruffy-looking nerf herder is liable to pull to get away from them.  Here's just one example:

image 

Shocking, isn't it?  I know.  It's called the StrategyPattern, and if you're not looking out for it, well you might just end up with some brand new ventilation courtesy of Han's blaster.  Here's how it works:

Let's say you find yourself in a particular context, like, say, a cantina on a desert planet.  Bartender gives you a drink, and you're parched, so you reach for it.  But wait!!  Is that beaker really filled with good old Membrosia?  How do you know nobody has slipped the bartender a couple hundred galactic credits along with a bottle marked "Membrosia", but which is really heavily spiked with carsunum?  What does the bartender care?  All he knows is he gets a free bottle and two hundred credits out of the deal - makes no difference to him.  People keel over in his place all the time.  Seems like a pretty good strategy for knocking you off, wouldn't you say?

Here's another strategy pattern scenario I bet you didn't think about:

image

Boba Fett is a bounty hunter. 

   1: public class BountyHunter : ICantinaPatron
   2: {
   3:     private string _name;
   4:     private IList<IStuff> _basket = new List<IStuff>();
   5:  
   6:     public BountyHunter(string name)
   7:     {
   8:         _name = name;
   9:     }
  10:  
  11:     public string Name
  12:     {
  13:         get { return _name; }
  14:     }
  15:  
  16:     public IList<IStuff> Basket
  17:     {
  18:         get { return _basket; }
  19:     }
  20: }

He happens to walk into the Mos Eisley cantina on a day when they're giving away stuff to all the cantina patrons: today, it's fuzzy animals. (Don't ask me.  Some corporate marketing idiot from the cantina's Membrosia distributor thought it would be a good promotional gimmick.) 

   1: public class Cantina
   2: {
   3:     private int _bribe;
   4:     private StuffStrategy _stuffStrategy = new LitterOfKittens();
   5:  
   6:     public void AcceptBribe(int bribe)
   7:     {
   8:         _bribe = bribe;
   9:     }
  10:  
  11:     public void SetStuffStrategy(StuffStrategy stuffStrategy)
  12:     {
  13:         if (_bribe > 100) _stuffStrategy = stuffStrategy;
  14:     }
  15:  
  16:     public ICantinaPatron GetPatron(string name)
  17:     {
  18:         ICantinaPatron cantinaPatron = new BountyHunter(name);
  19:         _stuffStrategy.GetStuff(cantinaPatron);
  20:         return cantinaPatron;
  21:     }
  22: }
  23:  
  24: public abstract class StuffStrategy
  25: {
  26:     public abstract void GetStuff(ICantinaPatron cantinaPatron);
  27: }
  28:  
  29: public interface IStuff
  30: {
  31:     string DoSomething(ICantinaPatron patron);
  32: }
  33:  
  34: public interface IFuzzyAnimal : IStuff
  35: {
  36:     string Name { get; }
  37:     string MakeNoise();
  38:     string Play();
  39: }
  40:  
  41:  

So when Boba Fett takes his basket of stuff from the barkeep and opens it up to see what he got, he discovers a litter of adorable kittens named Blossom, Minty, Butterscotch, Cotton Candy, Blue Belle, Snuzzle, and Rainbow Dash.

   1: public class Kitten : IFuzzyAnimal
   2: {
   3:     private string _name;
   4:     private string _playmate;
   5:  
   6:     public Kitten(string name)
   7:     {
   8:         _name = name;
   9:     }
  10:  
  11:     public string Name
  12:     {
  13:         get { return _name; }
  14:     }
  15:  
  16:     public string MakeNoise()
  17:     {
  18:         return
  19:             "Hi, " + _playmate + "!  My name is " + _name +
  20:             ", and I'm a kitten!  Meow!  prrrrr  prrrrrrrrrrrr";
  21:     }
  22:  
  23:     public string Play()
  24:     {
  25:         return _name + " blinks her big grey eyes and paws " + _playmate + "'s finger adorably.";
  26:     }
  27:  
  28:     public string DoSomething(ICantinaPatron patron)
  29:     {
  30:         _playmate = patron.Name;
  31:         return MakeNoise() + "\r\n" + Play() + "\r\n";
  32:     }
  33: }
  34:  
  35: public class LitterOfKittens : StuffStrategy
  36: {
  37:     public override void GetStuff(ICantinaPatron cantinaPatron)
  38:     {
  39:         cantinaPatron.Basket.Add(new Kitten("Blossom"));
  40:         cantinaPatron.Basket.Add(new Kitten("Minty"));
  41:         cantinaPatron.Basket.Add(new Kitten("Butterscotch"));
  42:         cantinaPatron.Basket.Add(new Kitten("Cotton Candy"));
  43:         cantinaPatron.Basket.Add(new Kitten("Blue Belle"));
  44:         cantinaPatron.Basket.Add(new Kitten("Snuzzle"));
  45:         cantinaPatron.Basket.Add(new Kitten("Rainbow Dash"));
  46:     }
  47: }

Here's what happens when Boba Fett plays with each kitten, and it's just the cutest thing you've ever seen.  Almost brings a tear to your eye:

image

greedo Greedo is also a noble bounty hunter, but he has a dark secret: he just loves fuzzy, adorable kittens!  He's so excited when he sees what Boba Fett got that he completely forgets about the Strategy Pattern!  Oh no!  Greedo beware!  While Greedo is distracted, Han has slipped the barkeep a bribe along with another group of "fuzzy animals":

   1: internal class Program
   2: {
   3:     private static Cantina _cantina;
   4:  
   5:     private static void Main(string[] args)
   6:     {
   7:         _cantina = new Cantina();
   8:  
   9:         GetANewPatron("Boba Fett");
  10:  
  11:         _cantina.AcceptBribe(200);
  12:         _cantina.SetStuffStrategy(new ConfusionOfWeasels());
  13:         
  14:         GetANewPatron("Greedo");
  15:  
  16:         Console.Read();
  17:     }
  18:  
  19:     private static void GetANewPatron(string name)
  20:     {
  21:         ICantinaPatron patron = _cantina.GetPatron(name);
  22:         Console.WriteLine(patron.Name + " enters the cantina");
  23:         foreach (IStuff thing in patron.Basket)
  24:             Console.WriteLine(thing.DoSomething(patron));
  25:     }
  26:  
  27: }
  28:  
  29: public class Weasel : IFuzzyAnimal
  30: {
  31:     private string _name;
  32:     private string _playmate;
  33:  
  34:     public Weasel(string name)
  35:     {
  36:         _name = name;
  37:     }
  38:  
  39:     public string Name
  40:     {
  41:         get { return _name; }
  42:     }
  43:  
  44:     public string MakeNoise()
  45:     {
  46:         return
  47:             "Hi, " + _playmate + "!  My name is " + _name +
  48:             ", and I'm a weasel!  SSHHHHREEEIIIIKKKK!!!  HISSSSSSSS!!!!!";
  49:     }
  50:  
  51:     public string Play()
  52:     {
  53:         return _name + " rips " + _playmate + " a new orifice.";
  54:     }
  55:  
  56:     public string DoSomething(ICantinaPatron patron)
  57:     {
  58:         _playmate = patron.Name;
  59:         return MakeNoise() + "\r\n" + Play() + "\r\n";
  60:     }
  61: }
  62:  
  63: public class ConfusionOfWeasels : StuffStrategy
  64: {
  65:     public override void GetStuff(ICantinaPatron cantinaPatron)
  66:     {
  67:         cantinaPatron.Basket.Add(new Weasel("Lefty"));
  68:         cantinaPatron.Basket.Add(new Weasel("One Eye"));
  69:         cantinaPatron.Basket.Add(new Weasel("Sanchez"));
  70:         cantinaPatron.Basket.Add(new Weasel("Scar"));
  71:         cantinaPatron.Basket.Add(new Weasel("Ashes"));
  72:         cantinaPatron.Basket.Add(new Weasel("Blade"));
  73:         cantinaPatron.Basket.Add(new Weasel("Rainbow Dash"));
  74:     }
  75: }

What does poor Greedo get in his basket?  That's right: a really nasty confusion of weasels (that's the right term for a collection of weasels; I looked it up) named Lefty, One Eye, Sanchez, Scar, Ashes, Blade, and (oddly enough) Rainbow Dash!  Greedo flew halfway across the galaxy to bring Han back to Jabba, but instead here's what he gets for his troubles:

image

So what's the morale of this tale?  If you're a galactic bounty hunter and you ever find yourself in a desert planet cantina that's giving away baskets of fuzzy animals, be super careful, because what you think is gonna be a litter of kittens might just turn out to be a confusion of weasels.  And, oh yeah, the Strategy Pattern is a good way to achieve some nice decoupling in your design.  But seriously, don't forget about the weasel part.

Share this post :