Home » Commands » Restrict Object Instantiation via the Singleton Pattern in Sitecore

Restrict Object Instantiation via the Singleton Pattern in Sitecore

Sitecore Technology MVP 2016
Sitecore MVP 2015
Sitecore MVP 2014

Enter your email address to follow this blog and receive notifications of new posts by email.

Tweets

This post is a continuation of a series of posts I’m putting together around using design patterns in Sitecore, and will show a “proof of concept” around using the Singleton pattern — a creational pattern which restricts the creation of a class to only one instance, and also provides a global reference to it.

In this “proof of concept”, I am using a Singleton which contains a method that will “un-parent” an Item — all of the Item’s children will become its siblings in the Sitecore content tree — and will utilize this in a command which will be wired-up to the Sitecore Ribbon. I honestly don’t see much utility in having such functionality in Sitecore — you just might 🙂 — but the functionality itself is not the purpose of this post.

I first started out by creating the following class which serves as the Singleton:

using System.Linq;

using Sitecore.Data.Items;
using Sitecore.Diagnostics;

namespace Sitecore.Sandbox.Items
{
    public class ItemOperations
    {
        private static volatile ItemOperations current;
        
        private static object locker = new object();

        private ItemOperations() 
        { 
        }

        public static ItemOperations Current
        {
            get 
            {
                if (current == null) 
                {
                    lock (locker) 
                    {
                        if (current == null)
                        {
                            current = new ItemOperations();
                        }
                    }
                }

                return current;
            }
        }

        public void Unparent(Item item)
        {
            Assert.ArgumentNotNull(item, "item");
            if(!item.Children.Any())
            {
                return;
            }

            foreach(Item child in item.Children)
            {
                child.MoveTo(item.Parent);
            }
        }
    }
}

Before going into the details of why the class above is a Singleton, I want to point out that it contains one method — the Unparent() method — which iterates over all child Items of the Item passed to it, and moves them under the passed Item’s parent — this will move these child Items to the same level as their former parent.

You might be asking “what makes this a Singleton”? The first clue is the private constructor — this class cannot be instantiated by other classes. It can only be instantiated from within itself.

This instantiation is being done in the logic of its Current property. If the static private variable “current” is null, we “lock” an arbitrary object — this is done to make this multithreading “friendly” so that we can avoid collisions in the case when two different threads invoke the Current property at about the exact same time — and then create an instance of the ItemOperations class. This instance is then saved in the static variable “current”.

When the next call to the Current property is made on this class’ type, the “current” variable is already set, so the instance stored in it is returned to the caller.

The reason why the variable “current” and its associated property “Current” are static is to ensure these aren’t bound to an instance of the class but instead are bound to the class’ type, and the “Current” property can be accessed directly on the class’ name.

I then created the following subclass of Sitecore.Shell.Framework.Commands.Command which is needed for integration into the Sitecore Ribbon:

using System.Collections.Generic;
using System.Linq;

using Sitecore.Data.Items;
using Sitecore.Shell.Framework.Commands;

using Sitecore.Sandbox.Items;

namespace Sitecore.Sandbox.Shell.Framework.Commands
{
    public class Unparent : Command
    {
        public Unparent()
        {
        }

        public override void Execute(CommandContext context)
        {
            Item item = GetItem(context);
            ItemOperations.Current.Unparent(item);
        }

        public override CommandState QueryState(CommandContext context)
        {
            Item item = GetItem(context);
            if (item == null || !item.Children.Any())
            {
                return CommandState.Hidden;
            }

            return CommandState.Enabled;
        }

        protected virtual Item GetItem(CommandContext context)
        {
            if(!context.Items.Any())
            {
                return null;
            }

            return context.Items.First();
        }
    }
}

The QueryState() method checks to see whether the selected Item in the Sitecore content tree has any children and returns the Hidden value on the Sitecore.Shell.Framework.Commands.CommandState enum — this lets the Sitecore Client code know that the button associated with this command should be hidden. If the Item does have children, the Enabled value on the Sitecore.Shell.Framework.Commands.CommandState enum is returned.

The Execute() method just passes the selected Item in the Sitecore content tree to the Unparent() method on the ItemOperations Singleton above — the Unparent() method is where the child Items are moved up a level.

I then had to register the command above with Sitecore via the following patch configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <commands>
      <command name="item:Unparent" type="Sitecore.Sandbox.Shell.Framework.Commands.Unparent, Sitecore.Sandbox" />
    </commands>
  </sitecore>
</configuration>

Now that the above command is registered in Sitecore, we must bind it to Sitecore ribbon. I did that in the core database:

unparent-ribbon

I won’t go into details of the above as I’ve already covered how to do so in many of my previous posts — do have a look!

Let’s take this for a spin!

Let’s choose an Item with some child Items:

unparent-1

After clicking the “Unparent” button in the Ribbon, I saw the following:

unparent-2

As you can see, this Item was “un-parented”.

Ok, now that we had some fun with this, let’s have a serious discussion about the Singleton pattern — yeah, I know serious discussions aren’t always pleasant but what I have to say is pretty important regarding this pattern.

Although the Singleton pattern does make it easy to implement things quite fast, and does allow for less memory usage due to having less object instances floating around in memory, it unfortunately promotes tight coupling in your classes.

If you have to change something on the Singleton class itself — perhaps a method signature on it — you will have to also update every single class that references it. This can be quite costly from a development effort and painful — especially if the Singleton is being referenced in a gazillion places ( is gazillion a word? 😉 ).

So, please do think twice before using this pattern — sure, it might be alright to use a Singleton in a pinch but do be sure it won’t lead to a maintenance nightmare for future development in your Sitecore solutions.

Advertisements

3 Comments

  1. jonskeet says:

    There are simpler ways of implementing the singleton pattern… http://csharpindepth.com/Articles/General/Singleton.aspx

  2. ben says:

    what happens if I have a private member variable in that singleton? all calls will be sharing and changing the same state. you have inadvertently created a global variable. http://misko.hevery.com/2008/08/25/root-cause-of-singletons/

    • Thanks for the comment Ben. If you read the closing remarks of this post — I doubt anyone has given that I’ve received multiple responses from folks like you on its inadequacies — you will see that I don’t espouse the usage of this pattern at all.

Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: