Design pattern to remove switch case - c#

I have a requirement to verify whether the postal code for a particular country is mandatory or not based on the countryid supplied. Currently I'm doing this with a switch statement but this code breaks the Open/Closed SOLID principle. I would like to know how to get rid of the switch in this scenario.
public class PostCodeVerifyMandatory : IPostCodeVerifyMandatory {
public bool IsPostCodeRequired(int countryId, string region)
{
switch (countryId) {
case 1: //UK
case 12: //Australia
case 29: //Brazil
case 31: //Brunei
case 37: //Canada
case 56: //Denmark
case 105: //Japan
case 110: //South Korea
case 114: //Latvia
case 136: //Moldova
case 137: //Monaco
case 145: //Netherlands
case 165: //Poland
case 166: //Portugal
case 183: //Slovak Republic (Slovakia)
case 189: //Spain
case 196: //Sweden
case 197: //Switzerland
case 199: //Taiwan Region
case 213: //Ukraine
case 215: //USA
case 221: //Vietnam
return true;
case 232: //Ireland
return region == "Dublin";
default:
return false;
}
}
}

Your switch statement effectively maps integers to booleans, with a default value of false.
So in this case, I would simply create a Dictionary<int,bool> with the appropriate values. Since the values are pretty much fixed, you could initialize it in the declaration:
Dictionary<int, bool> dict = new Dictionary<int, bool>() {
{ 1 /* UK */, true },
{ 12 /* Australia */, false }
...etc...
};
As #Nick points out, the case for Ireland means you'll still need some extra logic, so you'll want the Dictionary to be private, and the answer accessible via your IsPostCodeRequired(int,strnig) method.
EDIT:
It would probably be best to get these values from a database, as #JustinHarvey points out.
If you want to be very strict about the Open/Closed Principle, you could use the Strategy design pattern - you would create a separate ConcreteStrategy object for every country. If a new country were added, you would create a new ConcreteStrategy object for that country. That way you can add the logic for countries with special rules, without touching your original code.
However, the number of countries with special rules is probably very small, so unless you really cannot change the code once it's in production, this is over-engineering.

Maybe something like this:
private Dictionary<int, string> _dict;
protected Dictionary<int, string> CountryDictionary
{
get
{
if (_dict == null)
{
_dict = new Dictionary<int, string>();
_dict.Add(1, "UK");
_dict.Add(12, "Australia");
// and so on
}
return _dict;
}
}
public class PostCodeVerifyMandatory : IPostCodeVerifyMandatory
{
public bool IsPostCodeRequired(int countryId, string region)
{
return CountryDictionary.ContainsKey(countryId);
}
}

I'd probably follow that piece of advice from the c2 Wiki page "Switch statements smell" :
Using a database or TableOrientedProgramming is sometimes the
appropriate "fix", not polymorphism. For example, store product
classications are best handled in a database with many-to-many
category tables, not case statements.
You could have something like :
public class Country
{
public List<Region> Regions { get; set; }
public bool IsPostCodeRequiredByDefault { get; set; }
}
public class Region
{
private bool? _isPostCodeRequired;
public Country Country { get; set; }
public bool IsPostCodeRequired
{
get { return _isPostCodeRequired ?? Country.IsPostCodeRequiredByDefault; }
}
}
Which also has the benefit of adressing a secondary "primitive obsession" smell by making region a first-class domain concept instead of just a string.

Try this:
public class PostCodeVerifyMandatory : IPostCodeVerifyMandatory
{
public List<Func<int, string, bool>> Rules { get; private set; }
public PostCodeVerifyMandatory()
{
Rules = new List<Func<int, string, bool>>();
}
public bool IsPostCodeRequired(int countryId, string region)
{
if(Rules == null)
return false;
return (Rules.Any(r => r(countryId, region)));
}
}
You will have to load the ruleset, before using it:
var simpleCountries = new List<int>
{
1, // UK
12, // Australia
29, // Brazil
56, // Brunei
//..
//..
215, //USA
221 //Vietnam
};
var postCodeVerifier = new PostCodeVerifyMandatory();
// 1 rule for simple countries
postCodeVerifier.Rules.Add((id, region) => simpleCountries.Contains(id));
// Special rule for Ireland
postCodeVerifier.Rules.Add((id, region) => id == 232 && region.Equals("Dublin"));
var a = postCodeVerifier.IsPostCodeRequired(232, "Dublin");
or to make it fully data driven (using dictionary as example):
var countries = new Dictionary<int, string>
{
{ 1, null }, // UK
{ 12, null }, // Australia
{ 29, null }, // Brazil
{ 56, null }, // Brunei
//..
//..
{ 215, null }, //USA
{ 221, null }, //Vietnam
{ 232, "Dublin" } // Ireland
};
var postCodeVerifier = new PostCodeVerifyMandatory();
// 1 rule for all
postCodeVerifier.Rules.Add((id, region) =>
countries.ContainsKey(id) &&
(countries[id] ?? region) == region);

Related

Use LINQ to retrieve multiple scalar values

I am wondering if there is some clever way to retrieve data from an enumerable using LINQ when individual values from multiple records are needed.
For example, let's say you have a person with three different phone fields:
public class Person
{
public Phone HomePhone { get; set; }
public Phone WorkPhone { get; set; }
public Phone CellPhone { get; set; }
}
...but the phone list is stored in a normalized format:
public enum PhoneType
{
Home, Work, Cell
}
public class Phone
{
public PhoneType Type { get; set; }
public string Number { get; set; }
}
static public IEnumerable<Phone> GetPhoneList()
{
yield return new Phone { Type = PhoneType.Home, Number = "8005551212" };
yield return new Phone { Type = PhoneType.Work, Number = "8005551313" };
yield return new Phone { Type = PhoneType.Cell, Number = "8005551414" };
}
If you needed to populate Person, you could write a loop, and get everything you need in one pass:
public static Person GetPerson1()
{
var result = new Person();
foreach (var ph in GetPhoneList())
{
switch (ph.Type)
{
case PhoneType.Home: result.HomePhone = ph; break;
case PhoneType.Work: result.WorkPhone = ph; break;
case PhoneType.Cell: result.CellPhone = ph; break;
}
}
return result;
}
But if you wanted to use LINQ, it seems like three passes may be needed:
public static Person GetPerson2()
{
return new Person
{
HomePhone = GetPhoneList().Single( ph => ph.Type == PhoneType.Home ),
WorkPhone = GetPhoneList().Single( ph => ph.Type == PhoneType.Work ),
CellPhone = GetPhoneList().Single( ph => ph.Type == PhoneType.Cell )
};
}
Is there a clever way to use LINQ to get it all with only one pass over the enumeration?
Here is a link to a Fiddle if you'd like to explore my code.
(I am aware I could use a dictionary or other data structure to solve this particular problem; this is just an example.)
Normally, you can't do this in LINQ.
If you really want to, you can create a Foreach extension method and do the same as your GetPerson1 method.
public static class Ext
{
public static void Foreach<T>(this IEnumerable<T> e, Action<T> action)
{
foreach (T item in e)
{
action(item);
}
}
}
and then
public static Person GetPerson2()
{
var p = new Person();
var pl = GetPhoneList();
pl.Foreach(ph =>
{
switch (ph.Type)
{
case PhoneType.Home: p.HomePhone = ph; break;
case PhoneType.Work: p.WorkPhone = ph; break;
case PhoneType.Cell: p.CellPhone = ph; break;
}
});
return p;
}
But you really shouldn't. LINQ is meant to operate on IEnumerables (item by item), and LINQ functions should be without side effects, while your foreach loop and Foreach extension methods are only creating side effects, changing the state of the Person object.
And, besides, the fact that you need a 'clever way' should be an indication that this is not the way it's meant to be used :)
There's a great article by Eric Lippert with more details here: https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/
If there is no guarantee that numbers from the same person come in a sequence then you have to enumerate the list until you find all the numbers. It does not seem to me this is a good candidate for LINQ, whose purpose is to make the code more readable. Your foreach is just fine, and I would just break the loop when all numbers are found.
If you want to enumerate all the persons, and not just one then Dictionary approach is probably most effective. GroupBy internally uses a dictionary and you can use GroupBy to collect all the numbers belonging to a person, and then Aggregate to make a Person out of them. Let's assume there is some property Phone.PersonID, and also Person.PersonID, then you would have something like this:
GetPhoneList()
.GroupBy(x => x.PersonID)
.Select(x => x.Aggregate(new Person() { PersonID = x.Key },
(person, phone) =>
{
switch (phone.Type)
{
case PhoneType.Home: person.HomePhone = phone; break;
case PhoneType.Work: person.WorkPhone = phone; break;
case PhoneType.Cell: person.CellPhone = phone; break;
}
return person;
}));
I assume here that GetPhoneList() returns all the phones of all persons.

