Random Thoughts of a Scatterbrain.
 Monday, October 01, 2007

The Slow Death of DRM

10/1/2007 12:27:50 PM (Eastern Daylight Time, UTC-04:00)

I've been in a somewhat heated debate with my once CEO and now VP regarding the effectiveness (or rather, the ineffectiveness) of DRM and how the media companies are really just screwing themselves (whilst also screwing customers) by not adapting and accepting digital as this generation's radio.

I've always held the stance that DRM is a useless encumbrance to legitimate users of the content while providing merely a false sense of security to the copyright holders; those who want the content bad enough will circumvent the DRM somehow.  In the end, regardless of how good the DRM is, the simple fact is that the end product must be output at some point in time.  The content can always be captured as output from some trusted system (though some quality may be sacrificed).

Time and again, we've seen that the application of DRM is a fruitless effort in the cat and mouse game with hackers that the hackers have won every time.  Witness:

In his open letter, Steve Jobs comments on DRM and states:

Imagine a world where every online store sells DRM-free music encoded in open licensable formats. In such a world, any player can play music purchased from any store, and any store can sell music which is playable on all players. This is clearly the best alternative for consumers, and Apple would embrace it in a heartbeat. If the big four music companies would license Apple their music without the requirement that it be protected with a DRM, we would switch to selling only DRM-free music on our iTunes store. Every iPod ever made will play this DRM-free music.

Why would the big four music companies agree to let Apple and others distribute their music without using DRM systems to protect it? The simplest answer is because DRMs haven’t worked, and may never work, to halt music piracy. Though the big four music companies require that all their music sold online be protected with DRMs, these same music companies continue to sell billions of CDs a year which contain completely unprotected music. That’s right! No DRM system was ever developed for the CD, so all the music distributed on CDs can be easily uploaded to the Internet, then (illegally) downloaded and played on any computer or player.

I tend to think that a technology visionary like Jobs "gets it".  He understands that it is quite likely that no perfect DRM system can ever be created but in an effort to use these imperfect DRM systems, the only people that are being punished are legitimate consumers of the media by being locked into proprietary stacks of players, online stores, and digital media.

He also touches upon an oft ignored point: the CD, a digital source, itself does not contain any form of DRM.  It's true that the designers of the format perhaps did not foresee a world digitally connected and able to distribute 650MB worth of data in mere seconds (BitTorrent),  but that does not absolve the fact that they're plugging the crack in the dam while ignoring the gaping hole.

It is my view that the actual number of people who actually rip and distribute music from CDs and DVDs are a very small percentage of all consumers.  Meanwhile, there is a much larger percentage of consumers who get their copies illegally from these sources via peer to peer and file sharing networks.  And yet a larger percentage of people are actual legitimate consumers who plunk down the full price of the CD or DVD in stores and take it home with them.

What the music industry should be concerned about is not that marginal percentage of sources (those who hack the DRM systems or use the resultant software to rip and distribute the content - this group will continue to do so, indefinitely), but the much larger portion of the consumer population that illegally downloads the output of these providers even in the face of the minor threat of legal action.  The question of course is how they can reach this consumer (or at least a large proportion of this consumer).

The secret seems to be offering a "fair deal" to the consumers.  I can still remember the days when CD singles cost $5, 6, 7, even 8 dollars!  Of course, what is "fair" is arbitrary and, as my VP would say, "determined by the market" (what he seems to disregard is that the music industry was guilty of price fixing to artificially inflate the cost of CDs instead of allowing for the market to decide the fair price), but clearly, this price seems absurd!  Of course, then the question is, what is fair?  Is iTunes' $0.99 model, "fair"?  It's difficult to say since "fair" is relative to the consumer.  To some, $25,000 is "fair" for a cell phone while to others, $250 seems absurd for a cell phone (obviously different products, paying for brand, etc.; but the essence is that they are functionally equivalent in damn near every way).  Price is not the only factor: consumers, as Jobs noted, expect that once they've paid for the content, they can reuse it (not redistribute it) in their cars, on their cell phones, on their portable music players, and so on.  In the consumer's eyes, DRM is but a nuissance driving them to find DRM free, illegally distributed versions of the content.

