Share via


SOLID - Open/Closed principle - Part 2

Decorator design pattern is used to add functionality to an object dynamically without the need of modifying the structure of the object. Decorator pattern is used to attach functionality or behavior to an object dynamically or statically without the need to altering the object's structure.

Decorator classes

using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.VisualStudio.TestTools.UITesting;
  
namespace TestFramework.UI.Web.Attributes
{
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
    public sealed  class SearchByAttribute : Attribute
    {
        
        public SearchByAttribute(string name, string value)
        {
            Name = name;
            Value = value;
            ConditionOperator = PropertyExpressionOperator.Contains;
        }
  
        public SearchByAttribute()
        {
            ConditionOperator = PropertyExpressionOperator.Contains;
        }
  
        [Required(AllowEmptyStrings = false, ErrorMessage = "Name is a required property when using a search condition")]
        public string  Name { get; set; }
  
        [Required(AllowEmptyStrings = false, ErrorMessage = "Value is a required property when using a search condition")]
        public string  Value { get; set; }
  
        public PropertyExpressionOperator ConditionOperator {  get; set; }
    }
}

Using the Decorator class in the client class.

using Microsoft.VisualStudio.TestTools.UITesting;
using Microsoft.VisualStudio.TestTools.UITesting.HtmlControls;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TestFramework.UI.Web.Attributes;
using TestFramework.UI.Web.Model;
   
namespace TestFramework.UI.Web.UITests.Pages
{
    public class  CustomPage : Page
    {
        [Control(Id = "PlaceHolder", IdConditionOperator = PropertyExpressionOperator.Contains, Class = "navigation-menu")]
        public HtmlDiv NavigationMenu { get; set; }
         
        [Parent("NavigationMenu")]
        [SearchBy(Name = "Title", Value = "")]
        [Control()]
        public HtmlHyperlink MenuLink { get; set; }
   
        [Parent("NavigationMenu")]
        [SearchBy(Name = "Title", Value = "", ConditionOperator = PropertyExpressionOperator.Contains)]
        [SearchBy(Name = "href", Value = "", ConditionOperator = PropertyExpressionOperator.Contains)]
        [Control()]
        public HtmlHyperlink MenuLink { get; set; }
   
        internal CustomPage AssertLinkExists()
        {
            //Mouse.Hover(GremiaMenuLink);
            Assert.IsTrue(MenuLink.Exists, "Failed to find navigational menu link on the page.");
            return this;
        }
   
        internal CustomPage ClickOnKamerMenu()
        {
            //Mouse.Hover(GremiaMenuLink);
            MenuLink.Click();
            Playback.Wait(5000);
            return this;
        }
    }
}

Accessing the control values using reflection.

System.Reflection.PropertyInfo property
  
var searchAttributes = property.GetCustomAttributes<SearchByAttribute>(false);
foreach (var searchAttribute in searchAttributes)
{
console.writeline((searchAttribute.Name + searchAttribute.Value + searchAttribute.ConditionOperator));
}

See Also