How to associate list with each enum item in c#

I have website to perform match action. I am writing code to restrict some of the action based on current match stage. For example, If first half is current stage then I should restrict direct end match without second half. For each stage, I have multiple restrict stages. Below is my stage enum.
public enum MatchStage
{
FirstHalfAssumedStartTime = 1,
FirstHalfAssumedEndTime = 2,
SecondHalfAssumedStartTime = 3,
SecondHalfAssumedEndTime = 4,
ExtraFirstHalfAssumedStartTime = 5,
ExtraFirstHalfAssumedEndTime = 6,
ExtraSecondHalfAssumedStartTime = 7,
ExtraSecondHalfAssumedEndTime = 8,
PauseStartTime = 9,
PauseEndTime = 10,
EndMatchTime = 11
}
I would like to make below method work. I want to call something like currentSiteCoreStage.RestrictedStages which returns List of restricted stages. How can I achieve this?
public static bool IsProposedStageValid(MatchStage currentSiteCoreStage, MatchStage proposedStage )
{
if (currentSiteCoreStage.RestrictedStages.Contains(proposedStage)) // I am hoping to make this work
return false;
}
What you have here is actually state machine. You can create method which defines possible stage transitions for each stage supported by your application. E.g. (of course you don't have to use iterators, you can simply return array of stages from each case block):
public static IEnumerable<MatchStage> GetAvailableStageTransitions(MatchStage stage)
{
switch (stage)
{
case MatchStage.FirstHalfAssumedStartTime:
yield return MatchStage.FirstHalfAssumedEndTime;
yield return MatchStage.PauseStartTime;
yield break;
case MatchStage.SecondHalfAssumedStartTime:
yield return MatchStage.SecondHalfAssumedEndTime;
yield return MatchStage.PauseStartTime;
yield break;
// etc
default:
throw new NotSupportedException($"Stage {stage} is not supported.");
}
}
Then checking if proposed stage is available:
GetAvailableStageTransitions(currentSiteCoreStage).Contains(proposedStage)
Depending on complexity of your system you can think about incapsulating state transions and logic realted to each state into state objects. For more details read about State Design Pattern.
Maybe consider something like this:
1: Your enum
2:
public class Stage
{
public MatchStage MathStage { get; set; }
public List<MatchStage> PossibleStages { get; set; }
}
3.
public static bool IsProposedStageValid(Stage currentSiteCoreStage, MatchStage proposedStage)
{
if (currentSiteCoreStage.PossibleStages.Contains(proposedStage)) // I am hoping to make this work
return false;
return true;
}
Example:
Stage st1 = new Stage
{
MathStage = MatchStage.FirstHalfAssumedStartTime,
PossibleStages = new List<MatchStage> { MatchStage.FirstHalfAssumedEndTime }
};
bool res = IsProposedStageValid(st1, MatchStage.PauseEndTime);
One approach is to have a class that knows about your current stage and can generate RestrictedStages based on the current stage. Something like:
public enum Stage
{
First, Second, Third
}
public class CoreStage
{
public Stage CurrentStage { get; private set; }
public IEnumerable<Stage> RestrictedStages { get; private set; }
public CoreStage(Stage currentStage)
{
CurrentStage = currentStage;
RestrictedStages = GenerateRestrictedStages();
}
private IEnumerable<Stage> GenerateRestrictedStages()
{
//apply your logic to determine Restricted stages based on current stage.
return null;
}
}
Now in your IsProposedStageValid method would look sth like below:
public static bool IsProposedStageValid(Stage currentSiteCoreStage, Stage proposedStage)
{
var coreStage = new CoreStage(currentSiteCoreStage);
if (coreStage.RestrictedStages.Contains(proposedStage))
{
//do whatever you want.
}
return false;
}

Enum with multiple descriptions

I have my enum like this.
public enum Gender
{
Man = 1,
Woman = 2
}
And I use ASP MVC4 to display the choices in a drop down like this.
#Html.DropDownListFor(model => model.Gender, new SelectList(Enum.GetValues(typeof(Namespace.Models.Enum.Gender))))
This works like a charm, it display Man/Woman in the drop down.
My problem is that I would like to show different names on those enums in different contexts.
Like one context would be if you are a Mom or a Dad. I would like to use the gender enum as base, but display Mom/Dad instad of Man/Woman.
Another context would be Boy/Girl, I still would like to use the gender enum, but display a different text.
Is this possible in any way?
EDIT
I used Kevin's solution and also added another extention method like this.
public static List<KeyValuePair<string, int>> GetValues(IGenderStrategy genderStrategy)
{
Dictionary<string, int> arr = new Dictionary<string, int>();
foreach (Gender g in System.Enum.GetValues(typeof(Gender)))
arr.Add(g.ToValue(genderStrategy), (int)g);
return arr.ToList();
}
Which I used like this in my view.
#Html.DropDownListFor(model => model.Gender, new SelectList(Chores.Models.Enum.EnumExtentions.GetValues(new Chores.Models.Enum.ParentStrategy()), "value", "key"))
I like #RakotVT answer of using an extension method but would extend it a bit further as you would need a new extension method for every situation which is not great.
I think a variation of the Strategy pattern might work better here (http://www.dofactory.com/Patterns/PatternStrategy.aspx)
Something like this -
public enum Gender
{
Man = 1,
Woman = 2
}
public interface IGenderStrategy
{
string DisplayName(Gender gender);
}
public class ParentStrategy : IGenderStrategy
{
public string DisplayName(Gender gender)
{
string retVal = String.Empty;
switch (gender)
{
case Gender.Man:
retVal = "Dad";
break;
case Gender.Woman:
retVal = "Mom";
break;
default:
throw new Exception("Gender not found");
}
return retVal;
}
}
public static class EnumExtentions
{
public static string ToValue(this Gender e, IGenderStrategy genderStategy)
{
return genderStategy.DisplayName(e);
}
}
public class Test
{
public Test()
{
Gender.Man.ToValue(new ParentStrategy());
}
}
Try to add Extentions class for your Enum. Here is an example of this class.
public static class EnumExtentions
{
public static string ToChildValue(this Gender e)
{
string retVal = string.Empty;
switch (e)
{
case Gender.Man:
retVal = "Boy";
break;
case Gender.Woman:
retVal = "Girl";
break;
}
return retVal;
}
public static string ToParentValue(this Gender e)
{
string retVal = string.Empty;
switch (e)
{
case Gender.Man:
retVal = "Dad";
break;
case Gender.Woman:
retVal = "Mom";
break;
}
return retVal;
}
}
Dunno if this is the neatest way, but how about something like:
#functions{
IEnumerable<SelectListItem> GetGenderSelectList(GenderContext genderContext)
{
return Enum.GetValues(typeof(Namespace.Models.Enum.Gender)).ToList().ConvertAll(x => new SelectListItem(){Value= x.ToString(), Text= GetGenderDescription(x, genderContext)});
}
string GetGenderDescription(Gender gender, GenderContext genderContext)
{
switch (GenderContext)
{
case Children: return gender == Man? "Boy" : "Girl";
case Parents: return gender == Man? "Dad" : "Mom";
default: return gender.ToString();
}
}
}
#Html.DropDownListFor(model => model.Gender, GetGenderSelectList(model.GenderContext))
Here 'GenderContext' is another Enum.
obviously you don't need to have those functions in the page functions - Could just add the list of items to the ViewBag before even getting to the view.

C# get/set solution

To give some background I'm trying to solve the Project Euler Problem 54 involving poker hands. Though there's infinite approaches to this. What I would like to do is enumerate through a list of strings, for example:
{ "8C", "TS", "KC", "9H", "4S" };
I would like to "get" an instance of class card with properties value, and suit, for each respective string. I've not yet utilized get/set so maybe there is an obvious approach to this I'm missing.
Ultimately I would like to have a list of objects type Card, I don't mind building all the card's ahead of time, such that "2H" returns an instance of type Card where suit = Hearts, and value = 2, for example.
I know this code is wrong, but it should give an idea of what I'm trying to do. Any suggestions would be appreciated.
class Card
{
public string suit;
public int value;
public string cardname
{
get
{
if (cardname == "2H") Card TwoH = new Card();
TwoH.suit = "Hearts"
TwoH.value = 2;
return TwoH;
}
}
}
Why not make a constructor that fills suit and value based on a string parameter
public Card(string name)
{
switch(name)
{
case "2H":
this.suit = "Hearts";
this.value = 2;
break;
//...
}
}
This might not be the exact solution you seem to be asking for but if the values you'll be getting (eg 2H, 3C etc) are all 2 characters long, then you can try this:
public class Card
{
public string suit { get; set; }
public int value { get; set; }
public static Card GetCard(string cardName)
{
string tmpSuit;
int tmpValue;
char[] cardNameParts = cardName.ToCharArray();
switch(charNameParts[0])
{
case "A":
tmpValue = 1;
break;
case "2":
tmpValue = 2;
break;
...
}
switch(charNameParts[1])
{
case "H":
tmpSuit= "Hearts";
break;
case "C":
tmpSuit= "Clubs";
break;
...
}
return new Card() { suit = tmpSuit, value = tmpValue };
}
}
I would do it like that:
public class Card
{
public string Suit { get; set; }
public int Value { get; set; }
public static Card FromString(string s)
{
if (s == "2H") return new Card() { Suit = "Hearts", Value = 2 };
else if (s == "....")
...
else return null;
}
}
I have converted your suit and value field into properties and instead of some getter method which in your case wouldn't work I have added a static method.
You can use it like this Card card2H = Card.FromString("2H");
Maybe use two switch statements, first
switch (cardname[0])
{
...
}
then
switch (cardname[1])
{
...
}
Before that, check that cardname.Length == 2. In each switch, have a default section where you throw an exception in case the char value doesn't make sense.

How can I avoid entering strings for known keys in C#

I have the following code:
switch(first)
{
case 'A':
vm.Content = contentService.Get("0001000", vm.RowKey);
return View("Article", vm);
case 'F':
vm.Content = contentService.Get("0002000", vm.RowKey);
return View("FavoritesList", vm);
}
'A' refers to a page type of Article with a key of "0001000"
'F' refers to a page type of Favorite with a key of "0002000"
Is there a way in C# that I could avoid having to code in the keys as a string?
Some way that would allow me to code in by the key abbreviation or name
and then have C# convert this to a string?
Can I use Enum for this? This seems ideal but I am not sure how to set up an Enum.
You may think about dictionary (if I right understood your question)
//class for holding relation between the code and page name
public class Data
{
public string Code {get;set;}
public string PageName {get;set;}
}
var dic = new Dictionary<string, Data >{
{"A", new Data{Code="0001000", PageName = "Article"},
{"F", newe Data{Code="0002000", PageName="FavoritesList"}
}
and after use it like:
Data dt = null;
if(dic.TryGetValue(first, out dt)) { // *first* is parameter you use in switch
vm.Content = contentService.Get(dt.Code, vm.RowKey);
return View(dt.PageName, vm);
}
You can use enums and use extension methods to allow an alternative text output.
The enum:
public enum PageTypes
{
A,
F
}
The extension method (needs to be in the top level of your project):
public static class Extensions
{
public static string getText(this PageTypes type)
{
switch (type)
{
case PageTypes.A:
return "0001000";
case PageTypes.F:
return "0002000";
default:
return null;
}
}
}
And your code:
PageTypes type;
//assing value to type:
//type = ...
var vm.Content = contentService.Get(type.getText(), vm.RowKey);
switch (type)
{
case PageTypes.A:
return View("Article", vm);
case PageTypes.F:
return View("FavoritesList", vm);
}
Now you do not need to use strings to retrieve the values.
I would put the keys in a dictionary, e.g.
var keyData = new Dictionary(char,string);
keyData.Add('A',"0001000");
keyData.Add('A',"0001000");
keyData.Add('F',"0002000");
You could then reference them using
var value = keyData['A'];

Categories

Resources