Silverlight RPG: Steel Saga
24 March 09 03:34 PM | Avi Pilosof | 0 Comments   

My friend Darren who wrote the hilarious Buddy Knavery game has released a preview of his new project called Steel Saga.

http://www.steelsaga.com

It’s an old-style RPG written in Silverlight, and seems quite deep. At the moment there are only two “zones” (one outdoor and one dungeon), but Darren hates having a life, and is going to spend the coming weeks writing plenty more content.

Check it out; I still can’t believe one guy is doing all this work (programming, art, music).

 

Avi

Filed under: ,
Live Mesh Applications – whoah.
17 February 09 08:52 AM | Avi Pilosof | 1 Comments   

I’ve been playing with Live Mesh for a long time now, and have been loving it. During this time I’d been hearing about applications for Live Mesh, and while it sounded interesting in theory I didn’t really get what the point would be.

Then I watched this video:

http://briangorbett.com/mesh/mesh-video-player/

 

The video walks you through the development process of a Live Mesh app. The “whoah” moment is when I realized that the one codebase - without any special code - created an application that ran identically on the web and on the desktop. Money shot starts at about 25:30; but it helps to watch the whole vid.

This is fascinating; this is a “Web OS” (I hate that term).

 

Avi

Silverlight Game Contest: $5000 prize
16 February 09 09:51 AM | Avi Pilosof | 3 Comments   

The site is a little low on details/rules, but the prize seems pretty sweet:

http://www.serverquestcontest.com

 

Filed under:
Be Paranoid. Be Very Paranoid.
03 December 08 01:29 PM | Avi Pilosof | 2 Comments   

Do you maintain an application with a backend database? Does that application ever write to the database? Be afraid.

The worst situation to be in with respect to the above is when a customer mails you and says: “Why is my data gone?”

99% of the time, it’s because they clicked on the “Delete” button; but sometimes you need proof – this is what I want to discuss.

Log every interesting write to the database. There are various levels at which you can do this:

  • Application logic level: This gives you the most context, the most usable logs, and in fact gives you data which can generally be exposed directly to the customer, preventing lots of questions. The problem is that it’s not reliable: The app could succeed in writing then fail in logging. Alternatively, a dev on the app might forget to add logging to a new feature.
  • Application object model level: A little less context/readability than above, a little more reliability.
  • Data-Access-Layer level: Less context than above, more reliability.
  • Sproc level: Same tradeoff.
  • Table level (triggers): And same again.

As you can see, the lower you go, the less chance for error and the less value a log entry gives you. You can increase the value of a “deep” log entry by forcing a set of arguments up the stack. For example, you can force every sproc which writes data to require username/machine/reason arguments – but this can become costly to maintain.

So which is right? Depends on your app, your level of paranoia, your customer’s needs, etc. Generally I stay away from triggers and try to add logging at the sproc level for critical sprocs, and at the app/OM level for extra context; better to have too much logging than too little.

Here’s a nice trick for creating a footprint log table that your sprocs (or triggers) can use quite easily - Create a simple table with the following columns (at least):

 

Name Default value Comment
LogEntry   The text you want to log.
TimeStamp getdate() Automatically fills in the current datetime.
User system_user Automatically inserts the logged in user’s ID.
Machine host_name() Automatically inserts the user’s client machine name.
Application app_name() Automatically inserts the name of the application running this code (provided you set it in the connection string, which you should!)

 

Inserting into this table requires only the one column, but you capture lots of extra data for free. If this is done from a trigger, then you’ll even catch slackers with access to the DB who are updating it manually.

 

Oh, don’t forget to age off old entries before the table ends up at 50GB ;)

 

Avi

WPF: Allow your users to define their own styles using XAML
27 October 08 11:55 AM | Avi Pilosof | 0 Comments   

An application I’ve been working on has the vague property of “displaying items on the screen”, and a fairly unusual requirement which specifies that users be able to completely define how the items look.

Our items are displayed on a timeline and can represent pretty much anything (up to the user), hence the flexibility required for the visuals.

