World is now on Opti ID! Learn more

KhurramKhang
Jan 1, 2020
  31
(0 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

Please login to comment.
Latest blogs
Make Global Assets Site- and Language-Aware at Indexing Time

I had a support case the other day with a question around search on global assets on a multisite. This is the result of that investigation. This co...

dada | Jun 26, 2025

The remote server returned an error: (400) Bad Request – when configuring Azure Storage for an older Optimizely CMS site

How to fix a strange issue that occurred when I moved editor-uploaded files for some old Optimizely CMS 11 solutions to Azure Storage.

Tomas Hensrud Gulla | Jun 26, 2025 |

Enable Opal AI for your Optimizely products

Learn how to enable Opal AI, and meet your infinite workforce.

Tomas Hensrud Gulla | Jun 25, 2025 |

Deploying to Optimizely Frontend Hosting: A Practical Guide

Optimizely Frontend Hosting is a cloud-based solution for deploying headless frontend applications - currently supporting only Next.js projects. It...

Szymon Uryga | Jun 25, 2025

World on Opti ID

We're excited to announce that world.optimizely.com is now integrated with Opti ID! What does this mean for you? New Users:  You can now log in wit...

Patrick Lam | Jun 22, 2025

Avoid Scandinavian Letters in File Names in Optimizely CMS

Discover how Scandinavian letters in file names can break media in Optimizely CMS—and learn a simple code fix to automatically sanitize uploads for...

Henning Sjørbotten | Jun 19, 2025 |