Toggling features of an application

The name of the pictureThe name of the pictureThe name of the pictureClash 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.







share|improve this question





















  • 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
















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.







share|improve this question





















  • 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












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.







share|improve this question













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.









share|improve this question












share|improve this question




share|improve this question








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 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
















  • 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















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










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();




enter image description here



enter image description here



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.






share|improve this answer





















  • 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










Your Answer




StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");

StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);








 

draft saved


draft discarded


















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






























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();




enter image description here



enter image description here



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.






share|improve this answer





















  • 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














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();




enter image description here



enter image description here



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.






share|improve this answer





















  • 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












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();




enter image description here



enter image description here



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.






share|improve this answer













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();




enter image description here



enter image description here



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.







share|improve this answer













share|improve this answer



share|improve this answer











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
















  • 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












 

draft saved


draft discarded


























 


draft saved


draft discarded














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













































































Popular posts from this blog

Chat program with C++ and SFML

Function to Return a JSON Like Objects Using VBA Collections and Arrays

Will my employers contract hold up in court?