In order to support this, we do the following:

  1. Have a generic class (DataItem) that represents a rendered item and which supports a few basic properties (name, start date, etc).
  2. Support any number of custom properties for that class (stored as an internal collection).
  3. Allow the user to define a “Style name” for any item (think of a CSS style name).
  4. Allow them to define a global list of styles (think of a CSS stylesheet), each with custom XAML.
  5. Allow that XAML to bind to any custom property of an item.
  6. Lookup the style at runtime, load the XAML then display it in the user control.

There are a few small tricks that make this possible:

 

Binding To Custom Properties

Because custom properties on an item are just key-value pairs (in our case, strings), we needed a unified way to read any property (whether native or custom) from an item so that XAML binding could use a single syntax.

First, we create a method on our item class that returns the value of any property:

   1: public string GetFieldValue(string fieldName)
   2: {
   3:     // First attempt to get an actual item property using reflection.
   4:     PropertyInfo prop = typeof(DataItem).GetProperties()
   5:         .SingleOrDefault( p => p.Name.EqualsIgnoreCase(fieldName) );
   6:     if (prop != null)
   7:     {
   8:         object val = prop.GetValue(this, null);
   9:         return val == null ? "" : val.ToString();
  10:     }
  11:  
  12:     // Failing that, attempt to get it from the custom field collection.
  13:     if ( _CustomFields.ContainsKey(fieldName) )
  14:         return _CustomFields[fieldName];
  15:     // Failing that, just return an empty string.
  16:     else
  17:         return "";
  18: }

So now we can get the value of an item’s property whether it’s native or custom – but how do we use XAML data binding to bind to the results of this method? Use a Value Converter:

   1: /// <summary>
   2: /// This takes as a parameter the name of the field to read from the
   3: /// DataItem, and returns the value of that field from the bound-to.
   4: /// DataItem.
   5: /// </summary>
   6: public class DataItemPropertyReader : IValueConverter
   7: {
   8:     public object Convert(object value, Type targetType,
   9:         object parameter, System.Globalization.CultureInfo culture)
  10:     {
  11:         return (value as DataItem)
  12:           .GetFieldValue(parameter.ToString());
  13:     }
  14:  
  15:     public object ConvertBack(object value, Type targetType,
  16:         object parameter, System.Globalization.CultureInfo culture)
  17:     {
  18:         throw new NotImplementedException();
  19:     }
  20: }

Declare an instance of this guy which is app-wide (say, in Styles.xaml):

   1: <!-- This is used by individual DataItems to bind to custom properties in items -->
   2: <MyNamespace:DataItemPropertyReader x:Key="DI" />

And now the users who write the XAML can bind like this:

   1: <TextBlock Text="{Binding Converter={StaticResource DI}, ConverterParameter=Name}" />
   2: <TextBlock Text="{Binding Converter={StaticResource DI}, ConverterParameter=Custom1}" />
   3: <TextBlock Text="{Binding Converter={StaticResource DI}, ConverterParameter=Address}" />

 

Displaying Custom XAML Dynamically

Now it comes time to read this XAML and display it. This has been shown before many times, so I won’t go into it too much. It’s fairly simple; the only trip-ups are name-spaces and data contexts.

