Damon Payne: Hand waving software architect

103db signal to noise ratio at < .03% total harmonic distortion
Solution Architect, software developer, geek
Damon Payne at Blogged
2007 Microsoft MVP - Solution Architecture
 Sunday, September 14, 2008

If you are just joining, you might want to start from the beginning: http://www.damonpayne.com/2008/09/14/RunTimeIsDesignTimeForAGT0.aspx

Layouts and Modules

I’m about to steal some ideas that are in common use, ideas from PRISM, Castle Windsor, the Visual Studio design-time environment, and a thousand other things.  I’ll briefly explain some of the core concepts here.

Modules or Services

In a well designed Windows Forms application, one would have a proper separation of concerns.  Suppose my form was for place orders for big purple couches.  I would have a CouchOrderForm that implements ICouchOrderView with very little logic: populating values or potential values only, perhaps with Data Binding, and event handlers.  These event handlers would delegate the actual work to a CouchOrderPresenter of some kind.  CouchOrderPresenter would probably use to a CouchOrderDataAccess class or perhaps a CouchBussinessProcess class to accomplish the necessary tasks, and update the UI using a combination of data binding and events.  I know people who would frown upon this as “primitive”, but even getting this far would be miles ahead of the average .NET project I saw as a consultant.

For testability, this design accomplishes a fair bit of concern-separating.  I can test almost my entire program without messing around with “how do I test the logic that’s in the Form?”  For example, I have often created a unit test class that implements the ICouchOrderView interface and does asserts in the various callback.  This means that a database and whatever other “real” components are required must be present and that might be very Heavy for unit test time.  If my code was written to use ICouchDataAccess instead of CouchOrderDataAccess, it’s suddenly much easier to mock out parts of my framework.  I might also be able to supply different ICouchDataAccess implementations at run time if that makes sense for my program – though I think this need is often horribly misunderstood and over used by many advocates. 

When I originally encountered this idea we called it the Service Locator Pattern in Java.  The term “service” is a loaded word.  I used to ask interview questions along the lines of “What is a Class? Now what is an object?  Now what is a Component?  Now what is a Service?”  I would change terminology here to say “module” or “strategy”, but Service is the commonly known semantic of our times, so I’ll use service to talk about program chunks that are essentially managed components.  My Component Container/Service Locator will be a ServiceManager.  It might be useful to have some common behaviors for these components, so IService will be the base interface for components.

When I add these things to the IoC project, I notice that when I choose to Add à new item, “Interface” is not an option for Silverlight projects.  Doesn’t MSFT want us to take Silverlight seriously?

The idea is that the ServiceManager will figure out what classes are available when a component asks for an IFoo implementation.  I’ve seen lots of fascinating ways of doing this in Full Framework projects.  Config files, loading every assembly in a certain directory, manually resolving at program startup, and others.  In Silverlight, the AppManifest.xml file contains a “Deployment.Parts” tag, which contains all the assemblies that get deployed with your app.  Still, I don’t seem to be able to call Assembly.Load() on these, so for now I’m going to go with manual resolution at program startup.

Generics make getting started on a service locator a breeze:

    public class ServiceManager

    {

        static ServiceManager()

        {

            _services = new Dictionary<Type, object>();

        }

 

        private static Dictionary<Type, object> _services;

 

        /// <summary>

        /// Pass in a list of strong types and determine what service interfaces they provide to us:

        /// </summary>

        /// <param name="types"></param>

        public static void Manage(List<Type> types)

        {

            Type baseServiceType = typeof(IService);

 

            foreach (Type t in types)

            {

                Type[] interfaces = t.GetInterfaces();

                foreach (Type iface in interfaces)

                {

                    if(iface != baseServiceType)

                    {

                        Type svcInterface = iface.GetInterface("DamonPayne.AGT.IoC.IService", false);

                        if (null != svcInterface)//We know this interface extends IService

                        {

                            _services.Add(iface, ObtainDefaultInstance(t));

                        }

                    }

                }

            }

        }

 

        /// <summary>

        /// Since an object could implement more than one interface, we may have in in here under multiple keys

        /// </summary>

        /// <param name="t"></param>

        /// <returns></returns>

        protected static object ObtainDefaultInstance(Type t)

        {

            object o = null;

            var q = from val in _services.Values.AsQueryable<object>()

                    where val.GetType().IsAssignableFrom(t)

                    select val;

 

            o = q.FirstOrDefault<object>();

            if (null == o)

            {

                Type[] noArg = new Type[0];

                o = t.GetConstructor(noArg).Invoke(null);

            }

 

            return o;

        }

 

        public static TServiceType Resolve<TServiceType>()

        {

            TServiceType svc = default(TServiceType);

            Type t = typeof(TServiceType);

            if (_services.ContainsKey(t))

            {

                svc = (TServiceType)_services[t];

            }

            else

            {

                throw new NotSupportedException(string.Format("A provider for {0} was not found", t));

            }

            return svc;

        }

 

 

    }

