Toggling features of an application
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
10
down vote
favorite
I've written a library that uses generics and funcs to implement a feature toggle system without the need to use if statements.
This was mostly to hone my skills, as I'm still new to this profession. I'd really appreciate any comments on how to improve the code.
It can be found in this following git repo
An extract of the main class is below
Feature Toggle Class
namespace FeatureToggle.Classes
using System;
using Enums;
using Interfaces;
public class FeatureToggle <T> : IFeatureToggle <T>
private ToggleStatus Status(bool active)
return active ?
ToggleStatus.Active :
ToggleStatus.Inactive;
public ToggleStatus GetToggleState(IConfigParser parser, string toggleKey)
return Status(parser.GetToggleStatus(toggleKey));
public void ExecuteMethodIfToggleOn(Action methodToRun, IConfigParser configParser, string keyName)
var response = GetToggleState(configParser, keyName);
if (response == ToggleStatus.Active)
methodToRun();
public void ExecuteMethodIfToggleOn(Action methodToRun, string keyName)
IConfigParser configParser = new ConfigParser();
ExecuteMethodIfToggleOn(methodToRun, configParser, keyName);
public T ExecuteMethodIfToggleOn(Func<T> methodToRun, string keyName)
IConfigParser configParser = new ConfigParser();
return ExecuteMethodIfToggleOn(methodToRun, configParser, keyName);
public T ExecuteMethodIfToggleOn(Func<T> methodToRun, IConfigParser configParser, string keyName)
var response = GetToggleState(configParser, keyName);
if (response == ToggleStatus.Active)
return methodToRun();
return default(T);
Feature Toggle Interface
namespace FeatureToggle.Interfaces
using System;
using Enums;
public interface IFeatureToggle <T>
ToggleStatus GetToggleState(IConfigParser parser, string toggleKey);
void ExecuteMethodIfToggleOn(Action methodToRun, string keyName);
T ExecuteMethodIfToggleOn(Func<T> methodToRun, string keyName);
Config Parser Class
namespace FeatureToggle.Classes
using System;
using System.Collections.Specialized;
using System.Configuration;
using Interfaces;
public class ConfigParser : IConfigParser
private readonly NameValueCollection _toggles;
public ConfigParser()
if (ToggleConfigTagExists())
_toggles = ConfigurationManager.GetSection("Toggles") as NameValueCollection;
public bool ToggleConfigTagExists()
var toggleSection = ConfigurationManager.GetSection("Toggles");
return toggleSection != null;
public bool GetToggleStatus(string toggle)
return ParseBoolValueFromConfig(_toggles.GetValues(toggle)?[0]);
public bool ParseBoolValueFromConfig(string status)
Config Parser Interface
namespace FeatureToggle.Interfaces
public interface IConfigParser
bool GetToggleStatus(string toggle);
bool ParseBoolValueFromConfig(string status);
Integration tests
namespace FeatureToggleTests.Integration
using System;
using FeatureToggle.Classes;
using FeatureToggle.Enums;
using FeatureToggle.Interfaces;
using NUnit.Framework;
[TestFixture]
public class FeatureToggleIntegrationTests
[Test]
public void TestToggleStatusActiveIsReturnedWhenParsingAnItemThatIsToggledOn()
IConfigParser configParser = new ConfigParser();
IFeatureToggle<bool> featureToggle = new FeatureToggle<bool>();
var toggleStatus = featureToggle.GetToggleState(configParser, "ButtonToggle");
Assert.AreEqual(ToggleStatus.Active, toggleStatus);
[Test]
public void TestToggleStatusInactiveIsReturnedWhenParsingAnItemThatIsToggledOff()
IConfigParser configParser = new ConfigParser();
IFeatureToggle<bool> featureToggle = new FeatureToggle<bool>();
var toggleStatus = featureToggle.GetToggleState(configParser, "NotFinished");
Assert.AreEqual(ToggleStatus.Inactive, toggleStatus);
[Test]
public void TestOutOfRangeExceptionIsReturnedWhenParsingAnItemThatIsToggledAsdf()
var configParser = new ConfigParser();
IFeatureToggle<bool> featureToggle = new FeatureToggle<bool>();
Assert.Throws<ArgumentOutOfRangeException>(() => featureToggle.GetToggleState(configParser, "asdf"));
[Test]
public void TestNullReferenceExceptionIsReturnedWhenParsingAnItemThatDoesNotExist()
var configParser = new ConfigParser();
IFeatureToggle<bool> featureToggle = new FeatureToggle<bool>();
Assert.Throws<NullReferenceException>(() => featureToggle.GetToggleState(configParser, "wewewewewewewewe"));
[Test]
public void TestFakeMethodWillNotChangeValueIfConfigItemIsToggledToFalse()
var changeMe = "Unchanged";
FakeMethod("FakeFalse", out changeMe);
Assert.AreEqual("Unchanged", changeMe);
[Test]
public void TestFakeMethodWillChangeValueIfConfigItemIsToggledToTrue()
var changeMe = "Unchanged";
FakeMethod("FakeTrue", out changeMe);
Assert.AreEqual("has been changed", changeMe);
[Test]
public void TestActionFakeMethodThatReturnsTrueWillReturnTrueIfConfigItemIsToggledToTrue()
IFeatureToggle<bool> featureToggler = new FeatureToggle<bool>();
var result = featureToggler.ExecuteMethodIfToggleOn(FakeMethodThatReturnsTrue, "FakeTrue");
Assert.IsTrue(result);
[Test]
public void TestActionFakeMethodThatReturnsTrueWillReturnFalseIfConfigItemIsToggledToFalse()
IFeatureToggle<bool> featureToggler = new FeatureToggle<bool>();
var result = featureToggler.ExecuteMethodIfToggleOn(FakeMethodThatReturnsTrue, "FakeFalse");
Assert.IsFalse(result);
protected void FakeMethod(string keyName, out string changeMe)
IConfigParser configParser = new ConfigParser();
IFeatureToggle<bool> featureToggle = new FeatureToggle<bool>();
var response = featureToggle.GetToggleState(configParser, keyName);
if (response == ToggleStatus.Active)
changeMe = "has been changed";
return;
changeMe = "Unchanged";
protected bool FakeMethodThatReturnsTrue()
return true;
Unit Tests
namespace FeatureToggleTests.Unit
using System;
using FeatureToggle.Classes;
using FeatureToggle.Enums;
using FeatureToggle.Interfaces;
using NUnit.Framework;
[TestFixture]
public class FeatureToggleTests
[Test]
public void TestSuccessfullParseReturnsToggleStatusActive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<bool>();
var toggleResponse = toggle.GetToggleState(testParser, "positive");
Assert.AreEqual(ToggleStatus.Active, toggleResponse);
[Test]
public void TestUnSuccessfullParseReturnsToggleStatusInactive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<bool>();
var toggleResponse = toggle.GetToggleState(testParser, "anythingElse");
Assert.AreEqual(ToggleStatus.Inactive, toggleResponse);
[Test]
public void TestSuccessfullFuncCallWhenToggleStatusActive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<bool>();
Func<bool> theAction = AlwaysReturnTrue;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "positive");
Assert.AreEqual(true, toggleResponse);
[Test]
public void TestUnSuccessfullFuncCallWhenToggleStatusInactive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<bool>();
Func<bool> theAction = AlwaysReturnTrue;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "anythingElse");
Assert.AreEqual(false, toggleResponse);
[Test]
public void TestSuccessfullFuncStringCallWhenToggleStatusActive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<string>();
Func<string> theAction = AlwaysReturnFire;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "positive");
Assert.AreEqual("Fire", toggleResponse);
[Test]
public void TestUnSuccessfullFuncStringCallWhenToggleStatusInactive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<string>();
Func<string> theAction = AlwaysReturnFire;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "anythingElse");
Assert.AreNotEqual("Fire", toggleResponse);
[Test]
public void TestSuccessfullFuncTestDataTypeCallWhenToggleStatusActive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<TestDataType>();
Func<TestDataType> theAction = AlwaysReturnNewTestDataType;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "positive");
Assert.AreEqual(new TestDataType().HappynessIs, toggleResponse.HappynessIs);
[Test]
public void TestUnSuccessfullFuncTestDataTypeCallWhenToggleStatusInactive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<TestDataType>();
Func<TestDataType> theAction = AlwaysReturnNewTestDataType;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "anythingElse");
Assert.IsNull(toggleResponse);
protected bool AlwaysReturnTrue()
return true;
protected string AlwaysReturnFire()
return "Fire";
protected TestDataType AlwaysReturnNewTestDataType()
return new TestDataType();
public class TestDataType
public string HappynessIs = "Happy";
internal class ConfigParserTestDouble : IConfigParser
public bool ToggleConfigTagExists()
throw new System.NotImplementedException();
public bool GetToggleStatus(string toggle)
return toggle == "positive";
public bool ParseBoolValueFromConfig(string status)
throw new System.NotImplementedException();
Enum
namespace FeatureToggle.Enums
public enum ToggleStatus
Active,
Inactive
Explanation of classes:
FeatureToggle class consumes generic types and a configParser is injected in. When you want to toggle a method in your main library you call FeatureToggle.ExecuteMethodIfToggleOn(). Since it uses generic types it will output your return type/do stuff in method if the "toggle" status is on.
ConfigParser class parses out a true or false value from app.config. though it is implementing IConfigParser so I can extend the behaviour to read from a database or text file or whatever else I might need to read toggle values from.
The enum was there to make an explicit indication if something is toggled on or off, though I think this is over kill and a bool would be fine.
The integration and unit tests are there to prove this works and will pickup any bugs I introduce in any further changes. These tests just implement some quick test doubles with simple behaviour to ensure I can assert against an expected value.
c#
 |Â
show 4 more comments
up vote
10
down vote
favorite
I've written a library that uses generics and funcs to implement a feature toggle system without the need to use if statements.
This was mostly to hone my skills, as I'm still new to this profession. I'd really appreciate any comments on how to improve the code.
It can be found in this following git repo
An extract of the main class is below
Feature Toggle Class
namespace FeatureToggle.Classes
using System;
using Enums;
using Interfaces;
public class FeatureToggle <T> : IFeatureToggle <T>
private ToggleStatus Status(bool active)
return active ?
ToggleStatus.Active :
ToggleStatus.Inactive;
public ToggleStatus GetToggleState(IConfigParser parser, string toggleKey)
return Status(parser.GetToggleStatus(toggleKey));
public void ExecuteMethodIfToggleOn(Action methodToRun, IConfigParser configParser, string keyName)
var response = GetToggleState(configParser, keyName);
if (response == ToggleStatus.Active)
methodToRun();
public void ExecuteMethodIfToggleOn(Action methodToRun, string keyName)
IConfigParser configParser = new ConfigParser();
ExecuteMethodIfToggleOn(methodToRun, configParser, keyName);
public T ExecuteMethodIfToggleOn(Func<T> methodToRun, string keyName)
IConfigParser configParser = new ConfigParser();
return ExecuteMethodIfToggleOn(methodToRun, configParser, keyName);
public T ExecuteMethodIfToggleOn(Func<T> methodToRun, IConfigParser configParser, string keyName)
var response = GetToggleState(configParser, keyName);
if (response == ToggleStatus.Active)
return methodToRun();
return default(T);
Feature Toggle Interface
namespace FeatureToggle.Interfaces
using System;
using Enums;
public interface IFeatureToggle <T>
ToggleStatus GetToggleState(IConfigParser parser, string toggleKey);
void ExecuteMethodIfToggleOn(Action methodToRun, string keyName);
T ExecuteMethodIfToggleOn(Func<T> methodToRun, string keyName);
Config Parser Class
namespace FeatureToggle.Classes
using System;
using System.Collections.Specialized;
using System.Configuration;
using Interfaces;
public class ConfigParser : IConfigParser
private readonly NameValueCollection _toggles;
public ConfigParser()
if (ToggleConfigTagExists())
_toggles = ConfigurationManager.GetSection("Toggles") as NameValueCollection;
public bool ToggleConfigTagExists()
var toggleSection = ConfigurationManager.GetSection("Toggles");
return toggleSection != null;
public bool GetToggleStatus(string toggle)
return ParseBoolValueFromConfig(_toggles.GetValues(toggle)?[0]);
public bool ParseBoolValueFromConfig(string status)
Config Parser Interface
namespace FeatureToggle.Interfaces
public interface IConfigParser
bool GetToggleStatus(string toggle);
bool ParseBoolValueFromConfig(string status);
Integration tests
namespace FeatureToggleTests.Integration
using System;
using FeatureToggle.Classes;
using FeatureToggle.Enums;
using FeatureToggle.Interfaces;
using NUnit.Framework;
[TestFixture]
public class FeatureToggleIntegrationTests
[Test]
public void TestToggleStatusActiveIsReturnedWhenParsingAnItemThatIsToggledOn()
IConfigParser configParser = new ConfigParser();
IFeatureToggle<bool> featureToggle = new FeatureToggle<bool>();
var toggleStatus = featureToggle.GetToggleState(configParser, "ButtonToggle");
Assert.AreEqual(ToggleStatus.Active, toggleStatus);
[Test]
public void TestToggleStatusInactiveIsReturnedWhenParsingAnItemThatIsToggledOff()
IConfigParser configParser = new ConfigParser();
IFeatureToggle<bool> featureToggle = new FeatureToggle<bool>();
var toggleStatus = featureToggle.GetToggleState(configParser, "NotFinished");
Assert.AreEqual(ToggleStatus.Inactive, toggleStatus);
[Test]
public void TestOutOfRangeExceptionIsReturnedWhenParsingAnItemThatIsToggledAsdf()
var configParser = new ConfigParser();
IFeatureToggle<bool> featureToggle = new FeatureToggle<bool>();
Assert.Throws<ArgumentOutOfRangeException>(() => featureToggle.GetToggleState(configParser, "asdf"));
[Test]
public void TestNullReferenceExceptionIsReturnedWhenParsingAnItemThatDoesNotExist()
var configParser = new ConfigParser();
IFeatureToggle<bool> featureToggle = new FeatureToggle<bool>();
Assert.Throws<NullReferenceException>(() => featureToggle.GetToggleState(configParser, "wewewewewewewewe"));
[Test]
public void TestFakeMethodWillNotChangeValueIfConfigItemIsToggledToFalse()
var changeMe = "Unchanged";
FakeMethod("FakeFalse", out changeMe);
Assert.AreEqual("Unchanged", changeMe);
[Test]
public void TestFakeMethodWillChangeValueIfConfigItemIsToggledToTrue()
var changeMe = "Unchanged";
FakeMethod("FakeTrue", out changeMe);
Assert.AreEqual("has been changed", changeMe);
[Test]
public void TestActionFakeMethodThatReturnsTrueWillReturnTrueIfConfigItemIsToggledToTrue()
IFeatureToggle<bool> featureToggler = new FeatureToggle<bool>();
var result = featureToggler.ExecuteMethodIfToggleOn(FakeMethodThatReturnsTrue, "FakeTrue");
Assert.IsTrue(result);
[Test]
public void TestActionFakeMethodThatReturnsTrueWillReturnFalseIfConfigItemIsToggledToFalse()
IFeatureToggle<bool> featureToggler = new FeatureToggle<bool>();
var result = featureToggler.ExecuteMethodIfToggleOn(FakeMethodThatReturnsTrue, "FakeFalse");
Assert.IsFalse(result);
protected void FakeMethod(string keyName, out string changeMe)
IConfigParser configParser = new ConfigParser();
IFeatureToggle<bool> featureToggle = new FeatureToggle<bool>();
var response = featureToggle.GetToggleState(configParser, keyName);
if (response == ToggleStatus.Active)
changeMe = "has been changed";
return;
changeMe = "Unchanged";
protected bool FakeMethodThatReturnsTrue()
return true;
Unit Tests
namespace FeatureToggleTests.Unit
using System;
using FeatureToggle.Classes;
using FeatureToggle.Enums;
using FeatureToggle.Interfaces;
using NUnit.Framework;
[TestFixture]
public class FeatureToggleTests
[Test]
public void TestSuccessfullParseReturnsToggleStatusActive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<bool>();
var toggleResponse = toggle.GetToggleState(testParser, "positive");
Assert.AreEqual(ToggleStatus.Active, toggleResponse);
[Test]
public void TestUnSuccessfullParseReturnsToggleStatusInactive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<bool>();
var toggleResponse = toggle.GetToggleState(testParser, "anythingElse");
Assert.AreEqual(ToggleStatus.Inactive, toggleResponse);
[Test]
public void TestSuccessfullFuncCallWhenToggleStatusActive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<bool>();
Func<bool> theAction = AlwaysReturnTrue;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "positive");
Assert.AreEqual(true, toggleResponse);
[Test]
public void TestUnSuccessfullFuncCallWhenToggleStatusInactive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<bool>();
Func<bool> theAction = AlwaysReturnTrue;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "anythingElse");
Assert.AreEqual(false, toggleResponse);
[Test]
public void TestSuccessfullFuncStringCallWhenToggleStatusActive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<string>();
Func<string> theAction = AlwaysReturnFire;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "positive");
Assert.AreEqual("Fire", toggleResponse);
[Test]
public void TestUnSuccessfullFuncStringCallWhenToggleStatusInactive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<string>();
Func<string> theAction = AlwaysReturnFire;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "anythingElse");
Assert.AreNotEqual("Fire", toggleResponse);
[Test]
public void TestSuccessfullFuncTestDataTypeCallWhenToggleStatusActive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<TestDataType>();
Func<TestDataType> theAction = AlwaysReturnNewTestDataType;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "positive");
Assert.AreEqual(new TestDataType().HappynessIs, toggleResponse.HappynessIs);
[Test]
public void TestUnSuccessfullFuncTestDataTypeCallWhenToggleStatusInactive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<TestDataType>();
Func<TestDataType> theAction = AlwaysReturnNewTestDataType;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "anythingElse");
Assert.IsNull(toggleResponse);
protected bool AlwaysReturnTrue()
return true;
protected string AlwaysReturnFire()
return "Fire";
protected TestDataType AlwaysReturnNewTestDataType()
return new TestDataType();
public class TestDataType
public string HappynessIs = "Happy";
internal class ConfigParserTestDouble : IConfigParser
public bool ToggleConfigTagExists()
throw new System.NotImplementedException();
public bool GetToggleStatus(string toggle)
return toggle == "positive";
public bool ParseBoolValueFromConfig(string status)
throw new System.NotImplementedException();
Enum
namespace FeatureToggle.Enums
public enum ToggleStatus
Active,
Inactive
Explanation of classes:
FeatureToggle class consumes generic types and a configParser is injected in. When you want to toggle a method in your main library you call FeatureToggle.ExecuteMethodIfToggleOn(). Since it uses generic types it will output your return type/do stuff in method if the "toggle" status is on.
ConfigParser class parses out a true or false value from app.config. though it is implementing IConfigParser so I can extend the behaviour to read from a database or text file or whatever else I might need to read toggle values from.
The enum was there to make an explicit indication if something is toggled on or off, though I think this is over kill and a bool would be fine.
The integration and unit tests are there to prove this works and will pickup any bugs I introduce in any further changes. These tests just implement some quick test doubles with simple behaviour to ensure I can assert against an expected value.
c#
I think it would be better if you posted the ConfigParser too because this code seems to heavily rely on it
â IEatBagels
May 9 at 17:39
1
@TopinFrassi added :)
â ScottBot
May 9 at 18:17
For me it's hard to follow the idea of this code. What are you "toggling"? If there's a section in yourConfiguarionManager
, that contains all the strings that correlate to some boolean value, why don't you just retrieve the value and determine whether to execute the method or not just based on that? I'm hesitant to write and an answer as I'm feeling there's something I'm missing.
â Denis
May 9 at 19:42
Well It's to "toggle" pieces of code on or off. The configuration manager stuff is just one implementation of IConfigParser. The idea of it is you inject your implementation of IConfigParser in to the feature toggle. This then allows you to execute a method or not. So instead of wrapping your toggled code in if statements adding further levels of indentation you call your method as a parameter
â ScottBot
May 9 at 20:04
Another approach would be the 'plugin' paradigm.
â Aybe
May 11 at 17:48
 |Â