At runtime, use user control replaces the contents of its layout as such:

   1: private void RedrawItem()
   2: {
   3:     string XAML = GetXAMLByStyleName( Item.StyleName );
   4:  
   5:     UIElement el = Common.CreateUIElementFromXAML(XAML);
   6:  
   7:     // Remove what was there, and add the new stuff.
   8:     LayoutRoot.Children.Clear();
   9:     LayoutRoot.Children.Add(el);
  10:  
  11:     // Set the context, so that data binding knows
  12:     // what to look at.
  13:     LayoutRoot.DataContext = this.Item;
  14: }
  15:  
  16: public static UIElement CreateUIElementFromXAML(string XAML)
  17: {
  18:     // Wrap the XAML in a grid, to guarantee one root element
  19:     XAML = string.Format("<Grid>{0}</Grid>", XAML);
  20:  
  21:     byte[] XAMLbytes = System.Text.Encoding.UTF8.GetBytes(XAML);
  22:     MemoryStream memstream = new MemoryStream(XAMLbytes);
  23:  
  24:     // Can only load the XAML if we know about the right namespaces.
  25:     // (You'd do well to centralize this bit of code)
  26:     ParserContext pc = new ParserContext();
  27:     pc.XmlnsDictionary.Add("",
  28:       "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
  29:     pc.XmlnsDictionary.Add("x",
  30:       "http://schemas.microsoft.com/winfx/2006/xaml");
  31:     pc.XmlnsDictionary.Add("mc",
  32:       "http://schemas.openxmlformats.org/markup-compatibility/2006");
  33:     
  34:     UIElement el = (UIElement)XamlReader.Load(memstream, pc);
  35:     return el;
  36: }

As for performance; it’s not too bad. My app doesn’t need to do this for more than a few dozen items, so we haven’t really pushed it.

 

Avi

Filed under:
WPF: Supporting command line arguments and file extensions
27 October 08 11:05 AM | Avi Pilosof | 1 Comments   

Traditionally, handling command-line args in an app has been a simple case of reading arguments from the “int main()” function (or equivalent). In WPF this changes somewhat.

The usual setup is that you have your App.xaml.cs, which loads your initial window. While it’s the former that  has access to the arguments, it’s the latter that is likely to use them. So the steps are:

  1. In App.xaml.cs, catch the event that gives you the args.
  2. Store them somewhere.
  3. Access them from your main window.

Here’s one way to do it (demonstrating a single argument). Firstly, in App.xaml.cs:

   1: protected override void OnStartup(StartupEventArgs e)
   2: {
   3:     if (e.Args != null && e.Args.Count() > 0)
   4:     {
   5:         this.Properties["ArbitraryArgName"] = e.Args[0];
   6:     }
   7:  
   8:     base.OnStartup(e);
   9: }

Then in the main window, you can access Application.Current.Properties to get at your args. However, if this happens to be the name of a file you want to load, you should probably wait until after your window has finished loading so that you can react to that file load (eg; give an error, display data, etc):

   1: public MainContainer()
   2: {
   3:     InitializeComponent();
   4:  
   5:     // Make sure we handle command line args:
   6:     this.Loaded += new RoutedEventHandler(MainContainer_Loaded);
   7: }
   8:  
   9: void MainContainer_Loaded(object sender, RoutedEventArgs e)
  10: {
  11:     if (Application.Current.Properties["ArbitraryArgName"] != null)
  12:     {
  13:         string fname = Application.Current.Properties["ArbitraryArgName"].ToString();
  14:         // Act on the file...
  15:     }
  16: }

 

Launching via a double-click on a custom file type.

I created an application that supports a custom document format and I wanted a double-click on that document to open my application. I assumed that the application would open normally with the document presented as a command-line arg, but that’s not the case. Instead, it’s stored in the following tongue-twister:

AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData[0]

So, if you revisit the OnStartup method in App.xaml.cs, you can query this location and store it in the same way you did the command-line arg:

   1: protected override void OnStartup(StartupEventArgs e)
   2: {
   3:     // Check if this was launched by double-clicking a doc. If so, use that as the
   4:     // startup file name.
   5:     if (AppDomain.CurrentDomain.SetupInformation
   6:         .ActivationArguments.ActivationData != null
   7:     &&  AppDomain.CurrentDomain.SetupInformation
   8:         .ActivationArguments.ActivationData.Length > 0)
   9:     {
  10:         string fname = "No filename given";
  11:         try
  12:         {
  13:             fname = AppDomain.CurrentDomain.SetupInformation
  14:                     .ActivationArguments.ActivationData[0];
  15:             
  16:             // It comes in as a URI; this helps to convert it to a path.
  17:             Uri uri = new Uri(fname);
  18:             fname = uri.LocalPath;
  19:  
  20:             this.Properties["ArbitraryArgName"] = fname;
  21:  
  22:         }
  23:         catch (Exception ex)
  24:         {
  25:             // For some reason, this couldn't be read as a URI.
  26:             // Do what you must...
  27:         }
  28:     }
  29:  
  30:     base.OnStartup(e);
  31: }

Notice the little trick with using the “Uri” class; this is because the string you’ll see will look like this:

file:///c:/temp/my%20test%20doc.blah

 

Avi

Filed under:
You’re asking for the wrong thing.
16 October 08 08:50 AM | Avi Pilosof | 7 Comments   

Just stumbled upon this article by John Dvorak titled “My Windows 7 Wish List”, and I see the same pattern as many of the other fruitless wish list articles. Maybe IHBT

As a user, the user experience (UX) is the only thing that ever matters to you – the implementation is never the issue.

Take for example item (1) on the list:

“Build a new file system based on database technologies”. Why do you care about how the file system works internally? Do you actually want a file system with a DB in the backend, or do you just want to be able to run fast, useful searches? My guess is that it’s the latter.

Another one:

“Get rid of the miserable registry”. Is it the registry you care about, or the fact that it’s difficult to upgrade/migrate?

Another:

“Get off the cloud”. You’re worried that “suddenly the machine wants to contact the Internet for some reason or other”. This has nothing to do with the “cloud strategy”; you just want a machine that doesn’t use the network. Or something. I don’t really know based on that rant :)

 