Today's news that's buzzing around the Internet community is the upcoming release of Radiohead's next album.  Most of the buzz centers around the fact that this is the first major artist/group to release their music completely independently...no music labels involved.  Not only that, this is the first mass live experiment in determining "fair" pricing in terms of music and media:

From Time:

There's no label or distribution partner to cut into the band's profits — but then there may not be any profits. Drop In Rainbows' 15 songs into the on-line checkout basket and a question mark pops up where the price would normally be. Click it, and the prompt "It's Up To You" appears. Click again and it refreshes with the words "It's Really Up To You" — and really, it is. It's the first major album whose price is determined by what individual consumers want to pay for it. And it's perfectly acceptable to pay nothing at all.

It will be an interesting experiment indeed; the results of which, if shown to be successful, will shake the music industry to the core as other artists start to adopt the model.  The music industry has been put on notice: adapt or die.

Will Radiohead be successful?  Will they earn a dime?  One thing is for sure, they will gain a new audience of listeners who would otherwise not have been willing to purchase a CD for $16.00, but will surely download and sample the new tracks for free or for a nominal price.  But from this, it's easy to predict that Radiohead will surely increase sales of their previous albums as a new set of listeners discover the group because they've opened their content to the consumer.

It is the same with Internet radio stations, where the absurdity over the proposed rates to be paid by Internet radio stations was just recently put on hold.  In the age of HD radio broadcasts and radio-to-computer devices, what sense did it make to treat Internet radio any differently from traditional FM radio and even satellite radio?  Like traditional broadcast radio, Internet radio serves the same purpose in that it allows consumers to discover artists that would otherwise not have been given a glance (every CD I've purchased in the last 5 years has been a result of hearing the artist or group on an Internet radio station first).  It's simply that I'd rather listen to music from my computer than from my stereo.  To the consumer, the nuances of distribution and control of the media are irrelevant: the consumer just wants to listen to the music and it's really no different than an HD radio broadcast.

The media companies need to adapt and embrace technology.  They need to study how consumers want to use the content.  They need to understand that the old models won't work anymore in a connected world where content is expected to be transferrable with little hassle and reusable by the consumer (just as a CD should play in your car, in your desktop stereo, on your computer, or from a portable CD player (do people still use those?)).

 Saturday, September 29, 2007

Less Painful Windows Service Development

9/29/2007 12:11:08 PM (Eastern Daylight Time, UTC-04:00)

When developing Windows services applications, one of the most painful aspects is testing.

Sure, you can test individual component libraries separately with unit tests, but what about deploying and testing the system in an actual runtime environment?  What if your components are dependent on live communications (for example, two components that communicate via TCTP/IP bindings in WCF)?  You could use mocks, but at some point, testing the interactions of the full system will be necessary.

Typically, this is a painful process of either using installers to install and uninstall the service or manually starting and stopping an installed service to replace component library assemblies. 

The pain can be alleviated by using automated batch scripts on the post-build event.

ECHO Checking for existing deployment...
IF EXIST "
\\<server>\<deployment-target>" GOTO COPYFILES
GOTO SHOWNOTICE

:COPYFILES
ECHO Found deployment; copying output files...
ECHO Stopping Windows Service...
SC
\\<server> STOP <service-name>

:: COPY FILES HERE....

ECHO Starting Windows Service...
SC
\\<server> START <service-name>
ECHO Started Windows Service.
GOTO END

:SHOWNOTICE
ECHO Did not find deployment target...
GOTO END

:END
ECHO Completed build...

In this example, I'm deploying to a remote server on the local network (but it would work just as well on a local deployment).  I came up with the script a while back after I got tired of stopping services, copying binaries over by hand manually, and starting services when testing some appliactions that I was building.  This technique still requires a one time initial install to deploy the Windows service, but on subsequent builds,

  1. It checks to see if the deployment target exists,
  2. If so, it stops the service and replaces the binaries (I've left that script out since it's particular to any given deployment),
  3. It restarts the service.

I find this particularly useful for testing WF applications in custom Windows services based runtimes and for testing WCF applications in custom Windows services based runtimes as it allows me to install the service once and redeploy the component binaries with each build with ease.

 Friday, September 28, 2007

Programmathon VII Day 5, 6 - Extended Edition

9/28/2007 12:32:08 AM (Eastern Daylight Time, UTC-04:00)

Getting lots of work done.

Today, day 6, we planned to go out to the Red Butte Cafe to get some buffalo burgers.  Brad even called ahead to ask, before we embarked on a 30 minute journey, whether they still sold buffalo burgers.  Not only that, he specifically asked if the buffalo burgers are actually made from buffalo meat.

Unfortunately, when we were seated and finally prepared to order, we were all disappointed by the fact that they no longer served buffalo burgers.  We had to settle for tamer fare.  You can just see the disappointment on Jim's face.  The quest for buffalo burgers will have to wait till next time.

 Thursday, September 27, 2007

Dynamic SQL: Yea or Nay?

9/27/2007 2:42:01 PM (Eastern Daylight Time, UTC-04:00)

I've always been on the side of stored procedures in the classic debate over the merits of dynamic SQL.  In reality, I can only think of one good scenario where dynamic SQL at the application layer should be used: programmatic batch inserts.

I won't go into the performance debate, since there are tons of articles that already cover this area, but rather, I'd like to discuss the usability and development and architectural aspect of it.

In almost all other cases, it seems like the best choice is to have the application not generate dynamic SQL and use a stored procedure...always.  There are certainly times when dynamic SQL is necessary, for example, when generating selects against a dynamic table structure, but in those cases, the variable portions of the query can be parameterized into the stored procedure and the procedure should generate the dynamic SQL.

Some would argue that if the underlying data models change, the application layer will usually be forced to change are ignoring other aspects of model changes that don't necessitate application model changes.  These include performance tuning, filtering by table JOINs and reuse of the data logic in nested stored procedures or functions.

When working with compiled code like .NET, the core issue is that fixing query errors involves a recompile and redeploy, which in most cases, is much more difficult than just fixing a completely disconnected (but not completely decoupled since there is a quasi-interface (the return result type and structure)) stored procedure.

For example, if a dataset today contains data from table A and tomorrow it needs to include data from table A and B (let's say they both contain the same elements, but one is used for archives), it would be easy to update the procedure to UNION the results from the two datasets without affecting the application layer.

This isn't the only scenario, for example, let's say the requirement changes and now the data needs to be filtered by another table.  It would be easy to add a new INNER JOIN to the query without affecting the application layer.  Not only that, it also allows for the recombination of fields (for example a user name field today only needs to show first and last name, but tomorrow, it may need to show the middle initial as well - this change can be done at the database level and not affect the application or UI layers).  It can also make it easy to change the underlying table structure so long as the return data isn't expected to change: it provides a layer of decoupling between the application layer and the raw data storage.

In addition, having a stored procedure allows for easier testing of the data layer without the added overhead of having to execute the application runtime and walk through the debugger line by line just to figure out if the return data is correct; it is much more efficient to simply execute the query and simulate the use case to find if the data that is returned is correct.  It becomes much easier and much less painful to simulate data access tests since they can be run, observed, and analyzed nearly instantly.

In larger organizations with dedicated DBAs, stored procedures have the added benefit of allowing SQL experts to add performance tuning to eek out extra performance without requiring the application to be rewritten or recompiled.  Again, we see this decoupling of the application layer from the data layer.  Of course you could always have templated SQL stored in XML files or something that would get rid of that recompile, but it is still likely to necessitate more redeployment if the application in question is distributed.  This key point is not to be taken lightly since -- as an example -- an error in string formatting may require the replacement of binaries and services across dozens of servers.  Not only that, testing in such a scenario still requires interaction with the application layer, adding to the possible failure points, time required, and general development pain.

My own conclusion is that using dynamic SQL (including LINQ) creates too tight of a coupling between the application layer binaries and the underlying data store; it's great for RAD and testing, but in any application of significance (especially in highly distributed environments), dynamic SQL at the application layer seems like it's a maintenance and testing disaster waiting to happen.

Quote of the Day

9/27/2007 10:26:10 AM (Eastern Daylight Time, UTC-04:00)

It's been a while since we've had one of these.

This one is from Dwayne Johnson, aka, The Rock:

"I knew when I first wanted to get into this business that I was going to have to take on action roles.  But I always wanted that to be a luanching point.  I want to view the movies as school.  If I'm not learning or trying different things, then I'm not going to grow."

 Tuesday, September 25, 2007

Programmathon VII Day 4

9/25/2007 1:24:45 AM (Eastern Daylight Time, UTC-04:00)

Another day, another couple hundred lines of code packed away.

It's been kind of painful these last few days without my ergo keyboard and 24" LCD :-P  It's also been kind of slow going and there have been lots of frustrations as we try to get more pieces working. 

But occasionally, when we do align several of the components for a small slice of time, there are moments of sheer joy as you watch the whole of the machinery move.  I am reminded of a passage from Mythical Man Month by Fred Brooks:

Why is programming fun? What delights may its practioner expect as his reward?

First is the sheer joy of making things. As the child delights in his mud pie, so the adult enjoys building things, especially things of his own design. I think this delight must be an image of God's delight in making things, a delight shown in the distinctness and newness of each leaf and each snowflake.

Second is the pleasure of making things that are useful to other people. Deep within, we want others to use our work and to find it helpful. In this respect the programming system is not essentially different from the child's first clay pencil holder "for Daddy's office."

Third is the fascination of fashioning complex puzzle-like objects of interlocking moving parts and watching them work in subtle cycles, playing out the consequences of principles built in from the beginning. The programmed computer has all the fascination of the pinball machine or the jukebox mechanism, carried to the ultimate.

Fourth is the joy of always learning, which springs from the nonrepeating nature of the task. In one way or another the problem is ever new, and its solver learns something: sometimes practical, sometimes theoretical, and sometimes both.

Finally, there is the delight of working in such a tractable medium. The programmer, like the poet, works only slightly removed from pure thought-stuff. He builds his castles in the air, from air, creating by exertion of the imagination. Few media of creation are so flexible, so easy to polish and rework, so readily capable of realizing grand conceptual structures. (...)

Yet the program construct, unlike the poet's words, is real in the sense that it moves and works, producing visible outputs separately from the construct itself. It prints results, draws pictures, produces sounds, moves arms. The magic of myth and legend has come true in our time. One types the correct incantation on a keyboard, and a display screen comes to life, showing things that never were nor could be.

Programming then is fun because it gratifies creative longings built deep within us and delights sensibilities we have in common with all men.

It's kind of like any sort of addictive drug: you have short, blissful highs with grinding, intellectually anguishing lows when things just don't work right or the picture is murky.  Most of the time is kind of spent in a middle ground between intellectual orgasm and hair pulling aggrevation (not that I can pull my hair, but threading errors will do that to you), but there's always that moment when things are finally working in unison that makes all the work worth it.

No pictures yet but we did our regular Hooters lunch and had a special treat, Brazilian BBQ for dinner (excellent, excellent, excellent).

While I've been working 12-16 hour days these last few days, I have been kind of keeping up with the whole Ahmadinejad situation.  To tell the truth, I really don't understand what many of the haters (yes, I did just use that term) are ranting about.  Whatever happened to diplomacy?  What ever happened to listening to all sides of the story?

I am starting to seriously wonder just how much our perception of right and wrong is shaped by what the government, and consequently mass media, wants us to believe.  The core problem is that for many Americans, the level of independent thinking is severely lacking.  It's how we got into the mess in Iraq in the first place.  It's how we could have possibly elected a total dimwit as a president...twice no less.

Contrary to what our current government would have us believe, Ahmadinejad has shown himself to be more of a diplomat and thinker than just about everyone in our current administration.  Unlike our president, Ahmadinejad has shown that he isn't afraid of the tough questions and harsh criticism and cheap insults that he received from people who should have shown more respect to the leaders of one of the most influential countries in the Middle East today.  Ahmadinejad has indeed shown what it means to be a president and a diplomat (I'm not saying I agree with Iran's human rights policies or laws, but I can respect a man that calmly steps into the heart of the enemy's domain and wishes only to speak and open dialogue).

Scott Adams has a wonderful, tongue in cheek, blog post airing out his thoughts on Ahmadinejad's visit.  He emphasizes the double standards that we have set, the arbitrary usage of "terrorism" these days, and tries to emphasize that there are always two sides to a story.  This tends to be my view of the whole situation as well; I'm just not ready to believe that Iran deserves its infamous "Axis of Evil" membership designation.

Senator Mike Gravel also wrote a wonderful opinion piece as well:

Let's be clear -- a war with Iran will further isolate the United States in the world. It will unify the entire Middle East against U.S. forces that are stationed there. And worst of all, it will precipitate attacks on America that will far surpass the horror of 9/11. It's time to step away from the brink and begin finding common ground. Let Ahmadinejad go to Ground Zero and honor our dead. And together, let's all acknowledge that neither war nor terrorism will solve our problems.

We can only hope that our leaders aren't stupid enough to get us mired down in a decade of conflict and war that will cost the public hundreds of billions of dollars when there is an opportunity to air our differences in a diplomatic and political fashion.

 Monday, September 24, 2007

Programmathon VII Day 2, Day 3

9/24/2007 1:29:33 AM (Eastern Daylight Time, UTC-04:00)

Day two was pretty busy, so I didn't really get a chance to take any pictures.  Towards the end of the day, I was definitely feeling a bit high strung.  I'm goal oriented and I abide by the saying: say what you will do; do what you say.  This doesn't fly so well with everyone I guess.  Certainly, sometimes you do underestimate the task at hand and you simply have to live with "good enough", but this just causes all sort of anxieties with me.  But regardless, we made some really good progress; got lots of big pieces working.

The hard work contiuned on day 3. 

Man, just look at everyone hard at work.  Damn, look at that concentration on Jim's face.  That's what I like to see.

Dave (front right) was giving a quick presentation on some of the ComponentArt libraries.  I'm not a big fan of these things (I prefer the raw web services and AJAX.NET), but I guess not everyone wants to abandon the ASP.NET model so quickly.

We had lunch in the office because we were all in the groove.  But I hear it's Thuy's birthday tomorrow and we're heading to Hooters ;-).

Gray cells continue to churn after lunch.

For some reason, the thought occurred to me that I hadn't had a corn dog in, oh I dunno, 10 years?  Not only did we end up with any old corn dogs, these were freshly battered!  Pretty awesome with some squeezed lemonade.  You may have noticed the supersized drinks (in this case, lemonade).  But you really can't begin to imagine how much caffeine these guys consume on a daily basis.  Rare is the day that Brady or Brad will wander into the office without their 64oz. diet Coke in the morning (and then continue to order diet Cokes throughout the day..amazing that they sleep at all).

Still more work to do.  Day 4 awaits!

 Friday, September 21, 2007

Programmathon VII Begins!

9/21/2007 11:41:20 PM (Eastern Daylight Time, UTC-04:00)

So I'm back in Utah.  Today is day one of Programmathon VII. This time, we have two new faces, Dan and Thuy (all the ways from Vietnam!)

The highlight of any of the Programmathons are the meals and awesome sightseeing that we get to do while we're out in Utah (some trips being more memorable than others) to break up the long hours of work and occassional heated technical debates.  Of course Brad would probably disagree: the highlights of the Programmthons are really the 14 hour days we pull to get things done.

We ended up at Park City for dinner on day one.

The steak at Grub Steak was pretty good (and so was the atmosphere), but the waiter totally oversold the awesome-ness of their steaks.  7.5/10.

Luckily, I ended up with a Honda Odyssey instead of the Ford Freestar I was supposed to get.  This thing has some guts...no problem hauling 7 full grown adults up some pretty steep climbs.

Park City is a quaint little area.  The main street is lined with all sorts of eateries, expensive art galleries (expensive).  From left (above): Jim, Dave, Thuy, Me, Dan, and Brad.

More pictures with the locals.

Ice cream at Cow's.  Very good stuff.

Seems like the calm before the storm.  Only a few days left to wrap up version 1.

 Thursday, September 20, 2007

I Don't Like To Get Political, But...

9/20/2007 1:58:51 PM (Eastern Daylight Time, UTC-04:00)

A Republican leader with some balls, some heart, and most importantly, lots of humanity.

Well said, sir, well said:

Mayor Sanders: "With me this afternoon is my wife, Rana.

"I am here this afternoon to announce that I will sign the resolution that the City Council passed yesterday directing the city attorney to file a brief in support of gay marriage [with the California Supreme Court].

"My plan, as has been reported publicly, was to veto that resolution, so I feel like I owe all San Diegans an explanation for this change of heart.

"During the campaign two years ago, I announced that I did not support gay marriage and instead supported civil unions and domestic partnerships.

"I have personally wrestled with that position ever since. My opinion on this issue has evolved significantly -- as I think have the opinions of millions of Americans from all walks of life.

(Sanders with lesbian City Councilmember Toni Atkins)
"In order to be consistent with the position I took during the mayoral election, I intended to veto the council resolution. As late as yesterday afternoon, that was my position.

"The arrival of the resolution -- to sign or veto -- in my office late last night forced me to reflect and search my soul for the right thing to do.

"I have decided to lead with my heart -- to do what I think is right -- and to take a stand on behalf of equality and social justice. The right thing for me to do is to sign this resolution.

"For three decades, I have worked to bring enlightenment, justice and equality to all parts of our community.

"As I reflected on the choices that I had before me last night, I just could not bring myself to tell an entire group of people in our community that they were less important, less worthy and less deserving of the rights and responsibilities of marriage -- than anyone else -- simply because of their sexual orientation.

"A decision to veto this resolution would have been inconsistent with the values I have embraced over the past 30 years.

"I do believe that times have changed. And with changing time, and new life experiences, come different opinions. I think that's natural, and certainly it is true in my case.

"Two years ago, I believed that civil unions were a fair alternative. Those beliefs, in my case, have since changed.

"The concept of a 'separate but equal' institution is not something that I can support.

"I acknowledge that not all members of our community will agree or perhaps even understand my decision today.

"All I can offer them is that I am trying to do what I believe is right.

"I have close family members and friends who are members of the gay and lesbian community. These folks include my daughter Lisa and her partner, as well as members of my personal staff.

"I want for them the same thing that we all want for our loved ones -- for each of them to find a mate whom they love deeply and who loves them back; someone with whom they can grow old together and share life's wondrous adventures.

"And I want their relationships to be protected equally under the law. In the end, I could not look any of them in the face and tell them that their relationships -- their very lives -- were any less meaningful than the marriage that I share with my wife Rana. Thank you."

Touching, well thought out, reflective, compassionate, and sincere.

A hand for Jerry Sanders.

 Thursday, September 13, 2007

Double Dispatch To The Rescue

9/13/2007 9:55:42 AM (Eastern Daylight Time, UTC-04:00)

In working out a tricky design issue surrounding the usage of the Visitor pattern, I stumbled upon the related Double Dispatch pattern/mechanism.

In short, double dispatch, when implemented, allows an object - a "dispatcher" - to delegate method calls based on the runtime type or types involved in the interaction.

The two core problems with visitor are that:

  1. You must have access to the visitable object to implement an IVisitable interface.  Often times, when dealing with binary code references, this is not an option.
  2. Each visitor must implement IVisitor, regardless of whether all of the Visit() interactions make any sense.  This means that if there are 10 concrete IVisitable classes, then each IVisitor must implement 10 Visit() methods that support each of the concrete classes, even if the particular IVisitor has no valid operations on a given IVisitable.

I suppose point number 1 is not really a constraint, it's just that it's a criterion for a by-the-book visitor pattern implementation.  But point number 2 is a really sticky situation since in many cases, not all of the IVisitor concrete classes should support all of the IVisitable concrete classes.  Of course we could just add a blank method stub, even for operations that don't make sense, but that just seems like bloat and a maintenance nightmare.

The key in understanding the use of double dispatch is to understand the core issue with the Visitor pattern.  We want the visitor to perform different operations based on the runtime type passed into a call to an abstract Visitor base class.  Unlike method overloading, which relies on compile time types to determine the method to invoke, we want the code to decide which method to invoke based on the runtime type of the object being passed in.

Ideally, we could have a scenario like this:

public class Program {
    private static void Main(string[] args) {
        Collection<AbstractVisitor> visitors 
			= new Collection<AbstractVisitor>();
        Collection<Pet> pets = new Collection<Pet>();

        pets.Add(new Fish());
        pets.Add(new Dog());

        visitors.Add(new Feeder());
        visitors.Add(new Walker());

        // Visit each of the pets.
        foreach (Pet pet in pets) {
            foreach (AbstractVisitor visitor in visitors) {
                visitor.Visit(pet);
            }
        }
    }
}

/// <summary>
/// Abstract base class for visitors.
/// </summary>
public abstract class AbstractVisitor {
    public abstract void Visit(Pet pet);
}

/// <summary>
/// Concrete visitor, a pet feeder.
/// </summary>
public class Feeder : AbstractVisitor {
    public void Visit(Dog dog) {
        // Feed the dog
        dog.Visitors.Add("Fed the dog");
    }

    public void Visit(Fish fish) {
        // Feed the fish
        fish.Visitors.Add("Fed the fish");
    }
}

/// <summary>
/// Concrete visitor, a pet walker.
/// </summary>
public class Walker : AbstractVisitor {
    public void Visit(Dog dog) {
        dog.Visitors.Add("Walked the dog");
    }

    // Fish can't be walked!
}

But this code doesn't work!  Both Walker an Feeder must now implement Visit(Pet pet) like so:

/// <summary>
/// Abstract base class for visitors.
/// </summary>
public abstract class AbstractVisitor {
    public abstract void Visit(Pet pet);
}

/// <summary>
/// Concrete visitor, a pet feeder.
/// </summary>
public class Feeder : AbstractVisitor {
    // Yucky if block...
    public override void Visit(Pet pet) {
        if(pet is Dog) {
            Visit(pet as Dog);
        }
        if(pet is Fish) {
            Visit(pet as Fish);
        }
    }
    
    public void Visit(Dog dog) {
        // Feed the dog
        dog.Visitors.Add("Fed the dog");
    }

    public void Visit(Fish fish) {
        // Feed the fish
        fish.Visitors.Add("Fed the fish");
    }
}

This is a less than desirable situation since for every new pet type we introduce, we need to introduce another entry in the if-else block. Yuck. 

While there were many articles on how to implement double dispatch in C# using various techniques, by far, the simplest implementation that I found was one by Anthony Cowley.

So let's see how our code would look using this technique (I'll omit the implmentation of the abstract visitor class for now):