show 4 more comments
up vote
10
down vote
favorite
up vote
10
down vote
favorite
I've written a library that uses generics and funcs to implement a feature toggle system without the need to use if statements.
This was mostly to hone my skills, as I'm still new to this profession. I'd really appreciate any comments on how to improve the code.
It can be found in this following git repo
An extract of the main class is below
Feature Toggle Class
namespace FeatureToggle.Classes
using System;
using Enums;
using Interfaces;
public class FeatureToggle <T> : IFeatureToggle <T>
private ToggleStatus Status(bool active)
return active ?
ToggleStatus.Active :
ToggleStatus.Inactive;
public ToggleStatus GetToggleState(IConfigParser parser, string toggleKey)
return Status(parser.GetToggleStatus(toggleKey));
public void ExecuteMethodIfToggleOn(Action methodToRun, IConfigParser configParser, string keyName)
var response = GetToggleState(configParser, keyName);
if (response == ToggleStatus.Active)
methodToRun();
public void ExecuteMethodIfToggleOn(Action methodToRun, string keyName)
IConfigParser configParser = new ConfigParser();
ExecuteMethodIfToggleOn(methodToRun, configParser, keyName);
public T ExecuteMethodIfToggleOn(Func<T> methodToRun, string keyName)
IConfigParser configParser = new ConfigParser();
return ExecuteMethodIfToggleOn(methodToRun, configParser, keyName);
public T ExecuteMethodIfToggleOn(Func<T> methodToRun, IConfigParser configParser, string keyName)
var response = GetToggleState(configParser, keyName);
if (response == ToggleStatus.Active)
return methodToRun();
return default(T);
Feature Toggle Interface
namespace FeatureToggle.Interfaces
using System;
using Enums;
public interface IFeatureToggle <T>
ToggleStatus GetToggleState(IConfigParser parser, string toggleKey);
void ExecuteMethodIfToggleOn(Action methodToRun, string keyName);
T ExecuteMethodIfToggleOn(Func<T> methodToRun, string keyName);
Config Parser Class
namespace FeatureToggle.Classes
using System;
using System.Collections.Specialized;
using System.Configuration;
using Interfaces;
public class ConfigParser : IConfigParser
private readonly NameValueCollection _toggles;
public ConfigParser()
if (ToggleConfigTagExists())
_toggles = ConfigurationManager.GetSection("Toggles") as NameValueCollection;
public bool ToggleConfigTagExists()
var toggleSection = ConfigurationManager.GetSection("Toggles");
return toggleSection != null;
public bool GetToggleStatus(string toggle)
return ParseBoolValueFromConfig(_toggles.GetValues(toggle)?[0]);
public bool ParseBoolValueFromConfig(string status)
Config Parser Interface
namespace FeatureToggle.Interfaces
public interface IConfigParser
bool GetToggleStatus(string toggle);
bool ParseBoolValueFromConfig(string status);
Integration tests
namespace FeatureToggleTests.Integration
using System;
using FeatureToggle.Classes;
using FeatureToggle.Enums;
using FeatureToggle.Interfaces;
using NUnit.Framework;
[TestFixture]
public class FeatureToggleIntegrationTests
[Test]
public void TestToggleStatusActiveIsReturnedWhenParsingAnItemThatIsToggledOn()
IConfigParser configParser = new ConfigParser();
IFeatureToggle<bool> featureToggle = new FeatureToggle<bool>();
var toggleStatus = featureToggle.GetToggleState(configParser, "ButtonToggle");
Assert.AreEqual(ToggleStatus.Active, toggleStatus);
[Test]
public void TestToggleStatusInactiveIsReturnedWhenParsingAnItemThatIsToggledOff()
IConfigParser configParser = new ConfigParser();
IFeatureToggle<bool> featureToggle = new FeatureToggle<bool>();
var toggleStatus = featureToggle.GetToggleState(configParser, "NotFinished");
Assert.AreEqual(ToggleStatus.Inactive, toggleStatus);
[Test]
public void TestOutOfRangeExceptionIsReturnedWhenParsingAnItemThatIsToggledAsdf()
var configParser = new ConfigParser();
IFeatureToggle<bool> featureToggle = new FeatureToggle<bool>();
Assert.Throws<ArgumentOutOfRangeException>(() => featureToggle.GetToggleState(configParser, "asdf"));
[Test]
public void TestNullReferenceExceptionIsReturnedWhenParsingAnItemThatDoesNotExist()
var configParser = new ConfigParser();
IFeatureToggle<bool> featureToggle = new FeatureToggle<bool>();
Assert.Throws<NullReferenceException>(() => featureToggle.GetToggleState(configParser, "wewewewewewewewe"));
[Test]
public void TestFakeMethodWillNotChangeValueIfConfigItemIsToggledToFalse()
var changeMe = "Unchanged";
FakeMethod("FakeFalse", out changeMe);
Assert.AreEqual("Unchanged", changeMe);
[Test]
public void TestFakeMethodWillChangeValueIfConfigItemIsToggledToTrue()
var changeMe = "Unchanged";
FakeMethod("FakeTrue", out changeMe);
Assert.AreEqual("has been changed", changeMe);
[Test]
public void TestActionFakeMethodThatReturnsTrueWillReturnTrueIfConfigItemIsToggledToTrue()
IFeatureToggle<bool> featureToggler = new FeatureToggle<bool>();
var result = featureToggler.ExecuteMethodIfToggleOn(FakeMethodThatReturnsTrue, "FakeTrue");
Assert.IsTrue(result);
[Test]
public void TestActionFakeMethodThatReturnsTrueWillReturnFalseIfConfigItemIsToggledToFalse()
IFeatureToggle<bool> featureToggler = new FeatureToggle<bool>();
var result = featureToggler.ExecuteMethodIfToggleOn(FakeMethodThatReturnsTrue, "FakeFalse");
Assert.IsFalse(result);
protected void FakeMethod(string keyName, out string changeMe)
IConfigParser configParser = new ConfigParser();
IFeatureToggle<bool> featureToggle = new FeatureToggle<bool>();
var response = featureToggle.GetToggleState(configParser, keyName);
if (response == ToggleStatus.Active)
changeMe = "has been changed";
return;
changeMe = "Unchanged";
protected bool FakeMethodThatReturnsTrue()
return true;
Unit Tests
namespace FeatureToggleTests.Unit
using System;
using FeatureToggle.Classes;
using FeatureToggle.Enums;
using FeatureToggle.Interfaces;
using NUnit.Framework;
[TestFixture]
public class FeatureToggleTests
[Test]
public void TestSuccessfullParseReturnsToggleStatusActive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<bool>();
var toggleResponse = toggle.GetToggleState(testParser, "positive");
Assert.AreEqual(ToggleStatus.Active, toggleResponse);
[Test]
public void TestUnSuccessfullParseReturnsToggleStatusInactive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<bool>();
var toggleResponse = toggle.GetToggleState(testParser, "anythingElse");
Assert.AreEqual(ToggleStatus.Inactive, toggleResponse);
[Test]
public void TestSuccessfullFuncCallWhenToggleStatusActive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<bool>();
Func<bool> theAction = AlwaysReturnTrue;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "positive");
Assert.AreEqual(true, toggleResponse);
[Test]
public void TestUnSuccessfullFuncCallWhenToggleStatusInactive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<bool>();
Func<bool> theAction = AlwaysReturnTrue;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "anythingElse");
Assert.AreEqual(false, toggleResponse);
[Test]
public void TestSuccessfullFuncStringCallWhenToggleStatusActive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<string>();
Func<string> theAction = AlwaysReturnFire;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "positive");
Assert.AreEqual("Fire", toggleResponse);
[Test]
public void TestUnSuccessfullFuncStringCallWhenToggleStatusInactive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<string>();
Func<string> theAction = AlwaysReturnFire;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "anythingElse");
Assert.AreNotEqual("Fire", toggleResponse);
[Test]
public void TestSuccessfullFuncTestDataTypeCallWhenToggleStatusActive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<TestDataType>();
Func<TestDataType> theAction = AlwaysReturnNewTestDataType;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "positive");
Assert.AreEqual(new TestDataType().HappynessIs, toggleResponse.HappynessIs);
[Test]
public void TestUnSuccessfullFuncTestDataTypeCallWhenToggleStatusInactive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<TestDataType>();
Func<TestDataType> theAction = AlwaysReturnNewTestDataType;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "anythingElse");
Assert.IsNull(toggleResponse);
protected bool AlwaysReturnTrue()
return true;
protected string AlwaysReturnFire()
return "Fire";
protected TestDataType AlwaysReturnNewTestDataType()
return new TestDataType();
public class TestDataType
public string HappynessIs = "Happy";
internal class ConfigParserTestDouble : IConfigParser
public bool ToggleConfigTagExists()
throw new System.NotImplementedException();
public bool GetToggleStatus(string toggle)
return toggle == "positive";
public bool ParseBoolValueFromConfig(string status)
throw new System.NotImplementedException();
Enum
namespace FeatureToggle.Enums
public enum ToggleStatus
Active,
Inactive
Explanation of classes:
FeatureToggle class consumes generic types and a configParser is injected in. When you want to toggle a method in your main library you call FeatureToggle.ExecuteMethodIfToggleOn(). Since it uses generic types it will output your return type/do stuff in method if the "toggle" status is on.
ConfigParser class parses out a true or false value from app.config. though it is implementing IConfigParser so I can extend the behaviour to read from a database or text file or whatever else I might need to read toggle values from.
The enum was there to make an explicit indication if something is toggled on or off, though I think this is over kill and a bool would be fine.
The integration and unit tests are there to prove this works and will pickup any bugs I introduce in any further changes. These tests just implement some quick test doubles with simple behaviour to ensure I can assert against an expected value.
c#
I've written a library that uses generics and funcs to implement a feature toggle system without the need to use if statements.
This was mostly to hone my skills, as I'm still new to this profession. I'd really appreciate any comments on how to improve the code.
It can be found in this following git repo
An extract of the main class is below
Feature Toggle Class
namespace FeatureToggle.Classes
using System;
using Enums;
using Interfaces;
public class FeatureToggle <T> : IFeatureToggle <T>
private ToggleStatus Status(bool active)
return active ?
ToggleStatus.Active :
ToggleStatus.Inactive;
public ToggleStatus GetToggleState(IConfigParser parser, string toggleKey)
return Status(parser.GetToggleStatus(toggleKey));
public void ExecuteMethodIfToggleOn(Action methodToRun, IConfigParser configParser, string keyName)
var response = GetToggleState(configParser, keyName);
if (response == ToggleStatus.Active)
methodToRun();
public void ExecuteMethodIfToggleOn(Action methodToRun, string keyName)
IConfigParser configParser = new ConfigParser();
ExecuteMethodIfToggleOn(methodToRun, configParser, keyName);
public T ExecuteMethodIfToggleOn(Func<T> methodToRun, string keyName)
IConfigParser configParser = new ConfigParser();
return ExecuteMethodIfToggleOn(methodToRun, configParser, keyName);
public T ExecuteMethodIfToggleOn(Func<T> methodToRun, IConfigParser configParser, string keyName)
var response = GetToggleState(configParser, keyName);
if (response == ToggleStatus.Active)
return methodToRun();
return default(T);
Feature Toggle Interface
namespace FeatureToggle.Interfaces
using System;
using Enums;
public interface IFeatureToggle <T>
ToggleStatus GetToggleState(IConfigParser parser, string toggleKey);
void ExecuteMethodIfToggleOn(Action methodToRun, string keyName);
T ExecuteMethodIfToggleOn(Func<T> methodToRun, string keyName);
Config Parser Class
namespace FeatureToggle.Classes
using System;
using System.Collections.Specialized;
using System.Configuration;
using Interfaces;
public class ConfigParser : IConfigParser
private readonly NameValueCollection _toggles;
public ConfigParser()
if (ToggleConfigTagExists())
_toggles = ConfigurationManager.GetSection("Toggles") as NameValueCollection;
public bool ToggleConfigTagExists()
var toggleSection = ConfigurationManager.GetSection("Toggles");
return toggleSection != null;
public bool GetToggleStatus(string toggle)
return ParseBoolValueFromConfig(_toggles.GetValues(toggle)?[0]);
public bool ParseBoolValueFromConfig(string status)
Config Parser Interface
namespace FeatureToggle.Interfaces
public interface IConfigParser
bool GetToggleStatus(string toggle);
bool ParseBoolValueFromConfig(string status);
Integration tests
namespace FeatureToggleTests.Integration
using System;
using FeatureToggle.Classes;
using FeatureToggle.Enums;
using FeatureToggle.Interfaces;
using NUnit.Framework;
[TestFixture]
public class FeatureToggleIntegrationTests
[Test]
public void TestToggleStatusActiveIsReturnedWhenParsingAnItemThatIsToggledOn()
IConfigParser configParser = new ConfigParser();
IFeatureToggle<bool> featureToggle = new FeatureToggle<bool>();
var toggleStatus = featureToggle.GetToggleState(configParser, "ButtonToggle");
Assert.AreEqual(ToggleStatus.Active, toggleStatus);
[Test]
public void TestToggleStatusInactiveIsReturnedWhenParsingAnItemThatIsToggledOff()
IConfigParser configParser = new ConfigParser();
IFeatureToggle<bool> featureToggle = new FeatureToggle<bool>();
var toggleStatus = featureToggle.GetToggleState(configParser, "NotFinished");
Assert.AreEqual(ToggleStatus.Inactive, toggleStatus);
[Test]
public void TestOutOfRangeExceptionIsReturnedWhenParsingAnItemThatIsToggledAsdf()
var configParser = new ConfigParser();
IFeatureToggle<bool> featureToggle = new FeatureToggle<bool>();
Assert.Throws<ArgumentOutOfRangeException>(() => featureToggle.GetToggleState(configParser, "asdf"));
[Test]
public void TestNullReferenceExceptionIsReturnedWhenParsingAnItemThatDoesNotExist()
var configParser = new ConfigParser();
IFeatureToggle<bool> featureToggle = new FeatureToggle<bool>();
Assert.Throws<NullReferenceException>(() => featureToggle.GetToggleState(configParser, "wewewewewewewewe"));
[Test]
public void TestFakeMethodWillNotChangeValueIfConfigItemIsToggledToFalse()
var changeMe = "Unchanged";
FakeMethod("FakeFalse", out changeMe);
Assert.AreEqual("Unchanged", changeMe);
[Test]
public void TestFakeMethodWillChangeValueIfConfigItemIsToggledToTrue()
var changeMe = "Unchanged";
FakeMethod("FakeTrue", out changeMe);
Assert.AreEqual("has been changed", changeMe);
[Test]
public void TestActionFakeMethodThatReturnsTrueWillReturnTrueIfConfigItemIsToggledToTrue()
IFeatureToggle<bool> featureToggler = new FeatureToggle<bool>();
var result = featureToggler.ExecuteMethodIfToggleOn(FakeMethodThatReturnsTrue, "FakeTrue");
Assert.IsTrue(result);
[Test]
public void TestActionFakeMethodThatReturnsTrueWillReturnFalseIfConfigItemIsToggledToFalse()
IFeatureToggle<bool> featureToggler = new FeatureToggle<bool>();
var result = featureToggler.ExecuteMethodIfToggleOn(FakeMethodThatReturnsTrue, "FakeFalse");
Assert.IsFalse(result);
protected void FakeMethod(string keyName, out string changeMe)
IConfigParser configParser = new ConfigParser();
IFeatureToggle<bool> featureToggle = new FeatureToggle<bool>();
var response = featureToggle.GetToggleState(configParser, keyName);
if (response == ToggleStatus.Active)
changeMe = "has been changed";
return;
changeMe = "Unchanged";
protected bool FakeMethodThatReturnsTrue()
return true;
Unit Tests
namespace FeatureToggleTests.Unit
using System;
using FeatureToggle.Classes;
using FeatureToggle.Enums;
using FeatureToggle.Interfaces;
using NUnit.Framework;
[TestFixture]
public class FeatureToggleTests
[Test]
public void TestSuccessfullParseReturnsToggleStatusActive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<bool>();
var toggleResponse = toggle.GetToggleState(testParser, "positive");
Assert.AreEqual(ToggleStatus.Active, toggleResponse);
[Test]
public void TestUnSuccessfullParseReturnsToggleStatusInactive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<bool>();
var toggleResponse = toggle.GetToggleState(testParser, "anythingElse");
Assert.AreEqual(ToggleStatus.Inactive, toggleResponse);
[Test]
public void TestSuccessfullFuncCallWhenToggleStatusActive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<bool>();
Func<bool> theAction = AlwaysReturnTrue;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "positive");
Assert.AreEqual(true, toggleResponse);
[Test]
public void TestUnSuccessfullFuncCallWhenToggleStatusInactive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<bool>();
Func<bool> theAction = AlwaysReturnTrue;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "anythingElse");
Assert.AreEqual(false, toggleResponse);
[Test]
public void TestSuccessfullFuncStringCallWhenToggleStatusActive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<string>();
Func<string> theAction = AlwaysReturnFire;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "positive");
Assert.AreEqual("Fire", toggleResponse);
[Test]
public void TestUnSuccessfullFuncStringCallWhenToggleStatusInactive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<string>();
Func<string> theAction = AlwaysReturnFire;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "anythingElse");
Assert.AreNotEqual("Fire", toggleResponse);
[Test]
public void TestSuccessfullFuncTestDataTypeCallWhenToggleStatusActive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<TestDataType>();
Func<TestDataType> theAction = AlwaysReturnNewTestDataType;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "positive");
Assert.AreEqual(new TestDataType().HappynessIs, toggleResponse.HappynessIs);
[Test]
public void TestUnSuccessfullFuncTestDataTypeCallWhenToggleStatusInactive()
IConfigParser testParser = new ConfigParserTestDouble();
var toggle = new FeatureToggle<TestDataType>();
Func<TestDataType> theAction = AlwaysReturnNewTestDataType;
var toggleResponse = toggle.ExecuteMethodIfToggleOn(theAction, testParser, "anythingElse");
Assert.IsNull(toggleResponse);
protected bool AlwaysReturnTrue()
return true;
protected string AlwaysReturnFire()
return "Fire";
protected TestDataType AlwaysReturnNewTestDataType()
return new TestDataType();
public class TestDataType
public string HappynessIs = "Happy";
internal class ConfigParserTestDouble : IConfigParser
public bool ToggleConfigTagExists()
throw new System.NotImplementedException();
public bool GetToggleStatus(string toggle)
return toggle == "positive";
public bool ParseBoolValueFromConfig(string status)
throw new System.NotImplementedException();
Enum
namespace FeatureToggle.Enums
public enum ToggleStatus
Active,
Inactive
Explanation of classes:
FeatureToggle class consumes generic types and a configParser is injected in. When you want to toggle a method in your main library you call FeatureToggle.ExecuteMethodIfToggleOn(). Since it uses generic types it will output your return type/do stuff in method if the "toggle" status is on.
ConfigParser class parses out a true or false value from app.config. though it is implementing IConfigParser so I can extend the behaviour to read from a database or text file or whatever else I might need to read toggle values from.
The enum was there to make an explicit indication if something is toggled on or off, though I think this is over kill and a bool would be fine.
The integration and unit tests are there to prove this works and will pickup any bugs I introduce in any further changes. These tests just implement some quick test doubles with simple behaviour to ensure I can assert against an expected value.
c#
edited May 21 at 11:04
asked May 9 at 16:08
ScottBot
515
515
I think it would be better if you posted the ConfigParser too because this code seems to heavily rely on it
â IEatBagels
May 9 at 17:39
1
@TopinFrassi added :)
â ScottBot
May 9 at 18:17
For me it's hard to follow the idea of this code. What are you "toggling"? If there's a section in yourConfiguarionManager
, that contains all the strings that correlate to some boolean value, why don't you just retrieve the value and determine whether to execute the method or not just based on that? I'm hesitant to write and an answer as I'm feeling there's something I'm missing.
â Denis
May 9 at 19:42
Well It's to "toggle" pieces of code on or off. The configuration manager stuff is just one implementation of IConfigParser. The idea of it is you inject your implementation of IConfigParser in to the feature toggle. This then allows you to execute a method or not. So instead of wrapping your toggled code in if statements adding further levels of indentation you call your method as a parameter
â ScottBot
May 9 at 20:04
Another approach would be the 'plugin' paradigm.
â Aybe
May 11 at 17:48
 |Â
show 4 more comments
I think it would be better if you posted the ConfigParser too because this code seems to heavily rely on it
â IEatBagels
May 9 at 17:39
1
@TopinFrassi added :)
â ScottBot
May 9 at 18:17
For me it's hard to follow the idea of this code. What are you "toggling"? If there's a section in yourConfiguarionManager
, that contains all the strings that correlate to some boolean value, why don't you just retrieve the value and determine whether to execute the method or not just based on that? I'm hesitant to write and an answer as I'm feeling there's something I'm missing.
â Denis
May 9 at 19:42
Well It's to "toggle" pieces of code on or off. The configuration manager stuff is just one implementation of IConfigParser. The idea of it is you inject your implementation of IConfigParser in to the feature toggle. This then allows you to execute a method or not. So instead of wrapping your toggled code in if statements adding further levels of indentation you call your method as a parameter
â ScottBot
May 9 at 20:04
Another approach would be the 'plugin' paradigm.
â Aybe
May 11 at 17:48
I think it would be better if you posted the ConfigParser too because this code seems to heavily rely on it
â IEatBagels
May 9 at 17:39
I think it would be better if you posted the ConfigParser too because this code seems to heavily rely on it
â IEatBagels
May 9 at 17:39
1
1
@TopinFrassi added :)
â ScottBot
May 9 at 18:17
@TopinFrassi added :)
â ScottBot
May 9 at 18:17
For me it's hard to follow the idea of this code. What are you "toggling"? If there's a section in your
ConfiguarionManager
, that contains all the strings that correlate to some boolean value, why don't you just retrieve the value and determine whether to execute the method or not just based on that? I'm hesitant to write and an answer as I'm feeling there's something I'm missing.â Denis
May 9 at 19:42
For me it's hard to follow the idea of this code. What are you "toggling"? If there's a section in your
ConfiguarionManager
, that contains all the strings that correlate to some boolean value, why don't you just retrieve the value and determine whether to execute the method or not just based on that? I'm hesitant to write and an answer as I'm feeling there's something I'm missing.â Denis
May 9 at 19:42
Well It's to "toggle" pieces of code on or off. The configuration manager stuff is just one implementation of IConfigParser. The idea of it is you inject your implementation of IConfigParser in to the feature toggle. This then allows you to execute a method or not. So instead of wrapping your toggled code in if statements adding further levels of indentation you call your method as a parameter
â ScottBot
May 9 at 20:04
Well It's to "toggle" pieces of code on or off. The configuration manager stuff is just one implementation of IConfigParser. The idea of it is you inject your implementation of IConfigParser in to the feature toggle. This then allows you to execute a method or not. So instead of wrapping your toggled code in if statements adding further levels of indentation you call your method as a parameter
â ScottBot
May 9 at 20:04
Another approach would be the 'plugin' paradigm.
â Aybe
May 11 at 17:48
Another approach would be the 'plugin' paradigm.
â Aybe
May 11 at 17:48
 |Â