Alternatively just read this:

http://shippingseven.blogspot.com/2008/07/20-features-windows-7-should-include.html

 

Basically (and I’m talking about customers in general – not just MS/Windows) people often request implementation changes, but what they really want are UX changes. Do you want a powerful engine, or just a fast car? Do you want high carbon steel, or just a knife that’s very sharp and stays that way? Do you really care how the sausage is made? ;)

 

As a customer, it’s important to understand what you actually want since it makes it much easier to ask for it (and hence get it).

 

Avi

Trivial but useful extension method
30 September 08 04:34 PM | Avi Pilosof | 2 Comments   

Don’t know why I didn’t write this before; it makes code very readable. Often when you write anything graphics related, you want to constrain coordinates to window edges (for example).

So a simple method:

   1: /// <summary>
   2: /// Ensure that the given number falls within the
   3: /// given min/max constraints.
   4: /// </summary>
   5: public static double Constrain( this double num,
   6:                         double min, double max )
   7: {
   8:     if (num < min)
   9:         return min;
  10:     else if (num > max)
  11:         return max;
  12:     else
  13:         return num;
  14: }

It’s just a Floor combined with a Ceiling, but it reads nicely:

   1: xform.X = dx.Constrain(-this.ActualWidth, 0 );
   2: xform.Y = dy.Constrain(-this.ActualHeight, 0 );

 

Avi

On using Arrays
23 September 08 11:52 AM | Avi Pilosof | 0 Comments   

Eric Lippert has a great article on arrays:

http://blogs.msdn.com/ericlippert/archive/2008/09/22/arrays-considered-somewhat-harmful.aspx

 

I think this is especially useful to consider if you’re writing an API. Consider not just arrays, but any time you’re returning any collection: Are you returning values or a variables? Did you mean to?

 

Avi

Microsoft Bacon Home Edition, RC0
11 September 08 06:39 PM | Avi Pilosof | 2 Comments   

Totally unrelated to programming, but I deem this worthy of a blog post. A few weeks ago I decided to make my own bacon.

 

Why? Don't ask stupid questions; bacon is an axiom.

How? turns out that it's actually not so complex:

  1. Buy some pork belly.
    • Any butcher (even the one inside the supermarket) will be able to get you a piece. I used about 1.5Kg worth (~3lbs).
  2. Brine it for a week in the fridge.
    • I used salt+sugar+pepper. Next time I'll add honey or maple syrup.
  3. Smoke it.
    • I had a little trouble here, since I don't have anything resembling a smoker. Next step is to build one.

The details are in this great book, and I recommend that you read it before you accidentally make your own botulism. The truth is that I didn't, but only because the bookstore was out of stock! But seriously, there are risks involved - the fridge temperature has to be just right.

Here are some photos from the adventure:

Before the brine

Salting the meat

The Poor-Man's Smoker. Yes, that's just a cast iron pan with wood in it.

Removing the skin

Sacrificial tasting slice

 

How was it? Delicious. You can slice it thick, giving a good combination of crunch and chew.

My next project will either be some salami, or perhaps a more complex Silverlight physics sample. Depends on how hungry I get, and whether the book store can get that Charcuterie book in stock!

 

I'm so hungry now.

 

Avi

Computer Programming isn't Art
25 August 08 01:24 PM | Avi Pilosof | 6 Comments   

(Sorry, Mr Knuth)

My uncle's hobby is building beautiful furniture from wood. I noticed something last time I spoke with him about it: He doesn't talk about a table, or a book case, or a dresser; at least, that's not what he talks about with emotion. You only get a sense for what he loves doing when he describes how everything fits together, how he makes it. He cuts and dries trees to make the timber, he cuts it all down to size, and then he never use screws or nails - all the joints fit perfectly.

Similarly, when I talk to my colleagues about software that they're writing, they might be excited about what the software does, but they're more interested in discussing how it does it, how they put it together. They're proud of code which is efficient, clean, inventive, performant. A crafty data structure, a SQL query which performs 10x faster than its predecessor, an intelligent cache that cuts processing requirements by 80%. Those are cool - those get you the street cred :)

I see large parallels between the two. They don't fit my understanding of what "Art" is; they sound alot more like a craft. Art is about evoking and exploring emotion, about connecting with the viewer. When I hear "craft", I think of a blacksmith, a woodworker, a chef; someone who takes pride not just in the end result, but in the path used to reach it. It's not just about creating something nice, it's also about doing it with pride. This seems to fit more closely to the practice of writing software, to the constant need for study and improvement that characterizes it. At least; those are the kinds of engineers I would prefer to work with.

 

Avi

 

Back in Seattle
18 August 08 02:19 PM | Avi Pilosof | 0 Comments   

I recently came back from visiting Seattle (where I'd lived and worked for 7 years before returning to Sydney). My wife was presenting at TechReady, so I figured I'd hitch a ride and make a vacation of it. It was also a good opportunity to meet up with my team and try to remember what their faces looked like :)

Random thoughts from the trip:

  • The Pacific Northwest is just stunning in summer. Mind you, I'm not convinced that cold and grey for 7 out of 14 days should really qualify to be called "Summer".
  • Due to TechReady, downtown Seattle was packed with MS nerds from all over the world; Almost 6000 from what I heard. It was definitely evident when walking around in the morning and seeing a whole sea of TechReady badges converging on Pine St. It was also weird seeing restaurants and pubs packed on weekday evenings. The city seemed to have a little more vibrancy to it.
  • In what might be the coolest technological feat I've seen since the Amiga, building 41 (and probably the rest) now has tiny touch-screen LCDs outside every conference room. You can see who's supposed to be there, find a replacement room, etc. They even make beeping noises.
  • Microsoft is going even greener: The new eating utensils are all bio-degradable. I'm told that the forks/knives/spoons are made from a weird mixture of potatoes and/or corn and/or interns. They definitely have a faint smell of corn oil (interns?), but contrary to what I was told I couldn't get them to melt in either hot water or diet coke. Judging by the latter experiment, perhaps NASA might have a new material for the Shuttle's construction?
  • Downtown Bellevue looks like Dubai, and areas around the main MS campus are similarly festooned with cranes. The amount of construction going on in the area is scary.
  • In the past 5 years or so, my weight has fluctuated between about 170 and 171 lbs; even on vacation; even when being force-fed by in-laws. However, after 2 weeks in Seattle of hanging out with friends who have an inhuman capacity for drinking beer, I suddenly find myself about 7lbs heavier. Ouch.

