Try our conversational search powered by Generative AI!

K Khan
Jan 1, 2020
  2116
(6 votes)

Use of strategy pattern to change page behaviour

There are scenarios where we have to change our page behavior or algorithms at run time based on Epi Configurations. Generally, a developer approaches with if-else statements to deal with this by compromising design principals. We can get help from the Strategy Pattern to adhere to SOLID principals. Below is an Illustrations for a fake scenario to demonstrate implementation where editors will set a recipe filter to change the results of the page or selecting the right dataset for the page.

EPiServer Page and configurations

public enum LayoutType
    {
        Diabitic,
        HighProtein,
        LowFat,
        HighFibre
    }

    /// <summary>
    /// Create an interface for configuration
    /// </summary>
    public interface IStrategyConfiguration
    {
        LayoutType LayoutType { get; set; }
    }

    /// <summary>
    /// Implement Configuration in your page or block
    /// </summary>
    public class RecipeLandingPage : PageData, IStrategyConfiguration
    {
        [Display(GroupName = SystemTabNames.Settings, Name = "Layout", Description = "Select page layout")]
        public LayoutType LayoutType { get; set; }
    }

PageController/factory where we need to change the algorithm at runtime.

/// <summary>
    /// Page Controller
    /// </summary>
    public class RecipeLandingPageController : PageController<RecipeLandingPage>
    {
        /// <summary>
        /// IRecipeFilterSelector is a resolver that will select right IRecipeFilter for us
        /// </summary>
        private readonly IRecipeFilterSelector _recipeFilterSelector;

        public RecipeLandingPageController(IRecipeFilterSelector recipeFilterSelector)
        {
            _recipeFilterSelector = recipeFilterSelector;
        }

        public async Task<ActionResult> Index(RecipeLandingPage currentPage)
        {
            //Select a filter based on epi configurations
            var filter = _recipeFilterSelector.GetRecipeFilter(currentPage.LayoutType);
            var pageModel = YourPageModel(currentPage);
            pageModel.Advertisements = filter.GetAdvertisements();
            pageModel.Suggestions = filter.GetSuggestions();
            return View(model);
        }
    }

We need IRecipeFilterSelector that could return us correct IRecipeFilter.

    /// <summary>
    /// will resolve correct IRecipeFilter
    /// </summary>
    public interface IRecipeFilterSelector
    {
        IRecipeFilter GetRecipeFilter(LayoutType layoutType);
    }

    /// <summary>
    /// Select IRecipeFilter based on EPiCongigurations
    /// </summary>
    public class RecipeFilterSelector : IRecipeFilterSelector
    {
        private readonly IEnumerable<IRecipeFilter> _recipeFilters;
        // structuremap will push all implementations of IRecipeFilter
        public RecipeFilterSelector(IEnumerable<IRecipeFilter> recipeFilters)
        {
            _recipeFilters = recipeFilters;
        }

        public IRecipeFilter GetRecipeFilter(LayoutType layoutType)
        {
            return _recipeFilters.FirstOrDefault(x => x.LayoutType == layoutType);
        }
    }

We need separate implementations for IRecipeFilter

/// <summary>
    /// Main service
    /// </summary>
    public interface IRecipeFilter
    {
        LayoutType LayoutType { get; }
        IList<Advertisement> GetAdvertisements();
        IList<Suggestion> GetSuggestions();
    }

    /// <summary>
    /// Separation of concerns achieved, implementation for specific scenarios
    /// </summary>
    public class DiabiticRecipeFilter : IRecipeFilter
    {
        public LayoutType LayoutType => LayoutType.Diabitic;

        public IList<Advertisement> GetAdvertisements()
        {
            throw new NotImplementedException();
        }

        public IList<Suggestion> GetSuggestions()
        {
            throw new NotImplementedException();
        }
    }
    /// <summary>
    /// Separation of concerns achieved, implementation for specific scenarios
    /// </summary>

    public class HighProteinRecipeFilter : IRecipeFilter
    {
        public LayoutType LayoutType => LayoutType.HighProtein;

        public IList<Advertisement> GetAdvertisements()
        {
            throw new NotImplementedException();
        }

        public IList<Suggestion> GetSuggestions()
        {
            throw new NotImplementedException();
        }
    }

Join these pieces in structuremap to work together

//Collect all implementations of IRecipeFilter
    c.Scan(s =>
    {
    s.AssemblyContainingType(typeof(IRecipeFilter));
    s.AddAllTypesOf(typeof(IRecipeFilter));
    });
    //pass this to selector
    c.For<IRecipeFilterSelector>().Use<RecipeFilterSelector>().Singleton();

Be SOLID in the new year! Happy new year!

Jan 01, 2020

Comments

Drew Douglas
Drew Douglas Jan 2, 2020 05:11 PM

Great idea! Strategy is one of the design patterns that should be widely used. I find that over-application of design patterns is a code-smell for small teams, but I love strategy for simplifying behavior choices.

K Khan
K Khan Jan 2, 2020 05:58 PM

Glad you liked! Thanks!

K Khan
K Khan Jan 2, 2020 05:59 PM

Glad you liked! Thanks!

Anders Jacobsen
Anders Jacobsen Jan 3, 2020 08:10 AM

We try to use similar aproach when deailing with complex logic. It will make your logic much more solid as it does not depend on complex "if structures" and it gives the opotonity to target your unit test to each strategy implemented as they are seperated from each other.

Please login to comment.
Latest blogs
Anonymous Tracking Across Devices with Optimizely ODP

An article by Lead Integration Developer, Daniel Copping In this article, I’ll describe how you can use the Optimizely Data Platform (ODP) to...

Daniel Copping | Apr 30, 2024 | Syndicated blog

Optimizely Forms - How to add extra data automatically into submission

Some words about Optimizely Forms Optimizely Forms is a built-in add-on by Optimizely development team that enables to create forms dynamically via...

Binh Nguyen Thi | Apr 29, 2024

Azure AI Language – Extractive Summarisation in Optimizely CMS

In this article, I demonstrate how extractive summarisation, provided by the Azure AI Language platform, can be leveraged to produce a set of summa...

Anil Patel | Apr 26, 2024 | Syndicated blog

Optimizely Unit Testing Using CmsContentScaffolding Package

Introduction Unit tests shouldn't be created just for business logic, but also for the content and rules defined for content creation (available...

MilosR | Apr 26, 2024

Solving the mystery of high memory usage

Sometimes, my work is easy, the problem could be resolved with one look (when I’m lucky enough to look at where it needs to be looked, just like th...

Quan Mai | Apr 22, 2024 | Syndicated blog

Search & Navigation reporting improvements

From version 16.1.0 there are some updates on the statistics pages: Add pagination to search phrase list Allows choosing a custom date range to get...

Phong | Apr 22, 2024