Monday, February 20, 2017

Configure Xamarin.Forms iOS to use Caliburn.Micro

We’re continuing with our series of getting started with Xamarin.Forms and Caliburn.Micro, check out these previous posts if you’re interested:

Today, we’ll set up our Xamarin.Forms iOS project. Unlike the Droid project, we can get our project up and running with very few changes to the default template. So we'll start this post with configuring our Visual Studio instance to talk to our Mac.

And before you ask, unfortunately, although it's C# you absolutely need a Mac to compile and run the solution. On the plus side, you can do most of your development on your PC.

Verify Prerequisite Software on your Mac

You will need the following installed on your Mac:

  • XCode 7+
  • Xamarin Studio
  • Xamarin.iOS SDK

Setup your Mac for Remote Access

Before you can configure Visual Studio to use your Mac as a Build Host, you'll need to configure your Mac to allow users to remotely login to your machine. You can get the full details following this post, but in short we can simply set up our mac using the following:

  • Press Command ⌘ + Space to bring up Spotlight Search
  • Type Remote Login to open Sharing
  • Enable Remote Login and then specify Allow access for: All Users 

Configure your Build Host

Now that your Mac will accept remote logins, we can now configure Visual Studio on the PC to use your Mac as a build host.

  • From your PC, open our XF.CaliburnMicro project.
  • In the toolbar, click on the Xamarin Mac Agent icon (also Tools –> iOS –> Xamarin Mac Agent):

    mac_agent_toolbar

  • If your mac is running, it should appear in the dialog:

    image

  • Select your Mac, click Connect…
  • You’ll be prompted to login, so provide the credentials you use when logging into the Mac
  • Once connected, the toolbar changes colour and provides the option to launch the simulator:

    mac_agent_connected_toolbar

Note this assumes that both the Mac and the PC are on the same network and that all required software is installed and configured for use.

Create and configure IoC Container

Now that we have our environment dependencies out of the way, we can turn our attention to modifying the template code to leverage Caliburn.Micro.

Similar to our Droid project, we need to set up our IoC container. For iOS, we do this by using a CaliburnAppDelegate which looks almost identical to the Application class we created for Android.

using System;
using System.Collections.Generic;
using System.Reflection;

using Caliburn.Micro;

using XF.CaliburnMicro1.ViewModels;

namespace XF.CaliburnMicro1.iOS
{
    class CaliburnAppDelegate : CaliburnApplicationDelegate
    {
        private SimpleContainer _container;

        public CaliburnAppDelegate()
        {
            Initialize();
        }

        protected override void Configure()
        {
            _container = new SimpleContainer();
            _container.Instance(_container);

            _container.Singleton<App>();

            // TODO: Register all platform services here
        }

        protected override void BuildUp(object instance)
        {
            _container.BuildUp(instance);
        }

        protected override IEnumerable<object> GetAllInstances(Type service)
        {
            return _container.GetAllInstances(service);
        }

        protected override object GetInstance(Type service, string key)
        {
            return _container.GetInstance(service, key);
        }

        protected override IEnumerable<Assembly> SelectAssemblies()
        {
            return new[]
            {
                GetType().Assembly,
                typeof(MainViewModel).Assembly
            };
        }
    }
}

We then modify the AppDelegate to leverage our CaliburnAppDelegate, and change the start-up routine to use our App singleton.

using Foundation;
using UIKit;

using Caliburn.Micro;
using Xamarin.Forms.Platform.iOS;

namespace XF.CaliburnMicro1.iOS
{
    [Register("AppDelegate")]
    public partial class AppDelegate : FormsApplicationDelegate
    {
        private readonly CaliburnAppDelegate appDelegate = new CaliburnAppDelegate();

        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();

            LoadApplication(IoC.Get<App>());

            return base.FinishedLaunching(app, options);
        }
    }
}

Build and Run

To run your application, you’ll need to change the start project, select a target and the Device. Here I’ve selected XF.CaliburnMicro.iOS project, specified the iPhoneSimulator target and the iPhone 6s iOS 10.x simulator.

build_and_run

If everything worked, you should now see our Xamarin.Forms app backed by our Caliburn.Micro ViewModel.

 

Next Steps

So there you go! We’ve got both Android and iOS projects running. Our next post we’ll continue down this path and our Xamarin.Forms project to leverage the Universal Windows Platform which will target tablets, phones and PCs.

submit to reddit