Now I need something to use to test this out.  I think I might want a Logging service of some kind to display messages to me, so I’ll create an ILogService interface in the IoC project:

    public interface ILogService: IService

    {

        void Debug(string message);

    }

 

And I’ll need to provide an implementation somewhere.  For now I’ll add a folder/namespace to the Layout project and create a simple do-nothing implementation.  In the Application_Startup event handler I can now start setting things up.  I create a MemoryLogger and:

        private void Application_Startup(object sender, StartupEventArgs e)

        {

            this.RootVisual = new Page();

            List<Type> s = new List<Type>

            {

                typeof(MemoryLogger)

            };

            ServiceManager.Manage(s);

 

            ILogService logger = ServiceManager.Resolve<ILogService>();

            //success!

        }

Now I’m starting to get somewhere.  I’m planning on creating an implicit way to get these services later.  There seems to be two schools of thought here: setting services on serviced components using a property or field, and supplying all the necessary services in the constructor.  For my next step, though, I want to get some things on the screen to help visualize how this is going to fit together.

Composite UI

My notion of a Region Manager is stolen directly from “Composite Application Guidance for WPF”.  We can abstract out the idea of a Window and various views and evolve the idea of how the UI should function over time.  I know that I will have the notion of a View, which is docked into a Region, which is managed by a RegionManager of some kind.  First, we think about these interactions and come up with a first try at interfaces.  Just to start getting some things on the screen, a View will be just this:

    public interface IView

    {

        /// <summary>

        /// Grid, etc. extends canvas

        /// </summary>

        Canvas LayoutRoot { get; }

    }

I’m not sure that I need an explicit implementation of a Region, so my IRegionManager consists of the following:

    public interface IRegionManager : IService

    {

  UIElement RootVisual { get; }

        List<string> RegionNames { get; }

        void AddView(IView v);

        void AddView(IView v, string regionName);

        IView GetView(string regionName);

        List<IView> Views { get; }

    }

The IRegionManager implementation does what I need it to do for now.  Clearly, our Page is a suitable class for serving as the Region Manager.  In the last article I envisioned three regions, but now that I’ve got my logging idea I’ll extend it to four.  I need a region for a Toolbox, Designer Surface, Property Grid, and Message Console.  Implementing the IRegionManager interface and supplying some default content to be sure I’m laying things out the way I want, the XAML and runtime application looks like so:

<UserControl x:Class="DamonPayne.HTLayout.Page"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Width="800" Height="600">

    <Canvas x:Name="LayoutRoot" Background="White">

        <Grid x:Name="_regionArea" Background="Red" Width="800" Height="600">

            <Grid.RowDefinitions>

                <RowDefinition></RowDefinition>

                <RowDefinition></RowDefinition>

            </Grid.RowDefinitions>

            <Grid.ColumnDefinitions>

                <ColumnDefinition></ColumnDefinition>

                <ColumnDefinition></ColumnDefinition>

                <ColumnDefinition></ColumnDefinition>

            </Grid.ColumnDefinitions>

            <Canvas x:Name="Toolbox" Grid.Row="0" Grid.Column="0" Width="Auto">               

                <TextBlock>I am toolbox</TextBlock>

            </Canvas>

            <Canvas x:Name="DesignSurface" Grid.Row="0" Grid.Column="1" Width="Auto">

                <TextBlock>I am design surface</TextBlock>

            </Canvas>

            <Canvas x:Name="PropertyGrid" Grid.Row="0" Grid.Column="2" Width="Auto">

                <TextBlock>I am property grid</TextBlock>

            </Canvas>

            <Canvas x:Name="MessageConsole" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3">

                <TextBlock>I am the message console</TextBlock>

            </Canvas>

        </Grid>

    </Canvas>

The IRegionManager implementation is incredibly straightforward and in the source code at the end of this article.  Applying some refactoring we get the ServiceManager to create the Page for us and the application startup now looks like this:

        private void Application_Startup(object sender, StartupEventArgs e)

        {           

            List<Type> s = new List<Type>

            {

                typeof(MemoryLogger),

                typeof(Page)

            };

            ServiceManager.Manage(s);

 

            IRegionManager mgr = ServiceManager.Resolve<IRegionManager>();

            RootVisual = mgr.RootVisual;

            //success!

        }

The next thing to decide is how IViews get loaded into our IRegionManager on startup.  It would be nice if I could use the same mechanism for IService wire-up, but this article is getting a bit long already.   For now, I’m going to create the visual design for the message console to set up the next article.  I create a Controls folder/namespace to hold my application specific UI components and put a MessageConsoleView in.  This component will be a UserControl that implements IView.  As soon as I implemented IView, however, I see that LayoutRoot was a poor choice of name since it forced an explicit interface implementation, so I refactor to IView.VisualRoot instead.  I also see that when I said “Canvas” was a good root because Canvas and Grid derived, I meant “UserControl”, so I refactor that too.

Now I have something to think about for the next article.  The MessageConsoleView will obviously need to get at my ILogService implementation…

