I recently worked with a client who wanted a reasonably large subsystem added to Optimizely that would add automated management to their content. While cutting the code for this, I found myself writing similar code across multiple classes. I had to write it that way: 1) The client was currently on CMS11 and didn’t have access to newer language features; 2) The hierarchy of the classes prevented me from inserting a common ancestor. Thankfully, .NET has expanded the functionality of interfaces, so we can take advantage of those within Optimizely.
Original Interface
using OptimizelySDK; using OptimizelySDK.Entity; namespace Teapot.Interfaces.Services { public interface IExperimentation { public OptimizelyUserContext CreateUserContext(UserAttributes userAttributes = null, EventTags eventTags = null); public string GetUserId(); public void TrackEvent(string eventKey); } }
Interface with .NET Default Implementation
using OptimizelySDK; using OptimizelySDK.Entity; using Perficient.Infrastructure.Interfaces.Services; using System; namespace Teapot.Interfaces.Services { public interface IExperimentation { public OptimizelyUserContext CreateUserContext(UserAttributes userAttributes = null, EventTags eventTags = null); public string GetUserId(ICookieService cookieService) { var userId = cookieService.Get("opti-experiment-testA"); if (userId == null) { userId = Guid.NewGuid().ToString(); cookieService.Set("opti-experiment-testA", userId); } return userId; } public void TrackEvent(string eventKey); } }
Using Default Implementation
return _featureExpermentation.CreateUserContext((this as IExperimentation).GetUserId(_cookieService));
If the default implementation is called often, it makes for some ugly code. This can be remedied with a bit of syntactic sugar in the class by leveraging the interface:
public string GetUserId() { return (this as IExperimentation).GetUserId(_cookieService); }