Sunday, February 05, 2017

Configure Xamarin.Forms Droid to use Caliburn.Micro

In my last post, we created a Xamarin.Forms project and added necessary components for taking advantage of my favourite MVVM framework, Caliburn.Micro. However, the solution didn't compile because we needed to make some platform specific changes. Today we'll address this.

Add an Application

The main entry point into our Android application is the Application class as defined in the AndroidManifest.xml. By default, the Xamarin.Forms template does not include an Application class, but we’ll need one in order to hold some global state.

To register our custom application, we subclass Caliburn’s CaliburnApplication class and mark it with an [Application] attribute:

using System;
using Android.App;
using Android.Runtime;
using Caliburn.Micro;

namespace XF.CaliburnMicro1.Droid
{
    [Application]
    public class Application : CaliburnApplication
    {
        public Application(IntPtr javaReference, JniHandleOwnership transfer)
              : base(javaReference, transfer)
        {
        }

        public override void OnCreate()
        {
            base.OnCreate();

            Initialize();
        }
    }
}

Create and configure IoC

One of the main features of the CaliburnApplication is to create our platform-specific Inversion of Control (IoC) container and register any platform-specific abstractions.

There are four primary methods that we need to implement in our Application (SelectAssemblies, BuildUp, GetAllInstances, GetInstance). These methods are the backing implementation for Caliburn's IoC:

It's important to note that we register the Xamarin.Forms App as a singleton before we activate it. This is largely because we want the App to have the same lifetime as our Android Application, which avoids a few scenarios where the Android application lifecycle causes the App class to be reinitialized more than once.

Our Application class now looks like this:

using System;
using System.Collections.Generic;
using System.Reflection;

using Android.App;
using Android.Runtime;

using Caliburn.Micro;

using XF.CaliburnMicro1.ViewModels;

namespace XF.CaliburnMicro1.Droid
{
    [Application]
    public class Application : CaliburnApplication
    {
        private SimpleContainer _container;

        public Application(IntPtr javaReference, JniHandleOwnership transfer)
              : base(javaReference, transfer)
        {
        }

        public override void OnCreate()
        {
            base.OnCreate();

            Initialize();
        }

        protected override void Configure()
        {
            _container = new SimpleContainer();

            // make the container available for resolution
            _container.Instance(_container);

            // CRITICAL! make sure our Xamarin.Forms App 
            // can only be initilized once!
            _container.Singleton<App>();

            // TODO: Register any Platform-Specific abstractions here
        }

        protected override IEnumerable<Assembly> SelectAssemblies()
        {
            // Get a list of all assemblies that will be used
            // by the IoC container
            return new[]
            {
                GetType().Assembly,
                typeof (MainViewModel).Assembly
            };
        }

        protected override void BuildUp(object instance)
        {
            _container.BuildUp(instance);
        }

        protected override IEnumerable<object> GetAllInstances(Type service)
        {
            return _container.GetAllInstances(service);
        }

        protected override object GetInstance(Type service, string key)
        {
            return _container.GetInstance(service, key);
        }
    }
}

Modify MainActivity

The last modification we need to make is a slight change to our existing MainActivity. Rather than instantiating our App class, we resolve it from the IoC container.

using Android.App;
using Android.Content.PM;
using Android.OS;

using Xamarin.Forms.Platform.Android;

using Caliburn.Micro;

namespace XF.CaliburnMicro1.Droid
{
    [Activity(
        Label = "XF.CaliburnMicro1", 
        Icon = "@drawable/icon", 
        MainLauncher = true, 
        ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : FormsApplicationActivity
    {
        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            global::Xamarin.Forms.Forms.Init(this, bundle);

            // Resolve the App class to use the singleton we created
            LoadApplication(IoC.Get<App>());
        }
    }
}

Compile and Run

At this point, the android project should be good to go. Set the project as the default target, and run (F5). The project should automatically be configured to deploy the app to the emulator.

Next Steps

So we have our Android application running our Caliburn.Micro flavoured Xamarin.Forms app. The next step is to configure our iOS project with similar customizations.

submit to reddit

Saturday, February 04, 2017

Getting Started with Xamarin.Forms and Caliburn.Micro

Let me say this: Xamarin.Forms is awesome.

Xamarin.Forms is a great way to build native iOS and Android applications using C# and XAML. But if you’re just starting out, you’ll find that the most of the documentation out there, including Charles Petzold’s fantastic book, are lacking great examples on how to do MVVM, which is XAMLs strongest feature.

Last year when Caliburn.Micro finally released version 3.0 with support with Xamarin.Forms I was disappointed that there wasn’t much documentation to support it as well.

So with that said, lets build a Xamarin.Forms app using Caliburn.Micro.

Create Project

Good news here is that Visual Studio will do most of the work for us. To create a blank Xamarin.Forms project, choose File –> New Project. Then select the template Visual C# –> Cross Platform –> Blank App (Xamarin.Forms Portable).

image

If you specify the project name XF.CaliburnMicro, Visual Studio will create several projects:

  • XF.CaliburnMicro (Portable) – This is the PCL library that will hold our Views and ViewModels and core services and interfaces
  • XF.CaliburnMicro.Droid – Our Android application
  • XF.CaliburnMicro.iOS – Our iPhone / iPad application
  • XF.CaliburnMicro.UWP (Universal Windows) – Our Windows 10 desktop/tablet/mobile application.
  • XF.CaliburnMicro.Windows (Windows 8.1) – A Windows 8.1 app, for those of us that didn’t take the free upgrade.
  • XF.CaliburnMicro.WinPhone (Windows Phone 8.1) – A Windows Phone 8.1 Silverlight/XAML application.

Note: You’ll likely be prompted to specify a Windows 10 version. For now, don’t worry about this, just click OK.

Also note that I’m only going to focus on Android, iOS and UWP, so feel free to delete XF.CaliburnMicro.Windows and XF.Caliburn.WinPhone.

Compile and Run

Now would be a good time to make sure everything compiles and your machine is good to go. Xamarin.Forms requires a few dependencies to get up and running, so before we start messing things up this would be a good time for a sanity test.

For simplicity sake, let’s focus only on getting our Android emulator running. If you’re able to see the default Forms page, we’re good.

Moving along…

Reference NuGet Packages

It’s time to add Caliburn.Micro to the project. There are three key NuGet packages we’re interested in:

  • Caliburn.Micro
  • Caliburn.Micro.Core
  • Caliburn.Micro.XamarinForms

What seems a bit counter intuitive is that the Caliburn.Micro package is platform specific, so it cannot be referenced by the PCL library.

The best way to add these to your project is to right-click the solution and choose Manage NuGet Packages for Solution. Search for “Caliburn.Micro.XamarinForms” and add it to all projects. It’ll bring in the Caliburn.Micro.Core dependency.

Search for “Caliburn.Micro.Core” and add it to the PCL “XF.CaliburnMicro (Portable)” project.

Setup basic View and ViewModel

Caliburn uses conventions to automatically map Views to ViewModels and vice-versa, so you’ll need to create two folders Views and ViewModels in the XF.XamarinForms (Portable) project.

The ViewModels\MainViewModel is pretty straight forward:

using Caliburn.Micro;
using System.Runtime.CompilerServices;

namespace XF.CaliburnMicro1.ViewModels
{
    public class MainViewModel : Screen
    {
        private string _mainText;

        public MainViewModel()
        {
           MainText = "Hello World!";
        }         

        public string MainText
        {
            get { return _mainText; }
            set
            {
                SetField(ref _mainText, value);
            }
        }

        protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
        {
            if (!object.Equals(field, value))
            {
                field = value;
                NotifyOfPropertyChange(propertyName);
                return true;
            }
            return false;
        }
    }
}

And the Views\MainView.xaml is an empty screen created using Project –> Add New Item –> Forms Xaml Page:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XF.CaliburnMicro1.Views.MainView">
  <Label Text="{Binding MainText}" VerticalOptions="Center" HorizontalOptions="Center" />
</ContentPage>

Introduce Caliburn.Micro

If you’ve worked with Caliburn.Micro in the past, you would normally configure a Bootstrapper as the primary entry point for the application. In Xamarin.Forms, the closest equivalent is the App class that is created in the PCL project. We’ll use it to setup our Inversion of Control (IoC) container and register ViewModels, services and abstractions.

Caliburn.Micro ships with a simple IoC container, aptly named SimpleContainer, which offers simple functionality to register and resolve dependencies. It is possible to replace this container with a different one if you prefer, but in my experience the SimpleContainer does exactly what we need.

using Caliburn.Micro;
using Caliburn.Micro.Xamarin.Forms;
using Xamarin.Forms;
using XF.CaliburnMicro1.ViewModels;

namespace XF.CaliburnMicro1
{
    public class App : FormsApplication
    {
        private readonly SimpleContainer container;

