CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Jeremy D. Miller -- The Shade Tree Developer

Under the hood and working with .Net, TDD, Software Design, and Agile Stuff

Build your own CAB #13 - Embedded Controllers with a Dash of DSL

Start with http://andersnoras.com/blogs/anoras/archive/2007/07/04/i-m-coming-down-with-a-serious-case-of-the-dsls.aspx and come back.

Just to continue the world's longest run on sentence.  Before I start, here's the table of contents for the "Build your own CAB" series:

  1. Preamble
  2. The Humble Dialog Box
  3. Supervising Controller
  4. Passive View
  5. Presentation Model
  6. View to Presenter Communication
  7. Answering some questions
  8. What's the Model?
  9. Assigning Responsibilities in a Model View Presenter Architecture
  10. Domain Centric Validation with the Notification Pattern
  11. Unit Testing the UI with NUnitForms
  12. Event Aggregator
  13. Rein in runaway events with the "Latch"
  14. Embedded Controllers with a Dash of DSL (This Post)
  15. MicroControllers - Forthcoming
  16. Subcutaneous Testing - Forthcoming
  17. Creating the Application Shell - probably a couple posts
  18. Wiring the Components with an IoC tool - Forthcoming

Why is this post necessary?

Why, you might ask, are you writing a post on what amounts to a "ViewHelper?"  One simple reason - View's can easily become absolutely massive blobs of code.  Any chance to move a cohesive set of screen responsibilities into another class should serve to make the View itself simpler, and that's all this post is about.  Plus, breaking a View's behavior into multiple, cohesive classes can potentially lead to reuse opportunities for the little Embedded Controller classes.

For much of the last three years I've worked with a lot of legacy code over a half-dozen different codebase's.  All of them, to be charitable, were less than ideal in quality and structure.  If you ask me what the single biggest flaw or problem across all of these codebase's my answer would be near automatic -- long classes and long methods.  What I continuously see is code stuffed into modules until the modules are coming apart at the seams. 

Come to think of it, my current project is about 95% greenfield code so far.  Come Thanksgiving time when it's my turn to say what I'm thankful for my answer might just be "I'm thankful for getting six months of working on a greenfield project."

Embedded Controller

Again, I'm not a sanctioned patterns naming body, but the term "Embedded Controller" is my name for nonvisual classes that help a View control some distinct part of it's behavior.  The first example that comes to my mind is from a WinForms project that used a 3rd party grid (not a vendor on the CodeBetter friends list by the way).  The grid control needed a lot of consistent help and infrastructure code around it to implement the behavior we needed (little things like sorting and paging).  We quickly discovered that additional screens needed the exact same bootstrapping code, so the obvious answer was to extract that "grid helper" into it's own reusable class.  We were using a pretty strict Passive View approach, but even so, we didn't want the Presenter's tied that tightly to the screen mechanics.  Instead, the new GridHelper class was just something that the View controls used internally.  After the third screen with the grid control, development suddenly went faster. 

To differentiate Embedded Controller classes somewhat from the Presenter, here's the attributes of an Embedded Controller:

  • Nonvisual class used by a View to implement some of the View behavior.  I guess in
  • Completely encapsulated within a View.  There is no sign of the Embedded Controller in a View's publicly facing interface.
  • Embedded Controller's are happily aware of the actual, concrete UI widgets.  The Embedded Controller class "knows" about buttons and checkbox's and the nasty 3rd party grid that you're being forced to use.
  • Very limited in scope.  An Embedded Controller provides classical controller functionality for a very specific part of the screen

 

My advice for taking advantage of Embedded Controller's is threefold:

  1. Look for common UI coding tasks within a system and look for opportunities to encapsulate some screen mechanics in reusable pieces.  This is just another exercise in eliminating duplication.
  2. Split up any View class that gets too big.  I might have left the impression in earlier posts that the View code is somehow exempt from the normal coding standards because we've made it "simpler" now.  Code quality matters everywhere, and especially in areas of the code that are likely to change over time -- like View's.  Even with a Passive View architecture a complex screen will almost inevitably lead to complex code in the View.
  3. Pulling out an Embedded Controller might be an easy way to extend unit testing deeper into the View.  This won't always be true, but a "POCO" embedded controller class can often be quite easy to unit test inside vanilla xUnit tests without resorting to anything exotic like NUnitForms.  There is some widget behavior that only functions when a Form is visibly displayed, but a lot of behavior can be tested just by instantiating UI widgets directly within a unit test.  One way or another the UI widget stuff is nothing but CLR classes.

 

Sample:  The Control State Machine