It was a great trip, great to see the old friends, who now need to plan a trip to Sydney :)

 

Avi

Getting Code Coverage to Work in Visual Studio
18 August 08 01:49 PM | Avi Pilosof | 1 Comments   

Are you sick of seeing this error?:

"Code coverage is not enabled for this test run"

I was working on a project where my COV results were always giving the above error, and it took some figuring out. Basically, it came down to two things:

  1. I had the following setting switched off, so I wasn't being warned.
  2. I was using Ctrl+R, Ctrl+A to run my tests. That puts them in Debug mode (which isn't supported with COV), and which I had never realized. I should have been using Ctrl+R, A (ie; taking my finger off the Ctrl key before pressing 'A').

You live, you facepalm, you learn.

 

Avi

Creating Testable Applications Using the MVP Pattern
03 August 08 05:23 AM | Avi Pilosof | 1 Comments   

(The following post talks about ASP.NET, but it actually applies to all UI-based applications, web and non-web. PHP, WPF, Winforms, etc. It does not require a framework, nor anything to install - it's just an interesting way to write your code such that it has a clear separation of concerns).

 

The Problem

Your typical ASP.NET application is difficult to test, because much of the logic is contained within the codebehind files, which derive from Web.UI.Page, which needs an HttpContext, which is difficult to mock. Furthermore, the output of the methods in the codebehind is often not easily-testable, because it's a side-effect (such as calling DataBind() on a GridView).

This same problem exists in most UI frameworks: How do you test the logic used to generate a UI without having to take heavy dependencies on that UI's implementation (thereby complicating the tests immensely)?

 

The Goal

Ideally, you'd be able to take all the logic you want to test and put it outside of the UI framework (ie; the ASPX codebehind), such that you can test that logic separately without depending on that UI framework. You want your logic to be able to update the UI, but at the same time need to know as little as possible about the internals of that UI.

 

Achieving the Separation Using the Model-View-Presenter Pattern

The Model-View-Presenter (MVP) pattern separates your app into three parts:

  • The Model: This is the same object model that you currently use for your business objects, data objects, whatever. This remains unchanged.
  • The View: This is your ASPX file; but it's as thin and dumb as possible. By that I mean that it only renders data that has been given to it; it doesn't do any thinking about what data it should have and where to get it.
  • The Presenter: This is the brains of the operation. The View asks it to act on the user's desires; it consults the model, then tells the view what to render (although not how to render it - that's the key part!).

The key to all this is that the Presenter avoids having a dependency on the View by having all the View's data requirements defined in an interface, and creating the dependency on that interface instead of on the view. That can then easily be mocked up for testing.

All this is best presented as a before/after example. Here's the scenario: We have a page that needs to display a list of people, filtered by a user-defined string. In order to do this, it eventually needs to bind a list of people to a GridView, which will take care of the rendering.

 

The "Before" Code for handling the user's button press to filter the list:

In PeopleView.aspx.cs:

   1: void btnFilter_Click(object sender, EventArgs e)
   2: {
   3:     ShowSomePeople(txtFilter.Text);
   4: }
   5:  
   6: private void ShowSomePeople(string filter)
   7: {
   8:     List<Person> lst = LoadAllFromDB().Where(p => p.Name.Contains(filter)).ToList();
   9:     gvwPeople.DataSource = lst;
  10:     gvwPeople.DataBind();
  11: }
 
(Here, the LoadallFromDB() call is a call to the Model to get a list of all Person objects from the DB).
 

The "After" Code:

First we create the interface definition which defines what data our View needs in order to render the page. We know it needs the actual list of people to show, and maybe an error message in case of problems. So, in PeopleInterface.cs:

   1: public interface IPeopleInterface
   2: {
   3:     // The list of people to show
   4:     List<Person> Members { get; set; }
   5:  
   6:     // Show this if something went wrong
   7:     string Error { get; set; }
   8: }

In PeopleView.aspx.cs (the codebehind), we implement this interface:

 
   1: public partial class PeopleView : System.Web.UI.Page, IPeopleInterface
   2: {
   3:  // ...
   4: }
   5:  

Then we update ShowSomePeople to call into the Presenter, instead of doing its own thinking:

   1: private void ShowSomePeople(string filter)
   2: {
   3:     PeoplePresenter Presenter = new PeoplePresenter(this);
   4:     Presenter.LoadWithFilter(filter);
   5:     gvwPeople.DataSource = this.Members;
   6:     gvwPeople.DataBind();
   7: }
 
Finally the Presenter itself, in PeoplePresenter.cs (a standalone class):
 
   1: public PeoplePresenter(IPeopleInterface v)
   2: {
   3:     View = v;    // "View" is a private member
   4: }
   5:  
   6: public void LoadWithFilter(string filter)
   7: {
   8:     // Tell the view what data it should be showing
   9:     View.Members = this.LoadAllFromDB().Where(p => p.Name.Contains(filter)).ToList();
  10: }

 

The interface defines the "contract" between the View and the Presenter, allowing the Presenter to be agnostic of the guts of the View. In theory if you wanted to add a WPF client for this application, you would not need to touch the Model or the Presenter or your unit tests - you would just implement a WPF view that adhered to the same interface.

It may look like much more code, but note that it's just a couple of tiny classes per page; this overhead is fixed and doesn't grow with the complexity of your ASPX page.

 

Unit Testing

Because you've made your view "dumb" (it mostly just databinds), all your business logic exists in the Presenter and Model, meaning that you probably don't need to test the View. In order to test the Presenter, all you need to do is pass it a mocked up implementation of the interface that it asks for. So:

  1. Create a mock that implements the interface (example).
  2. Pass that into a new Presenter.
  3. Run the Presenter methods.
  4. Check the properties of the mocked object to see that the Presenter set them correctly.

 

What about MVC?

As Scott Guthrie has mentioned, MVC is not the new recommended method of building ASP.NET apps; it's just another method. I love the simplicity of the MVC framework, but my team has invested in server controls that are incompatible with it, making it too costly to move to at this point.

MVP gives us similar benefits (testability), without the cost of re-wiring our controls, and with pretty much no learning curve.

 

Notes:

  • Microsoft's Web Client Software Factory gives you templates for project creation which implement this pattern for ASP.NET.
  • If you want a comprehensive comparison and history of MVC, MVP and related patterns, see this article.

 

Avi

Code commenting? Try Business Commenting.
25 July 08 02:12 PM | Avi Pilosof | 16 Comments   

Jeff has a good post here about code comments and that they shouldn't be used as crutches:

Coding Horror: Coding Without Comments

I agree with the article, but one thing I rarely hear mentioned is that it's often more interesting to comment the business justification than it is to comment the code. Jeff goes some way towards suggesting this when he says to comment the "why", but for many people that translates to "Why did I write the code this way?".

I often find that code I'm maintaining is missing comments regarding the business logic. Rather than "Why is the code designed like this?", something like "Why is the business process designed like this?"

This becomes most useful when you look at unfamiliar code (your own, probably) and decide that the easily-digestible code is nevertheless doing something you are sure is silly, and you change it. Big mistake.

Example of a code architecture comment:

   1: // We cache these values because they only change once per
   2: // week or so.

Or:

   1: // We lazy-load these properties because they're rarely used
   2: // and they chew up a decent amount of RAM.

 

Example of a business architecture comment:

   1: // We copy the lab owner on this mail because even though
   2: // they don't own this resource, they wanted to be aware
   3: // of changes. See bug 54321 for request and history.

Or:

   1: // This used to delete items older than 30 days, but
   2: // bug #65432 requested that an exception be made for
   3: // items that are of type X.

 

All are useful, but I find the second type are rarely used. They become more valuable as the code ages.

 

Avi

More Posts Next page »
Page view tracker