        public App(SimpleContainer container)
        {
            this.container = container;

            // TODO: Register additional viewmodels and services
            container
                .PerRequest<MainViewModel>();

            Initialize();

            DisplayRootViewFor<MainViewModel>();
        }

        protected override void PrepareViewFirst(NavigationPage navigationPage)
        {
            container.Instance<INavigationService>(new NavigationPageAdapter(navigationPage));
        }

        protected override void OnStart()
        {
            // Handle when your app starts
        }

        protected override void OnSleep()
        {
            // Handle when your app sleeps
        }

        protected override void OnResume()
        {
            // Handle when your app resumes
        }
    }
}

Next Steps

At this point, the solution doesn’t compile, and that’s because we’ve changed the signature of the App class and we’ll have to adjust our iOS, Android and UWP projects to suit. Admittedly, it’s an awkward place to stop a blog post, but there’s a lot more to cover. Over the next few blog posts, we’ll setup the platform specific projects with their Caliburn parts and we’ll update the links below.

submit to reddit

Saturday, January 21, 2017

This time on a mac

There’s been less activity than normal around here. Looking back, I missed last year completely. That’s more than a blip, that’s a complete year. Not sure what happened, I think it was a perfect storm...

Those that know me know that I started practicing Karate in 2015 and my facebook feed has been filled with belt exams and karate videos. I’m highly motivated to obtain my first degree in under three years, so three times a week in evening classes and weekends makes it difficult to find a few extra cycles to experiment and blog. On the plus side, I’m probably the most fit I’ve ever been, I've dropped nearly twenty pounds, toned up and dangerous.

The other major shift for me was professionally work wasn’t selling the projects that suited my skills. I kinda had a crisis of faith of sorts, where I felt I had to choose a new religion. I switched into managing more people, bought a mac and learned Swift, and explored leveraging my .net and WPF strengths in Xamarin. Work would eventually pick up later in the year, but in true agency fashion it involved dusting off my Unity3D skills, learning how to rock a HoloLens and learn Python and Ruby while I was at it. There’s loads to blog about it.

I want to touch on the macbook reference above. I feel like I’ve made a career out of bashing apple. (I ended many arguments in the 90s with apple fanboys with my favourite argument killer: “...but it’s a mac…”) Buying a mac for me was huge slice of humble pie. I still use a PC at work, but my mac is now my playground and go to device when I want to do something that isn’t “work”. I’m writing this on a mac, trying out blogo today.

One last thing, to accompany my macbook I switched to an iPhone and parted ways with my Windows Phone. I’m no stranger to iOS as I’ve had an iPad and iPhone in the past, but I was on Windows Phone for years with three different devices. I loved the platform, and still do, but I got tired of waiting for apps that the rest of the planet had. Apply Pay and wallet are awesome. I may still buy a lumina to play with UWP with Xamarin.

So what should you expect this year around here? Well, hopefully I can make good on promises to blog more, but expect to see a combination of development and agile delivery practices. You’ll likely see some Xamarin, some UI automation, maybe a HoloLens. From an agile delivery perspective, expect some tips and tricks to bring to your team as well as a focus on DevOps and Test Automation.

Friday, December 04, 2015

List of C# Compiler Warnings

Generally, you want to fix compiler warnings as they come up, but you may need to suppress some of them for legitimate reasons. A list of warnings and errors can be found here, but there’s no high-level list.

Here is a summarized list of warnings.

 

Warning Description More Info

Level 1 Warnings (Severe)

CS0420 ‘identifier’: a reference to a volatile field will not be treated as volatile https://msdn.microsoft.com/en-us/library/4bw5ewxy.aspx
CS0465 Introducing a ‘Finalize’ method can interfere with destructor invocation. https://msdn.microsoft.com/en-us/library/02wtfwbt.aspx
CS1058

A previous catch clause already catches all exceptions

https://msdn.microsoft.com/en-us/library/ms228623.aspx
CS1060 Use of possibly unassigned field 'name'. Struct instance variables are initially unassigned if struct is unassigned. https://msdn.microsoft.com/en-us/library/bb384216.aspx
CS1598 XML parser could not be loaded for the following reason: 'reason'. The XML documentation file 'file' will not be generated. https://msdn.microsoft.com/en-us/library/szfbfb7e.aspx
CS1607 A warning was generated from the assembly-creation phase of the compilation. https://msdn.microsoft.com/en-us/library/4a0640cd.aspx
CS1616 Option 'option' overrides attribute 'attribute' given in a source file or added module https://msdn.microsoft.com/en-us/library/0b104xt8.aspx
CS1658

'warning text'. See also error 'error code'

The compiler emits this warning when it overrides an error with a warning.

https://msdn.microsoft.com/en-US/library/ft0k10fs.aspx
CS1683 Reference to type 'Type Name' claims it is defined in this assembly, but it is not defined in source or any added modules https://msdn.microsoft.com/en-US/library/6b351z64.aspx
CS1685 The predefined type 'System.type name' is defined in multiple assemblies in the global alias; using definition from 'File Name' https://msdn.microsoft.com/en-us/library/8xys0hxk.aspx
CS1690 Accessing a member on 'member' may cause a runtime exception because it is a field of a marshal-by-reference class https://msdn.microsoft.com/en-us/library/x524dkh4.aspx
CS1691 'number' is not a valid warning number https://msdn.microsoft.com/en-us/library/7z6tx3wa.aspx
CS1699 Use command line option "compiler_option" or appropriate project settings instead of "attribute_name" https://msdn.microsoft.com/en-us/library/xh3fc3x0.aspx
CS1762 A reference was created to embedded interop assembly '<assembly1>' because of an indirect reference to that assembly from assembly '<assembly2>'. Consider changing the 'Embed Interop Types' property on either assembly. https://msdn.microsoft.com/en-us/library/ff183282.aspx
CS1956 Member 'name' implements interface member 'name' in type 'type'. There are multiple matches for the interface member at run-time. It is implementation dependent which method will be called. https://msdn.microsoft.com/en-us/library/bb882526.aspx
CS3003 Type of 'variable' is not CLS-compliant https://msdn.microsoft.com/en-us/library/ax5w23a6.aspx
CS3007 Overloaded method 'method' differing only by unnamed array types is not CLS-compliant https://msdn.microsoft.com/en-us/library/3w1d36e0.aspx
CS3009 'type': base type 'type' is not CLS-compliant https://msdn.microsoft.com/en-us/library/tdd79w48.aspx
CS4014 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. https://msdn.microsoft.com/en-us/library/hh873131.aspx

Level 2 Warnings

CS0108 'member1' hides inherited member 'member2'. Use the new keyword if hiding was intended. https://msdn.microsoft.com/en-US/library/3s8070fc.aspx
CS0467 Inherited members from different interfaces that have the same signature cause an ambiguity error. https://msdn.microsoft.com/en-us/library/ms228509.aspx
CS0618 'member' is obsolete: 'text' https://msdn.microsoft.com/en-us/library/x5ye6x1e.aspx
CS1701 Assuming assembly reference "Assembly Name #1" matches "Assembly Name #2", you may need to supply runtime policy https://msdn.microsoft.com/en-us/library/2h4x8b08.aspx

Level 3 Warnings (Less Severe)

CS0675 Bitwise-or operator used on a sign-extended operand; consider casting to a smaller unsigned type first https://msdn.microsoft.com/en-us/library/wdc6717a.aspx
CS1700 Assembly reference Assembly Name is invalid and cannot be resolved https://msdn.microsoft.com/en-us/library/ac36c25f.aspx

Level 4 Warnings (Informational)

CS0429 Unreachable expression code detected https://msdn.microsoft.com/en-us/library/2ch1a3w5.aspx
CS1591 Missing XML comment for publicly visible type or member 'Type_or_Member' https://msdn.microsoft.com/en-us/library/zk18c1w9.aspx
CS1610 Unable to delete temporary file 'file' used for default Win32 resource -- resource https://msdn.microsoft.com/en-US/library/z4aytccf.aspx

Tuesday, November 17, 2015

Configure SourceTree to Rebase by default

Here’s a quick tip for Git for Windows users that are using Atlassian’s SourceTree as their visual interface.

If you’re using git flow as a branching strategy, a good habit to form is to use git pull --rebase when you update your local branch. This technique updates your local branch and then “replays” your changes on top, effectively putting your changes at the head of the branch.

The SourceTree Pull dialog supports this feature with a checkbox, but it’s not enabled by default.

image

To make this checkbox selected by default:

  1. Tools –> Options
  2. Git
  3. Check the “use rebase instead of merge by default for tracked branches” option

image

Friday, October 09, 2015

Init Methods are Crap

This title of this post pretty much sums up how I feel about Init methods. Let’s take a look at an example why.

I’ve been on a soap-box yelling at anyone who’ll listen that “work in the constructor” is perhaps the worst thing you can do as a developer. You’ve drank the cool-aid and you have some fairly important initialization logic that needs to be called, so you move the logic out of the constructor and into a separate method. Your code now looks like this:

public class MenuProvider : IMenuProvider
{
    private Array<MenuItem> _menuItems;
    
    public void Initialize()
    {
        // do work to populate menu items, serialize from file, etc...
        _menuItems = // some value;
    }

    public IEnumerable<MenuItem> MenuItems
    {
        get
        {
            return _menuItems;
        }
    }

    public IEnumerable<MenuItem> InActiveMenuItems
    {
        get
        {
	    return _menuItems.Where(i => i.Active == false);
        }
    }

}

It's a contrived example, but in the above code MenuItems will be null and InActiveMenuItems throws an error unless you call Initialize() first. Certainly, there must be a better design than this? (If you answered put the work in the constructor, please come see me after class, I.. uh,.. have something for you.)

Putting the onus on the developer to call methods in a certain order is a very backward approach. You, the class designer, should make it difficult for people to do things the wrong way. We can take on some of that onerous effort inside the class, something like this should work nicely:

public class MenuProvider : IMenuProvider
{
   private bool _isInitialized;
   private object _lock = new object();
   private Array<MenuItem> _menuItems;

   public IEnumerable<MenuItem> GetMenuItems()
   {
        EnsureInitialized();
    
        return _menuItems;
   }

   public IEnumerable<MenuItem> GetInactiveMenuItems()
   {
        EnsureInitialized();

        return _menuItems.Where(i => i.Active == false);
   }

   private void EnsureInitialized()
   {
        if (!_initialized)
        {
            Initialize();
        }
   }
   
   private void Initialize()
   {
        lock(_lock)
        {
            if (!_initialized)
            {
                // do initialization work
                _initialized = true;
            }
        }
   }
}

In this design, I've made a few minor adjustments. For stylistic purposes, I converted the properties into methods because properties look awkward on interfaces. Properties also aren't a good choice if there's work involved in calculating them, so methods feel better here.

Outside of the cosmetic changes, one of the most notable changes is that my Initialize method is now private. I've also added some plumbing logic to ensure that the Initialize method only does work the first time it's called, and since this is good defensive programming, this might have already existed and shouldn't be a surprise.

What should stand out to you, is that each method is responsible for ensuring that the initialization logic is called on the user's behalf using the EnsureInitialized method before doing any other work. Some developers may grumble that this seems like a simple thing to forget when adding new methods and thus has potential for silly bugs. That’s fair, but because the API for the class has been simplified the tests to catch this should be much easier to write and find.

Alternatively, we can take this approach further by splitting the concerns into smaller parts. One part could read the file contents and the other part could be responsible for serving the data to consumers. This is effectively just moving the Initialize method to another class. Something like this:

public class MenuProvider : IMenuProvider
{
    private MenuReader _reader;

    public MenuProvider(MenuReader reader)
    {
        _reader = reader;
    }

    public IEnumerable<MenuItem> GetMenuItems()
    {
        return _reader.GetAll();
    }

    public IEnumerable<MenuItem> GetInactiveMenuItems()
    {
        return _reader.GetAll().Where(i => i.Active == false);
    }
}

public abstract class MenuReader
{
    public abstract IEnumerable<MenuItem> GetAll();
}

public class StaticFileMenuReader : MenuReader
{
    private Array<MenuItem> _items;

    public IEnumerable<MenuItem> GetAll()
    {
        if (_items != null)
        {
            // do serialization work
        }
        return _items;
    }
}

This small change greatly simplifies the MenuProvider and the extra step to move the serialization logic into another class may seem familiar to others as a Repository pattern. The repository pattern in this example also affords us additional test flexibility as the file-system can be abstracted away with a test-double.

To sum up, it seems a bit odd to design a class that requires methods to be called in a specific order. Initialization in general is something that the class designer should be well equipped to handle, so don’t push this responsibility to others – a few small changes to your class makes your code more usable, responsive and easier to test.

submit to reddit