Here's a scenario from my current project that I bet all of us have dealt with a few times over.  We have a Trade screen that has seven different states depending upon whether you're creating or reviewing a Trade.  The various user actions available on the screen differ from state to state.  As the screen changes state either upon opening the screen or a result of user actions while it's open we need to enable/disable and show/hide different screen elements.  The screen started simple, so I just coded specific methods at first to enable or disable bits of the screen.  Fast forward a couple of weeks and the behavioral logic had exploded (funny how that happens when you actually get to talk to the end users).  Unsurprisingly, the code in the Presenter and View had become hairy, plus the screen had way too much flicker for that matter. 

Before I show any code, let's be pretty clear that this code is not very optimized or even very powerful.  All I want to talk about is the concepts and structure of the design irrespective of performance.

At least in concept, the solution was pretty simple.  Move that logic into a state machine construct.  Since we already had a full set of regression tests against the UI screen itself, I felt pretty safe making the changes.  All I did was create a class called "ControlState" that's nothing but a collection of Control's to display and enable for a particular screen state.  ControlState has a method called Activate() which simply loops through its internal collection to enable and show the configured controls (it's not shown but the call to Activate() is wrapped in SuspendLayout() and ResumeLayout()). 

 

        public class ControlState
        {
            private List<Control> _displayedItems = new List<Control>();
            private List<Control> _enabledItems = new List<Control>();
 
            public void ShowControls(params Control[] controls)
            {
                _displayedItems.AddRange(controls);
            }
 
            public void EnableControls(params Control[] controls)
            {
                _enabledItems.AddRange(controls);
            }
 
            public void Activate(ControlStateMachine<T> machine)
            {
                machine.LevelSet();
 
                foreach (Control item in _enabledItems)
                {
                    item.Enabled = true;
                }
 
                foreach (Control item in _displayedItems)
                {
                    item.Visible = true;
                }
            }
        }

As you can probably guess, there's a second class that aggregates all of the configured ControlState's and Control's called ControlStateMachine<T>, where T is an enumeration of the possible states.  Here's a little bit of its implementation. 

 

        private readonly Control _parent;
        private readonly IScreenBinder _binder;
        private List<Control> _displayedItems = new List<Control>();
        private List<Control> _enabledItems = new List<Control>();
        private Dictionary<T, ControlState> _states = new Dictionary<T, ControlState>();
        private T _currentState;
 
        public ControlStateMachine(Control parent, IScreenBinder binder)
        {
            _parent = parent;
            _binder = binder;
        }
 
        public void SetState(T stateKey)
        {
            _parent.SuspendLayout();
 
            _states[stateKey].Activate(_binder, this);
            _currentState = stateKey;
 
            _parent.ResumeLayout();
        }

The View itself just calls ControlStateMachine.SetState() to configure itself.

Now, for the cool part.  Here's a somewhat obfuscated version of our code that defines the state machine inside the View.* 

        private void configureStateMachine()
        {
            _stateMachine = new ControlStateMachine<TradeDetailState>(this, _binder);
 
            _stateMachine.OnStateChangeTo(TradeDetailState.Creation)
                .Show(createTradeButtonsPanel)
                .Enable(status1CheckBox, status2CheckBox, status3CheckBox, externalTradeIdTextbox);
 
            _stateMachine.OnStateChangeTo(TradeDetailState.Review)
                .Show(updateTradeButtonsPanel)
                .IsReadOnly()
                .Enable(
                    editTradeButton, 
                    status1CheckBox, 
                    status2CheckBox, 
                    status3CheckBox, 
                    externalTradeIdTextbox, 
                    cancelTradeButton);
 
            _stateMachine.OnStateChangeTo(TradeDetailState.ReviewDirty)
                .Show(updateTradeButtonsPanel)
                .IsReadOnly()
                .Enable(
                    undoButton, 
                    status1CheckBox, 
                    status2CheckBox, 
                    status3CheckBox, 
                    submitTradeChangesButton, 
                    externalTradeIdTextbox, 
                    cancelTradeButton);
 
            _stateMachine.OnStateChangeTo(TradeDetailState.Edit)
                .Show(updateTradeButtonsPanel)
                .Enable(externalTradeIdTextbox, cancelTradeButton);
 
            _stateMachine.OnStateChangeTo(TradeDetailState.EditDirty)
                .Show(updateTradeButtonsPanel)
                .Enable(undoButton, submitTradeChangesButton, externalTradeIdTextbox, cancelTradeButton);
 
            _stateMachine.OnStateChangeTo(TradeDetailState.Cancelled)
                .Show(updateTradeButtonsPanel)
                .DisableEverything()
                .IsReadOnly();
 
            _stateMachine.OnStateChangeTo(TradeDetailState.History)
                .Show()
                .DisableEverything()
                .IsReadOnly();
        }

If you haven't seen this kind of syntax before, it's what Martin Fowler (a real patterns naming authority) has christened Fluent Interface.  Why, oh why, did I go to the extra trouble of making a Fluent Interface instead of just defining the state machine by filling up the collection state (and let's be clear, it is a little more work)?  Because I thought it would make a good blog post wanted to create Domain Specific Language in the code to make the code easier to understand.

 

That's Not Really a DSL!

Unfortunately, in my opinion, the .Net community is fixated on graphical Domain Specific Language's (DSL) that revolve around yet more code generating visual tooling.  There's a complete other side to the DSL's however.  Another alternative is lexical languages which could be either internal/embedded or external to the language, with the pro-lexical argument being something like "people can read English you know."  I'm not particularly enamoured of creating my own programming language and interpreter, so my particular area of interest right now is in creating embedded DSL-like syntax's inside existing languages.  Granted, C# is very limited in this respect compared to other languages, but we can still achieve some useful gains with Fluent Interface coding. 

We're about to get IronRuby and C# 3 as fullblown CLR languages.  Both languages, and especially IronRuby, are far better suited for internal DSL's than C# 2.0.  The Ruby hype seems to be 9/10's Rails, but the stuff that ThoughtWorks and others are doing with expressive DSL development in Ruby might turn out to be the real value proposition behind Ruby.

So is my little ControlStateMachine a DSL or just an API?  Running it through the Is It a DSL or an API Checklist, the answer is probably no, but the real value is simply an expressive AP.  The real goal (or enabler) is to move toward coding syntax's and API's that speak clearly in the semantics of the problem domain.  The general idea is that the correctness, or intent, of the code should become much more readily verified through simple inspection.  Ideally, you could move to the point where you are able to show your business logic code to the actual business users.  There will still be plenty going on behind the scenes, but they don't need to see that.

 

David A. Black has a good post on Domain Specific Language that I'd recommend for a jumping off point.

I haven't delved deeply enough yet, but Boo sounds like a good way to create lexical DSL's in .Net today.

 

* It's obviously a screen for capturing a Trade, but since half of the developers in New York seem to be working on a similar project at any given time, I'm not too concerned about publishing this.



Comments

Brendan Tompkins said:

Jeremy, I think you've written a short book on this topic.  It's great!  

# July 6, 2007 5:19 PM

Sam Gentile said:

Multithreading and Concurrency Software Transactional Memory Part IV - Thread-Bound Transactions Software

# July 11, 2007 8:16 AM

Adventures of an aspiring agile developer in a not-quite agile world said:

Aside: I guess this post is really about mock frameworks rather than mocks, but I didn't want to break

# July 13, 2007 5:28 AM

Noticias externas said:

Aside: I guess this post is really about mock frameworks rather than mocks, but I didn&#39;t want to

# July 13, 2007 6:02 AM

Jeremy D. Miller -- The Shade Tree Developer said:

The title is a mouthful and accurately implies an alarmingly high jargon to code ration, but I just didn&#39;t

# July 24, 2007 5:18 AM

Jeremy D. Miller -- The Shade Tree Developer said:

Yes, this is overdue. Here is an introduction and table of contents to my &quot;Build Your Own CAB&quot;

# July 25, 2007 9:22 PM

Jeremy D. Miller -- The Shade Tree Developer said:

The title is a mouthful and accurately implies an alarmingly high jargon to code ration, but I just didn&#39;t

# July 26, 2007 10:57 AM

Jeremy D. Miller -- The Shade Tree Developer said:

To everybody that attended one of my talks at DevTeach this week. All of the materials are now online

# November 29, 2007 12:04 PM

hairy plus said:

Pingback from  hairy plus

# April 10, 2008 1:37 PM

Estado de las vistas (y II) « Carli??os Blog said:

Pingback from  Estado de las vistas (y II) &laquo; Carli??os Blog

# July 26, 2008 10:48 AM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add

About Jeremy D. Miller

Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy previously worked as a systems architect building mission critical supply chain software for a Fortune 100 company and learned agile development practices as a .Net consultant at ThoughtWorks, one of the pioneers of agile development. Jeremy is the author of the open source StructureMap (http://structuremap.sourceforge.net) tool for Dependency Injection with .Net and the forthcoming StoryTeller (http://storyteller.tigris.org) tool for supercharged FIT testing in .Net. Jeremy's thoughts on just about everything software related can be found on his weblog "The Shade Tree Developer" at http://codebetter.com/blogs/jeremy.miller, part of the popular CodeBetter site. Jeremy is a Microsoft MVP for C#. Check out Devlicio.us!

Our Sponsors

Free Tech Publications

This Blog

Syndication

News

All opinions expressed here constitute my (Jeremy D. Miller's) personal opinion, and do not necessarily represent the opinion of any other organization or person, including (but not limited to) my fellow employees, my employer, its clients or their agents.

About Me

"Best Of" Compendium

StructureMap (Dependency Injection for .Net)

StoryTeller (Supercharged Fit)

Build your own Cab

TestDriven

MVP