Construct, Initialise, Terminate and Destruct

Some patterns never die; they just seem to loose favour and inhabit the darker corners of software engineering practice. One such pattern is the “Construct, Initialise, Terminate and Destruct” pattern. Those of you who have been working in C++ or Real-time Systems will realise the importance of this pattern when constructor protection and two-phase termination are required.

Constructor protection is the requirement that the constructor does not throw any exceptions or die during the construction process. Here only the absolute minimum is undertaken to bring the object in a valid state. Construction of further objects and connections to external systems (to name a few) are then delegated to the Initialise procedure. The Initialise procedure will then take care of the more difficult and possibly error prone construction. At the other end of the object’s lifetime the Terminate process terminates all services etc. that were started in the Initialise method but in the reverse order.

Recently I have observed projects bringing together new technologies and uniting them with current patterns and frameworks. One such example is the MVVM pattern with the ever more popular Unity IOC. Using these two together seems like a dream come true when it comes to injecting all those dependencies through the constructor. This raises the point of “fat” constructors where all external class that provide services through the Separation of Concerns principle. Here great care must be taken to assure that all these helper classes are also built on the same CITD pattern otherwise the probability that constructors construct other objects that in turn start service and access databases starts climbing such that the application becomes fragile and unmanageable. Care must be taken to inject only those services that are really needed as the process can be difficult to debug if anything goes wrong.

The Initialisation step can be difficult to implement separately from the constructor in many cases. However, in the MVVM for both Silverlight and WPF the simplest solution is to attach a Command to the Load and Unload event of the View. When these Command are fired the execute Action can perform all the required initialisation and termination processing required.

As I mentioned previously there can be a controlled start-up and shutdown process involved in the Initialisation and Termination. The basic rules are:

  1. During initialisation take care of yourself first before taking care of your children. That means isolate and initialise all the services that lower processing requires before calling the lower processing
  2. Call the lower processing that is required by the current level.
  3. Finish off all other initialisation processing.

For the termination process it is similar but in reverse.

Care must be taken over the two phase initialisation and termination requirements. For example the termination process, the first phase, of the View Model will entail making sure that the Model has successfully terminated before the View Model can terminate. The Model then informs the View Model via an event that the Model has terminated and hence the second phase, that of View Model termination can be completed. Initialisation follows a similar pattern and, of course, depending on your MVVM flavour, the Initialisation and Termination process might entail more Models and/or Business Logic delegates.

So enough with the theory, what does the code look like? The example I will use is that of a WPF MVVM light application where a general MVVM will be considered.

The View

The View Model is referenced via the DataContext using the following mark-up in the XAML:

 DataContext="{Binding PostboxMessagesViewModel, Mode=OneWay, Source={StaticResource Locator}}"

The following XAML mark-up is required to link the Load and Unload event to Commands within the View Model:

    <Custom:Interaction.Triggers>
        <Custom:EventTrigger EventName="Loaded" SourceName="Page">
            <GalaSoft_MvvmLight_Command:EventToCommand x:Name="etcPageLoad" Command="{Binding LoadCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}"/>
        </Custom:EventTrigger>
        <Custom:EventTrigger EventName="Unloaded" SourceName="Page">
            <GalaSoft_MvvmLight_Command:EventToCommand x:Name="etcPageUnload" Command="{Binding UnloadCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}"/>
        </Custom:EventTrigger>
    </Custom:Interaction.Triggers>
    

 

The Model

Here the Initialise and Terminate methods are defined as well as the “Execute” handlers. The Model inherits from the interface ICtorInitTermDtor as it does not need to support a Command implementation. The following code shows the ICtorInitTermDtor interface:

    using System;

    public interface ICtorInitTermDtor
    {
        #region Event handlers

        /// <summary>
        /// Occurs when initialisation processing has completed.
        /// </summary>
        event EventHandler InitialiseCompletedEvent;

        /// <summary>
        /// Occurs when terminate processing has completed.
        /// </summary>
        event EventHandler TerminateCompletedEvent;
        
        #endregion

        #region Methods

        /// <summary>
        /// Initialises this instance. Called within the LoadCommand's "Execute" handler.
        /// </summary>
        void Initialise();

        /// <summary>
        /// Terminates this instance. Called within the UnloadCommand's "Execute" handler.
        /// </summary>
        void Terminate();

        #endregion
    }

The code in the Model is basically as follows:

    public class PostboxModel : BaseModel, IPostboxModel
    {

        #region Implementation of ICtorInitTermDtor

        /// <summary>
        ///   Gets or sets the load command. This command provides the connection between the View's
        ///   Load event and the View Model
        /// </summary>
        /// <value>The load command.</value>
        public RelayCommand LoadCommand { get; set; }

        /// <summary>
        ///   Gets or sets the unload command. This command provides the connection between the View's
        ///   Unload event and the View Model
        /// </summary>
        /// <value>The unload command.</value>
        public RelayCommand UnloadCommand { get; set; }

        /// <summary>
        ///   Gets or sets a value indicating whether this <see cref = "SearchPageModel" /> is terminating.
        /// </summary>
        /// <value><c>true</c> if terminating; otherwise, <c>false</c>.</value>
        protected bool Terminating { get; set; }

        /// <summary>
        ///   Initialises this instance. Called within the LoadCommand's "Execute" handler.
        /// </summary>
        public void Initialise()
        {
            this.LoadData();
        }

        /// <summary>
        ///   Terminates this instance. Called within the UnloadCommand's "Execute" handler.
        /// </summary>
        public void Terminate()
        {
            // stop all current requests to host
            this.Terminating = true;
            this.TerminateDataAccess();

            // remove all data containers
            this.UnloadData();
        }

        /// <summary>
        ///   Loads the data.
        /// </summary>
        private void LoadData()
        {
            if (this.UseLocalData)
            {
                this.LoadLocalData();
            }
            else
            {
                this.LoadLiveData();
            }
        }

        /// <summary>
        ///   Locals the live data.
        /// </summary>
        private void LoadLiveData()
        {
        }

        /// <summary>
        ///   Loads the local data.
        /// </summary>
        private void LoadLocalData()
        {
        }

        private void TerminateDataAccess()
        {
            if (!this.UseLocalData)
            {
            }
        }

        /// <summary>
        ///   Unloads the data.
        /// </summary>
        private void UnloadData()
        {
            if (this.UseLocalData)
            {
                this.Items.Clear();
            }
            else
            {
                this.LoadLiveData();
            }
        }

        #endregion
    }