public class Program {
    private static void Main(string[] args) {
        Collection<AbstractVisitor> visitors 
			= new Collection<AbstractVisitor>();
        Collection<Pet> pets = new Collection<Pet>();

        pets.Add(new Fish());
        pets.Add(new Dog());

        visitors.Add(new Feeder());
        visitors.Add(new Walker());

        // Visit each of the pets.
        foreach (Pet pet in pets) {
            foreach (AbstractVisitor visitor in visitors) {
                visitor.Visit(pet);
            }
        }

        // Check the results.
        foreach(Pet pet in pets) {
            Console.Out.WriteLine(pet.GetType().Name);
            foreach(String note in pet.Visitors) {
                Console.Out.WriteLine("\t{0}", note);
            }
        }
    }
}

/// <summary>
/// Concrete visitor, a pet feeder.
/// </summary>
public class Feeder : AbstractVisitor {
    public void Visit(Dog dog) {
        // Feed the dog
        dog.Visitors.Add("Fed the dog");
    }

    public void Visit(Fish fish) {
        // Feed the fish
        fish.Visitors.Add("Fed the fish");
    }
}

/// <summary>
/// Concrete visitor, a pet walker.
/// </summary>
public class Walker : AbstractVisitor {
    public void Visit(Dog dog) {
        dog.Visitors.Add("Walked the dog");
    }