Licensing

Now that I’ve got some code in here, I should decide on how I’m going to share that.  The Creative Commons license seems to fit my goals.  I’m not super up to date on open source license wording, but this one will do for now, in code-snippet form:

<?xml version="1.0" encoding="utf-8"?>

<CodeSnippets

    xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">

  <CodeSnippet Format="1.0.0">

    <Header>

      <Title>

        Creative Commons License

      </Title>

    </Header>

    <Snippet>

      <Code Language="CSharp">

        <![CDATA[

/*

Creative Commons - attribution-noncommerical-share alike 3.0 Unported

          You are free to copy, distribute, and transmit this work.

          You are free to adapt the work.

        Under the following conditions:

          -You must attribute the author(Damon Payne, http://www.damonpayne.com) for this work but not in a way

          that suggests the author endorses you or your use of the work.

          -You may not use this work for commercial purposes

          -If you alter, transform, or build upon this work, you are free to distribute the work under the same or similar license to this one.

*/

        ]]>

      </Code>

    </Snippet>

  </CodeSnippet>

</CodeSnippets>

If anyone out there wants to provide input on this license, please do.

Source code throug this article: DamonPayne.AGT[1].zip (538.15 KB)



Sunday, September 14, 2008 3:12:17 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

Often times, the act of explaining a question out loud to another person involves a lot of complex and fleeting thought, we think at over 600 words per minute, and coalescing into sentences for the transmission from mental constructs to speech.  The thought process involved in this preparation for vocalization often restructures the problem in such a way that the question is answered in the asking.  An early mentor of mine called this effect “Talking to the bear”; he had a teddy bear on top of his monitor who served as a sounding board for his toughest issues, and later my issues as well. 

For a while, I’ve wanted to do some Reality Blogging.  Take a complex idea and a set of design goals, state them out loud, and slowly work towards a completed product or module.  Often times, I have presented an idea in, say, eight parts, because the idea was large enough to warrant breaking up for explanation.  The idea was fully baked before the first part was published.  The notion here, rather, is not to complete the idea but rather to work with both Visual Studio and Word open; to state design goals and see how they pan out later, to see how easy it was to refactor, to see a complex idea emerge out of nothing.

The Goal

The goal is to have an extensible, lightweight design surface and design-time environment for Silverlight.  As I have written about in the past (http://www.damonpayne.com/2007/04/24/Designer0.aspx) there are any number of neat reasons for which it would be useful to have  design time capabilities at runtime in applications.  My example application will be a Home Theater Layout application for Silverlight.  The vision is something like this:

So, I want this to be able to work in a manner we are familiar with: I drag things out of a toolbox onto a design surface, where I am allowed to drag and resize things.  When I select things I will be able to edit the selected item using a property grid.  I’ll want to support limited undo and be able to select multiple things to move around and so forth.

 In Windows forms, you can host the designer in your own application though it’s a lot of work. When the time came to make tradeoffs, the WPF designer “Cider” was not made hostable outside of Visual Studio.  Besides, there is no PropertyGrid for Silverlight, and no <<lots of other stuff>> for Silverlight, so this is really a big effort. 

Silver is Argentum in Latin, ergo its period table symbol is AG.  Ergo is also Latin, how fun.  Anyway, all the cool kids abbreviate Silverlight as “AG” and nothing sensible like “SL”.  The Latin word for “design” is more or less tela.  I dub thee, The ArGentum Tela project, or AGT for short. 

Design Goals

As Anders Heijlsberg said way back when he was famous for something called Delphi, it’s good to start out with some goals, it’s good to think about “how do I want people to use this?”  I have some design goals and functional requirements:

·         I want different aspects of the UI to function independent of each other

·         I want to be able to swap out different implementations for any aspects of the program that makes sense for

·         I want component to component communication to happen without components having references to each other

·         I want the building blocks of my program to feel like Serviced Components

·         I want to be able to serialize the results of my designer surface for later retrieval

·         I want a healthy # of common tasks accessible through the keyboard

·         I want this to work on the Mac.  I thought everyone was running Windows by now but apparently there are literally dozens of Mac users out there, so I should take their poor incomplete keyboards into account.

·         I want to package this as a reusable design-time library with a Reference Implementation – the home theater layout tool

·         I want to have as little code-behind/presenter as possible

I’ve been re-reading my GoF book, arguing with Dan almost every day, and reviewing the way the PRISM code looks.  I have some ideas about how I want to accomplish all of this.  As of now, my solution looks like this then:

·         DamonPayne.AGT

o   DamonPayne.HTLayout – the Silverlight sample project

o   HTLayoutWeb – a web test project for HTLayout

o   DamonPayne.AGT.IoC – how’s that for a name?  I’m going to pursue some inversion of control concepts for Silverlight, and that stuff will live in this assembly.

o   DamonPayne.AGT.Design – the code that makes the designer work will live here.

 

What’s next?

I’ve got Word open and Visual Studio open; it’s time to get started.  Where to begin?  In the next article