show 4 more comments
1 Answer
1
active
oldest
votes
up vote
3
down vote
Here, I am posting an alternative approach you could try.
Instead of this you could make a plugin system,
Pros
- Your application does not have potentially toggled code
- Concerns are separated, your app does the plumbing, features are delegated
- Plugins work against a well-thought/defined public interface
- Testing might be easier in the end (rough guess)
- (whatever else I forgot)
Cons
- A bit of plumbing is involved at the beginning
- More types in the end
- (whatever else I forgot)
What a plugin implements:
public interface IPlugin
void Do(IWorkspace workspace);
The workspace it performs operations on (here it's very simple):
public interface IWorkspace
void ShowMessage(string message);
Two examples of plugins:
public sealed class Plugin1 : IPlugin
public void Do(IWorkspace workspace)
workspace.ShowMessage("Plugin1 here !");
public sealed class Plugin2 : IPlugin
public void Do(IWorkspace workspace)
workspace.ShowMessage("Plugin2 here !");
An example of the workspace (should be in your app):
using System.Windows;
internal sealed class Workspace : IWorkspace
public void ShowMessage(string message)
MessageBox.Show(message);
Here the mini-app that shows these plugins (WPF):
XAML
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Menu x:Name="Menu" />
</Grid>
</Window>
Code behind:
using System;
using System.Linq;
using System.Reflection;
using System.Windows.Controls;
namespace WpfApp1
public partial class MainWindow
public MainWindow()
InitializeComponent();
// get the plugins in assembly
var plugins = Assembly
.GetExecutingAssembly()
.GetTypes()
.Where(s => typeof(IPlugin).IsAssignableFrom(s))
.Where(s => s.IsClass)
.Where(s => s.GetConstructor(Type.EmptyTypes) != null)
.Select(s => (IPlugin) Activator.CreateInstance(s))
.ToArray()
;
// populate plugins in menu
var root = new MenuItem Header = "Plugins";
foreach (var plugin in plugins)
var item = new MenuItem Header = plugin.GetType().Name;
item.Click += (sender, args) => plugin.Do(Workspace); ;
root.Items.Add(item);
Menu.Items.Add(root);
private IWorkspace Workspace get; = new Workspace();
IMHO the pros definitely outweighs the cons, while the beginning is a little difficult, in the end things are well separated and your app is less likely to become a huge mess.
One of the strong arguments is that your plugins work against a public interface and don't see/deal with whatever (private) gory details of your app.
What you should do next:
- your workspace/plugin system ends in its own assembly, it will be referenced by your app, plugins and unit tests
- code your plugins into separate assemblies, organize them as you see fit
- make a system that dynamically dis/en-ables plugins and update the menu, much like some extensions in Visual Studio
This is merely an example to push you to think out of the box, change it to your needs but IMO it address a very important aspect: no more spaghetti logic in your app.
Thanks aybe, I've done some digging in to the plugin paradigm and I feel that it would possibly take more effort to achieve my desired outcome using it. I am interested in it however and will look to use it at work. My issue at the moment is making the code self descriptive, I find that the most challenging, and would probably struggle to achieve that with plugin
â ScottBot
May 12 at 19:13
Good luck mate !
â Aybe
May 14 at 6:31
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
3
down vote
Here, I am posting an alternative approach you could try.
Instead of this you could make a plugin system,
Pros
- Your application does not have potentially toggled code
- Concerns are separated, your app does the plumbing, features are delegated
- Plugins work against a well-thought/defined public interface
- Testing might be easier in the end (rough guess)
- (whatever else I forgot)
Cons
- A bit of plumbing is involved at the beginning
- More types in the end
- (whatever else I forgot)
What a plugin implements:
public interface IPlugin
void Do(IWorkspace workspace);
The workspace it performs operations on (here it's very simple):
public interface IWorkspace
void ShowMessage(string message);
Two examples of plugins:
public sealed class Plugin1 : IPlugin
public void Do(IWorkspace workspace)
workspace.ShowMessage("Plugin1 here !");
public sealed class Plugin2 : IPlugin
public void Do(IWorkspace workspace)
workspace.ShowMessage("Plugin2 here !");
An example of the workspace (should be in your app):
using System.Windows;
internal sealed class Workspace : IWorkspace
public void ShowMessage(string message)
MessageBox.Show(message);
Here the mini-app that shows these plugins (WPF):
XAML
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Menu x:Name="Menu" />
</Grid>
</Window>
Code behind:
using System;
using System.Linq;
using System.Reflection;
using System.Windows.Controls;
namespace WpfApp1
public partial class MainWindow
public MainWindow()
InitializeComponent();
// get the plugins in assembly
var plugins = Assembly
.GetExecutingAssembly()
.GetTypes()
.Where(s => typeof(IPlugin).IsAssignableFrom(s))
.Where(s => s.IsClass)
.Where(s => s.GetConstructor(Type.EmptyTypes) != null)
.Select(s => (IPlugin) Activator.CreateInstance(s))
.ToArray()
;
// populate plugins in menu
var root = new MenuItem Header = "Plugins";
foreach (var plugin in plugins)
var item = new MenuItem Header = plugin.GetType().Name;
item.Click += (sender, args) => plugin.Do(Workspace); ;
root.Items.Add(item);
Menu.Items.Add(root);
private IWorkspace Workspace get; = new Workspace();
IMHO the pros definitely outweighs the cons, while the beginning is a little difficult, in the end things are well separated and your app is less likely to become a huge mess.
One of the strong arguments is that your plugins work against a public interface and don't see/deal with whatever (private) gory details of your app.
What you should do next:
- your workspace/plugin system ends in its own assembly, it will be referenced by your app, plugins and unit tests
- code your plugins into separate assemblies, organize them as you see fit
- make a system that dynamically dis/en-ables plugins and update the menu, much like some extensions in Visual Studio
This is merely an example to push you to think out of the box, change it to your needs but IMO it address a very important aspect: no more spaghetti logic in your app.
Thanks aybe, I've done some digging in to the plugin paradigm and I feel that it would possibly take more effort to achieve my desired outcome using it. I am interested in it however and will look to use it at work. My issue at the moment is making the code self descriptive, I find that the most challenging, and would probably struggle to achieve that with plugin
â ScottBot
May 12 at 19:13
Good luck mate !
â Aybe
May 14 at 6:31
add a comment |Â
up vote
3
down vote
Here, I am posting an alternative approach you could try.
Instead of this you could make a plugin system,
Pros
- Your application does not have potentially toggled code
- Concerns are separated, your app does the plumbing, features are delegated
- Plugins work against a well-thought/defined public interface
- Testing might be easier in the end (rough guess)
- (whatever else I forgot)
Cons
- A bit of plumbing is involved at the beginning
- More types in the end
- (whatever else I forgot)
What a plugin implements:
public interface IPlugin
void Do(IWorkspace workspace);
The workspace it performs operations on (here it's very simple):
public interface IWorkspace
void ShowMessage(string message);
Two examples of plugins:
public sealed class Plugin1 : IPlugin
public void Do(IWorkspace workspace)
workspace.ShowMessage("Plugin1 here !");
public sealed class Plugin2 : IPlugin
public void Do(IWorkspace workspace)
workspace.ShowMessage("Plugin2 here !");
An example of the workspace (should be in your app):
using System.Windows;
internal sealed class Workspace : IWorkspace
public void ShowMessage(string message)
MessageBox.Show(message);
Here the mini-app that shows these plugins (WPF):
XAML
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Menu x:Name="Menu" />
</Grid>
</Window>
Code behind:
using System;
using System.Linq;
using System.Reflection;
using System.Windows.Controls;
namespace WpfApp1
public partial class MainWindow
public MainWindow()
InitializeComponent();
// get the plugins in assembly
var plugins = Assembly
.GetExecutingAssembly()
.GetTypes()
.Where(s => typeof(IPlugin).IsAssignableFrom(s))
.Where(s => s.IsClass)
.Where(s => s.GetConstructor(Type.EmptyTypes) != null)
.Select(s => (IPlugin) Activator.CreateInstance(s))
.ToArray()
;
// populate plugins in menu
var root = new MenuItem Header = "Plugins";
foreach (var plugin in plugins)
var item = new MenuItem Header = plugin.GetType().Name;
item.Click += (sender, args) => plugin.Do(Workspace); ;
root.Items.Add(item);
Menu.Items.Add(root);
private IWorkspace Workspace get; = new Workspace();
IMHO the pros definitely outweighs the cons, while the beginning is a little difficult, in the end things are well separated and your app is less likely to become a huge mess.
One of the strong arguments is that your plugins work against a public interface and don't see/deal with whatever (private) gory details of your app.
What you should do next:
- your workspace/plugin system ends in its own assembly, it will be referenced by your app, plugins and unit tests
- code your plugins into separate assemblies, organize them as you see fit
- make a system that dynamically dis/en-ables plugins and update the menu, much like some extensions in Visual Studio
This is merely an example to push you to think out of the box, change it to your needs but IMO it address a very important aspect: no more spaghetti logic in your app.
Thanks aybe, I've done some digging in to the plugin paradigm and I feel that it would possibly take more effort to achieve my desired outcome using it. I am interested in it however and will look to use it at work. My issue at the moment is making the code self descriptive, I find that the most challenging, and would probably struggle to achieve that with plugin
â ScottBot
May 12 at 19:13
Good luck mate !
â Aybe
May 14 at 6:31
add a comment |Â
up vote
3
down vote
up vote
3
down vote
Here, I am posting an alternative approach you could try.
Instead of this you could make a plugin system,
Pros
- Your application does not have potentially toggled code
- Concerns are separated, your app does the plumbing, features are delegated
- Plugins work against a well-thought/defined public interface
- Testing might be easier in the end (rough guess)
- (whatever else I forgot)
Cons
- A bit of plumbing is involved at the beginning
- More types in the end
- (whatever else I forgot)
What a plugin implements:
public interface IPlugin
void Do(IWorkspace workspace);
The workspace it performs operations on (here it's very simple):
public interface IWorkspace
void ShowMessage(string message);
Two examples of plugins:
public sealed class Plugin1 : IPlugin
public void Do(IWorkspace workspace)
workspace.ShowMessage("Plugin1 here !");
public sealed class Plugin2 : IPlugin
public void Do(IWorkspace workspace)
workspace.ShowMessage("Plugin2 here !");
An example of the workspace (should be in your app):
using System.Windows;
internal sealed class Workspace : IWorkspace
public void ShowMessage(string message)
MessageBox.Show(message);
Here the mini-app that shows these plugins (WPF):
XAML
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Menu x:Name="Menu" />
</Grid>
</Window>
Code behind:
using System;
using System.Linq;
using System.Reflection;
using System.Windows.Controls;
namespace WpfApp1
public partial class MainWindow
public MainWindow()
InitializeComponent();
// get the plugins in assembly
var plugins = Assembly
.GetExecutingAssembly()
.GetTypes()
.Where(s => typeof(IPlugin).IsAssignableFrom(s))
.Where(s => s.IsClass)
.Where(s => s.GetConstructor(Type.EmptyTypes) != null)
.Select(s => (IPlugin) Activator.CreateInstance(s))
.ToArray()
;
// populate plugins in menu
var root = new MenuItem Header = "Plugins";
foreach (var plugin in plugins)
var item = new MenuItem Header = plugin.GetType().Name;
item.Click += (sender, args) => plugin.Do(Workspace); ;
root.Items.Add(item);
Menu.Items.Add(root);
private IWorkspace Workspace get; = new Workspace();
IMHO the pros definitely outweighs the cons, while the beginning is a little difficult, in the end things are well separated and your app is less likely to become a huge mess.
One of the strong arguments is that your plugins work against a public interface and don't see/deal with whatever (private) gory details of your app.
What you should do next:
- your workspace/plugin system ends in its own assembly, it will be referenced by your app, plugins and unit tests
- code your plugins into separate assemblies, organize them as you see fit
- make a system that dynamically dis/en-ables plugins and update the menu, much like some extensions in Visual Studio
This is merely an example to push you to think out of the box, change it to your needs but IMO it address a very important aspect: no more spaghetti logic in your app.
Here, I am posting an alternative approach you could try.
Instead of this you could make a plugin system,
Pros
- Your application does not have potentially toggled code
- Concerns are separated, your app does the plumbing, features are delegated
- Plugins work against a well-thought/defined public interface
- Testing might be easier in the end (rough guess)
- (whatever else I forgot)
Cons
- A bit of plumbing is involved at the beginning
- More types in the end
- (whatever else I forgot)
What a plugin implements:
public interface IPlugin
void Do(IWorkspace workspace);
The workspace it performs operations on (here it's very simple):
public interface IWorkspace
void ShowMessage(string message);
Two examples of plugins:
public sealed class Plugin1 : IPlugin
public void Do(IWorkspace workspace)
workspace.ShowMessage("Plugin1 here !");
public sealed class Plugin2 : IPlugin
public void Do(IWorkspace workspace)
workspace.ShowMessage("Plugin2 here !");
An example of the workspace (should be in your app):
using System.Windows;
internal sealed class Workspace : IWorkspace
public void ShowMessage(string message)
MessageBox.Show(message);
Here the mini-app that shows these plugins (WPF):
XAML
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Menu x:Name="Menu" />
</Grid>
</Window>
Code behind:
using System;
using System.Linq;
using System.Reflection;
using System.Windows.Controls;
namespace WpfApp1
public partial class MainWindow
public MainWindow()
InitializeComponent();
// get the plugins in assembly
var plugins = Assembly
.GetExecutingAssembly()
.GetTypes()
.Where(s => typeof(IPlugin).IsAssignableFrom(s))
.Where(s => s.IsClass)
.Where(s => s.GetConstructor(Type.EmptyTypes) != null)
.Select(s => (IPlugin) Activator.CreateInstance(s))
.ToArray()
;
// populate plugins in menu
var root = new MenuItem Header = "Plugins";
foreach (var plugin in plugins)
var item = new MenuItem Header = plugin.GetType().Name;
item.Click += (sender, args) => plugin.Do(Workspace); ;
root.Items.Add(item);
Menu.Items.Add(root);
private IWorkspace Workspace get; = new Workspace();
IMHO the pros definitely outweighs the cons, while the beginning is a little difficult, in the end things are well separated and your app is less likely to become a huge mess.
One of the strong arguments is that your plugins work against a public interface and don't see/deal with whatever (private) gory details of your app.
What you should do next:
- your workspace/plugin system ends in its own assembly, it will be referenced by your app, plugins and unit tests
- code your plugins into separate assemblies, organize them as you see fit
- make a system that dynamically dis/en-ables plugins and update the menu, much like some extensions in Visual Studio
This is merely an example to push you to think out of the box, change it to your needs but IMO it address a very important aspect: no more spaghetti logic in your app.
answered May 11 at 19:55
Aybe
3871313
3871313
Thanks aybe, I've done some digging in to the plugin paradigm and I feel that it would possibly take more effort to achieve my desired outcome using it. I am interested in it however and will look to use it at work. My issue at the moment is making the code self descriptive, I find that the most challenging, and would probably struggle to achieve that with plugin
â ScottBot
May 12 at 19:13
Good luck mate !
â Aybe
May 14 at 6:31
add a comment |Â
Thanks aybe, I've done some digging in to the plugin paradigm and I feel that it would possibly take more effort to achieve my desired outcome using it. I am interested in it however and will look to use it at work. My issue at the moment is making the code self descriptive, I find that the most challenging, and would probably struggle to achieve that with plugin
â ScottBot
May 12 at 19:13
Good luck mate !
â Aybe
May 14 at 6:31
Thanks aybe, I've done some digging in to the plugin paradigm and I feel that it would possibly take more effort to achieve my desired outcome using it. I am interested in it however and will look to use it at work. My issue at the moment is making the code self descriptive, I find that the most challenging, and would probably struggle to achieve that with plugin
â ScottBot
May 12 at 19:13
Thanks aybe, I've done some digging in to the plugin paradigm and I feel that it would possibly take more effort to achieve my desired outcome using it. I am interested in it however and will look to use it at work. My issue at the moment is making the code self descriptive, I find that the most challenging, and would probably struggle to achieve that with plugin
â ScottBot
May 12 at 19:13
Good luck mate !
â Aybe
May 14 at 6:31
Good luck mate !
â Aybe
May 14 at 6:31
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f194024%2ftoggling-features-of-an-application%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
I think it would be better if you posted the ConfigParser too because this code seems to heavily rely on it
â IEatBagels
May 9 at 17:39
1
@TopinFrassi added :)
â ScottBot
May 9 at 18:17
For me it's hard to follow the idea of this code. What are you "toggling"? If there's a section in your
ConfiguarionManager
, that contains all the strings that correlate to some boolean value, why don't you just retrieve the value and determine whether to execute the method or not just based on that? I'm hesitant to write and an answer as I'm feeling there's something I'm missing.â Denis
May 9 at 19:42
Well It's to "toggle" pieces of code on or off. The configuration manager stuff is just one implementation of IConfigParser. The idea of it is you inject your implementation of IConfigParser in to the feature toggle. This then allows you to execute a method or not. So instead of wrapping your toggled code in if statements adding further levels of indentation you call your method as a parameter
â ScottBot
May 9 at 20:04
Another approach would be the 'plugin' paradigm.
â Aybe
May 11 at 17:48