    // Fish can't be walked!
}

/// <summary>
/// Base class for pets.
/// </summary>
public abstract class Pet {
    private readonly Collection<string> visitors;

    public Collection<string> Visitors {
        get { return visitors; }
    }

    protected Pet() {
        visitors = new Collection<string>();
    }
}

/// <summary>
/// A pet fish.
/// </summary>
public class Fish : Pet {}

/// <summary>
/// A pet dog.
/// </summary>
public class Dog : Pet {}

When I run this program, I get the following output:

As expected, the dog was walked and fed, but the fish was only fed. Now we can go about adding pets to our heart's content and we won't be forced to add another Visit() method declaration. If there is no Visit() method implemented for a particular pet type, then nothing happens and the default implementation of Visit() on the abstract class handles it. The magic all happens in Cowley's implementation of AbstractVisitor:

/// <summary>
/// Abstract base class for visitors.
/// </summary>
public abstract class AbstractVisitor {
    private static readonly Dictionary<long, MethodInfo> dispatch;

    static AbstractVisitor() {
        dispatch = new Dictionary<long, MethodInfo>();

        foreach (Type t in Assembly.GetCallingAssembly().GetTypes()) {
            if (t.IsSubclassOf(typeof(AbstractVisitor))) {
                foreach (MethodInfo mi in t.GetMethods()) {
                    if (mi.Name == "Visit") {
                        ParameterInfo[] pars = mi.GetParameters();
                        if (pars.Length == 1) {
                            Int64 code = ((Int64)t.GetHashCode() << 32) +
                                pars[0].ParameterType.GetHashCode();
                            dispatch.Add(code, mi);
                        }
                    }
                }
            }
        }
    }

    public virtual void Visit(object pet) {
        Int64 hash = ((Int64)GetType().GetHashCode() << 32) + 
            pet.GetType().GetHashCode();
        if (dispatch.ContainsKey(hash)) {
            dispatch[hash].Invoke(this, new object[] { pet });
        }
        else {
            // This is our fallback functionality
            Console.WriteLine("Visiting object " + pet.ToString());
        }
    }
}

You may have spotted an issue here: since the Visit() method is virtual, the subclasses of AbstractVisitor aren't strictly forced to define a Visit() method of any sort nor is there a constraint that forces any implementation to have only one argument.  But this is just fine, since all of the classes must inherit from AbstractVisitor to participate in this pattern, it will also inherit the default implementation of Visit() which, conveniently, contains a fallback do-nothing behavior.

You can download the full source here: SimpleDoubleDispatchSample.zip (4.08 KB)

I also have a second sample that I modified from Cowley's code that uses singleton instances of the visitors and improved encapsulation of the dispatch lookup table.  I also modified the catch all virtual Visit() method to a call to another virtual method to allow subclasses to change the default implementation of the case that no specific Visit() is found: SimpleDoubleDispatchSample.2.zip (4.12 KB)

Once again, credit goes to Anthony Cowley for an excellent and simple implementation of double dispatch in C#. There were two others that I examined, namely one by Max Hajek (which actually generated IL) and one by Steve Love using Generics.  In both cases, I thought the solutions were far more complex than the actual problem that they tried to solve, though Max's solution seems far more powerful and may be more useful in some scenarios.

RSS 2.0 Atom 1.0 CDF