The View Model

Here the Commands are defined as well as the “Execute” handlers. The View Model constructor has the Model injected as a parameter so:

    public interface IPostboxMessagesViewModel : IBaseViewModel
    {
    }

Additionally, the View Model implements the ICtorInitTermDtorCmd interface (which may also be implemented within a View Model base class)

    using GalaSoft.MvvmLight.Command;

    public interface ICtorInitTermDtorCmd : ICtorInitTermDtor
    {
        #region Commands

        /// <summary>
        /// Gets or sets the load command. This command provides the connection between the View's
        /// Load event and the View Model
        /// </summary>
        /// <value>The load command.</value>
        RelayCommand LoadCommand { get; set; }

        /// <summary>
        /// Gets or sets the unload command. This command provides the connection between the View's
        /// Unload event and the View Model
        /// </summary>
        /// <value>The unload command.</value>
        RelayCommand UnloadCommand { get; set; }

        #endregion

    using System;
    using System.Collections.ObjectModel;
    using System.Runtime.Serialization;
    using System.Windows.Input;
    using GalaSoft.MvvmLight.Command;
    using Interfaces;
    using Utilities;

    public class BaseViewModel
    {
        private readonly IBaseModel model;

        /// <summary>
        /// Initializes a new instance of the <see cref="BaseViewModel"/> class.
        /// </summary>
        /// <param name="pModel">The p model.</param>
        protected BaseViewModel(IBaseModel pModel)
        {
            this.model = pModel;
        }

        /// <summary>
        /// Called when the initialisation has been completed.
        /// </summary>
        protected void OnInitialiseCompleted()
        {
            var loc = InitialiseCompletedEvent;
            if (loc != null)
            {
                loc(this, new EventArgs());
            }
        }

        /// <summary>
        /// Called when the initialisation has been completed.
        /// </summary>
        protected void OnTerminationCompleted()
        {
            var loc = TerminateCompletedEvent;
            if (loc != null)
            {
                loc(this, new EventArgs());
            }
        }

        /// <summary>
        /// Occurs when initialisation processing has completed.
        /// </summary>
        public event EventHandler InitialiseCompletedEvent;

        /// <summary>
        /// Occurs when terminate processing has completed.
        /// </summary>
        public event EventHandler TerminateCompletedEvent;

        /// <summary>
        /// Gets or sets the load command. This command provides the connection between the View's
        /// Load event and the View Model
        /// </summary>
        /// <value>The load command.</value>
        public RelayCommand LoadCommand { get; set; }

        /// <summary>
        /// Gets or sets the unload command. This command provides the connection between the View's
        /// Unload event and the View Model
        /// </summary>
        /// <value>The unload command.</value>
        public RelayCommand UnloadCommand { get; set; }

    }

The actual ViewModel with the specific parts removed is as follows:

    using System;
    using System.ComponentModel;
    using System.Windows.Input;
    using GalaSoft.MvvmLight.Command;
    using Interfaces.Postbox;

    public class PostboxMessagesViewModel : BaseViewModel, IPostboxMessagesViewModel
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="PostboxMessagesViewModel"/> class.
        /// </summary>
        public PostboxMessagesViewModel()
            : base(null)
        {
            this.RegisterCommands();
        }

        /// <summary>
        /// Registers the commands.
        /// </summary>
        private void RegisterCommands()
        {
            this.LoadCommand = new RelayCommand(this.Load);
            this.UnloadCommand = new RelayCommand(this.Unload);
        }

        /// <summary>
        /// Unloads this instance.
        /// </summary>
        private void Unload()
        {
        }

        /// <summary>
        /// Loads this instance.
        /// </summary>
        private void Load()
        {
        }

        #region Implementation of INotifyPropertyChanging

        /// <summary>
        /// Occurs when [property changing].
        /// </summary>
        public event PropertyChangingEventHandler PropertyChanging;

        #endregion

        #region Implementation of ICtorInitTermDtor

        /// <summary>
        /// Initialises this instance. Called within the LoadCommand's "Execute" handler.
        /// </summary>
        public void Initialise()
        {
        }

        /// <summary>
        /// Terminates this instance. Called within the UnloadCommand's "Execute" handler.
        /// </summary>
        public void Terminate()
        {
        }

        #endregion

        #region Implementation of ICtorInitTermDtorCmd

        #endregion

        #region Implementation of IBaseViewModel

        /// <summary>
        /// Gets or sets the change view command.
        /// </summary>
        /// <value>The change view command.</value>
        public RelayCommand ChangeViewCommand { get; set; }

        #endregion

    }

That’s all folks…

Advertisements

~ by Intelligence4 on October 8, 2011.

 
%d bloggers like this: