2
\$\begingroup\$

I have implemented some sort of the strategy pattern using an abstract class and an interface:

public interface ICombination
{
    void Check(int[] hand, UsersProperties user);
    void UpdateHand(UsersProperties user, int combinationType, int powerType);
}

public abstract class ICombinationAnalyzer : CardVariables, ICombination
{
    public abstract void Check(int[] hand, UsersProperties user);

    public void UpdateHand(UsersProperties user, int combinationType, int powerType)
    {
        user.Type = combinationType;
        user.Power = powerType + user.Type*100;
        MainPoker.Win.Add(new Hand {Power = user.Power, Current = user.Type});
    }

    protected ICombinationAnalyzer(UsersProperties user, IReadOnlyList<int> hand) : base(user, hand)
    {

    }
}

CardVariables is just a class where I keep some variables used in the calculates down below. I don't think there's point in showing it here.

There are a few derived methods I'm not going to show entirely here because they are quite similar to one another.

public class RoyalFlush : ICombinationAnalyzer
{
    public RoyalFlush(UsersProperties user, IReadOnlyList<int> hand) : base(user, hand)
    {
    }

    public override void Check(int[] hand, UsersProperties user)
    {
        const int localType = (int) Hand.Combinations.RoyalFlush;
        foreach (
            var t in
                CombinedArrays.Where(t => t.Length >= 5)
                    .Where(
                        t =>
                            t[0] == (int) Cards.CardTypes.Ace && t.Contains((int) Cards.CardTypes.Ten) &&
                            t.Contains((int) Cards.CardTypes.Jack) && t.Contains((int) Cards.CardTypes.Queen) &&
                            t.Contains((int) Cards.CardTypes.King)))
        {
            UpdateHand(user, localType, t.Max()/4);
        }
    }
}

And here's one more:

public class Straight : ICombinationAnalyzer
{
    public Straight(UsersProperties user, IReadOnlyList<int> hand) : base(user, hand)
    {
    }

    public override void Check(int[] hand, UsersProperties user)
    {
        const int localType = (int) Hand.Combinations.Straight;
        int[] op = Straight.Select(o => o/4).Distinct().ToArray();
        for (int j = 0; j < op.Length - 4; j++)
        {
            if (op[j] + 1 == op[j + 1] && op[j] + 2 == op[j + 2] && op[j] + 3 == op[j + 3] && op[j] + 4 == op[j + 4])
            {
                UpdateHand(user, localType, op.Max() - 4 == op[j] ? op.Max() : op[j + 4]);
            }
            if (op[j] != (int) Cards.CardTypes.Ace || op[j + 1] != (int) Cards.CardTypes.Ten ||
                op[j + 2] != (int) Cards.CardTypes.Jack || op[j + 3] != (int) Cards.CardTypes.Queen ||
                op[j + 4] != (int) Cards.CardTypes.King) continue;

            UpdateHand(user, localType, 13);
        }
    }
}

Now the problem is where I'm actually implementing the strategy pattern:

List<SwitchCombination> listCombinations = new List<SwitchCombination>();
                var switchCombination = new SwitchCombination( new HighCard(user, _reserve));
                listCombinations.Add(switchCombination);
                switchCombination = new SwitchCombination(new PairFromTable(user,_reserve));
                listCombinations.Add(switchCombination);
                switchCombination = new SwitchCombination(new PairInHand(user, _reserve));
                listCombinations.Add(switchCombination);
                switchCombination = new SwitchCombination(new PairHandTable(user, _reserve));
                listCombinations.Add(switchCombination);
                switchCombination = new SwitchCombination(new TwoPairFromTable(user, _reserve));
                listCombinations.Add(switchCombination);
                switchCombination = new SwitchCombination(new TwoPairHandPairTablePair(user, _reserve));
                listCombinations.Add(switchCombination);
                switchCombination = new SwitchCombination(new TwoPairHandTable(user, _reserve));
                listCombinations.Add(switchCombination);
                switchCombination = new SwitchCombination(new TwoPairTwoDifferent(user, _reserve));
                listCombinations.Add(switchCombination);
                switchCombination = new SwitchCombination(new ThreeOfAKind(user, _reserve));
                listCombinations.Add(switchCombination);
                switchCombination = new SwitchCombination(new Combinations.Straight(user, _reserve));
                listCombinations.Add(switchCombination);
                switchCombination = new SwitchCombination(new Combinations.Flush(user, _reserve));
                listCombinations.Add(switchCombination);
                switchCombination = new SwitchCombination(new Combinations.FullHouse(user, _reserve));
                listCombinations.Add(switchCombination);
                switchCombination = new SwitchCombination(new Combinations.FourOfAKind(user, _reserve));
                listCombinations.Add(switchCombination);
                switchCombination = new SwitchCombination(new Combinations.StraightFlush(user, _reserve));
                listCombinations.Add(switchCombination);
                switchCombination = new SwitchCombination(new Combinations.RoyalFlush(user, _reserve));
                listCombinations.Add(switchCombination);
                foreach (var listCombination in listCombinations)
                {
                    listCombination.CheckCombination(user, _reserve);
                }

It's my first time ever doing this pattern and I'm not sure if it can be made any better but i know that if I'm writing something similar a couple times there's probably a better way of doing it. I created list of those Combinations so i can iterate through them this way I avoid typing switchCombinations.CheckCombination(user, _reserve); all the time.

However, I can't seem to find a way to reduce the overall size of that. As I said earlier, it's my first time implementing this pattern and I'm not sure if it's even effective or if that's the right way to go.

EDIT : Added the SwitchCombination class :

    internal class SwitchCombination
{
    private readonly ICombinationAnalyzer _combinationAnalyzer;

    public SwitchCombination(ICombinationAnalyzer combination)
    {
        _combinationAnalyzer = combination;
    }

    public void CheckCombination(UsersProperties inputUser, int[] inputHand)
    {
        _combinationAnalyzer.Check(inputHand, inputUser);
    }
}
\$\endgroup\$
4
\$\begingroup\$

You have an public abstract class called ICombinationAnalyzer: that's wrong, only interfaces can use the I prefix. Instead I'd expect this class to have Base in its name at the start or the end (whatever is your preference).


This seems odd: user.Power = powerType + user.Type*100; To me it would make more sense for the user to have a powerType field, and for Power to simply be a get which returns powerType + Type*100;.

I'm confused by all of UpdateHand(UsersProperties user, int combinationType, int powerType), to be honest: this method updates two properties of its parameter user and then calls MainPoker.Win.Add(new Hand {Power = user.Power, Current = user.Type}); which doesn't look like it updates a Hand, but instead adds a new one.

Why are combinationType and powerType ints? Their names suggest they should be enums, but then you use them in a calculation...

This whole method throws up all kinds of warning signs to me and I can't shake the feeling you have some fundamental flaws in the design of your solution.


The RoyalFlush also raises some questions: Hand.Combinations.RoyalFlush is cast to an int -- so why isn't it one in the first place? Same with Cards.CardTypes.Ace and others in that namespace.

Why the need for Hand.Combinations. and Cards.CardTypes. in the first place? It seems odd to use subclasses for such fundamental values.

This whole block is just a mess:

t[0] == (int) Cards.CardTypes.Ace 
&& t.Contains((int) Cards.CardTypes.Ten) 
&& t.Contains((int) Cards.CardTypes.Jack) 
&& t.Contains((int) Cards.CardTypes.Queen) 
&& t.Contains((int) Cards.CardTypes.King)))

Even formatted like this I feel there's too much going on: the casts to int don't help, plus there's the odd check that the first item is an Ace while you use .Contains() for the other types.


But Straight is even worse than RoyalFlush and just is incomprehensible. What is op? Why do you need to execute int[] op = Straight.Select(o => o/4).Distinct().ToArray(); each time -- it's not likely to change, now is it?

op[j] + 1 == op[j + 1] && op[j] + 2 == op[j + 2] && op[j] + 3 == op[j + 3] && op[j] + 4 == op[j + 4] suggests that op is sorted, but there's no evidence of this being the case.

This next bit looks a lot like a similar block in RoyalFlush above and I'm wondering whether they shouldn't be the same.

if (op[j] != (int) Cards.CardTypes.Ace 
    || op[j + 1] != (int) Cards.CardTypes.Ten 
    || op[j + 2] != (int) Cards.CardTypes.Jack 
    || op[j + 3] != (int) Cards.CardTypes.Queen 
    || op[j + 4] != (int) Cards.CardTypes.King) 
{
    continue;
}

The "problem" you identify seems trivial. You can easily reduce it to this:

var listCombinations = new List<SwitchCombination>
{
    new SwitchCombination(new HighCard(user, _reserve)),
    new SwitchCombination(new PairFromTable(user,_reserve)),
    new SwitchCombination(new PairInHand(user, _reserve)),
    new SwitchCombination(new PairHandTable(user, _reserve)),
    new SwitchCombination(new TwoPairFromTable(user, _reserve)),
    new SwitchCombination(new TwoPairHandPairTablePair(user, _reserve)),
    new SwitchCombination(new TwoPairHandTable(user, _reserve)),
    new SwitchCombination(new TwoPairTwoDifferent(user, _reserve)),
    new SwitchCombination(new ThreeOfAKind(user, _reserve)),
    new SwitchCombination(new Combinations.Straight(user, _reserve)),
    new SwitchCombination(new Combinations.Flush(user, _reserve)),
    new SwitchCombination(new Combinations.FullHouse(user, _reserve)),
    new SwitchCombination(new Combinations.FourOfAKind(user, _reserve)),
    new SwitchCombination(new Combinations.StraightFlush(user, _reserve)),
    new SwitchCombination(new Combinations.RoyalFlush(user, _reserve)),
}

I'd probably refactor that even more, but right now you only posted fragments of your code and it's too much work to fill in the gaps.


UPDATE: The SwitchCombination you posted is a completely superfluous wrapper class, if that's all of its code. var listCombinations = new List<SwitchCombination> should instead be something like var combinationAnalyzers = new List<BaseCombinationAnalyzer> and be filled with classes that implement BaseCombinationAnalyzer, e.g. new PairFromTable(user,_reserve) instead of new SwitchCombination(new PairFromTable(user,_reserve)).

\$\endgroup\$
  • \$\begingroup\$ Once i have the powerType declared as a field public int powerType; should i just change powerType instead of changing the Power property will it automatically update the Power property ? Yes UpdateHand(UsersProperties user, int combinationType, int powerType) doesn't update's Hand it just adds one more to the Win which is a list, later on when the game is about to end i check every player if he has the highest Hand in Win, if he has it than he wins.I know there are problems with my naming .. combinationType indeed should be enum type im getting the value from enum anyway \$\endgroup\$ – Denis Mar 7 '16 at 18:44
  • \$\begingroup\$ "Hand.Combinations.RoyalFlush is cast to an int -- so why isn't it one in the first place?" Are you suggesting me to convert the Hand.Combinations.RoyalFlush enum to a static class ? The weird check has t[0] for no actual reason. If i switch it to contains too how can i improve it even more ? Straight.Select(o => o/4).Distinct().ToArray(); WILL change everytime a new user is checking his hand the Straight arrays is being changed a little bit. op[j] + 1 == op[j + 1] and so on how can i make this one better should i use some LINQ ? I really dont know it will look so if you can do it.. \$\endgroup\$ – Denis Mar 7 '16 at 18:45
  • \$\begingroup\$ "This next bit looks a lot like a similar block in RoyalFlush above and I'm wondering whether they shouldn't be the same." You are probably right about this. \$\endgroup\$ – Denis Mar 7 '16 at 18:45
  • \$\begingroup\$ Note that a field shouldn't be public, that would make it a property. WRT the rest of your comments: the code you posted is too fragmented to tell. I'd advise you to post a new question and post more complete code excerpts, perhaps including a link to a repository. \$\endgroup\$ – BCdotWEB Mar 7 '16 at 18:50
  • \$\begingroup\$ how should i declare it than ? private int powerType; public int PowerType { get { return powerType; } set { powerType = value; } } this looks like a property too \$\endgroup\$ – Denis Mar 7 '16 at 18:56
3
\$\begingroup\$

One note on naming: ICombinationAnalyzer is a class—albeit abstract—not an interface, so its name should not start with I.

This boilerplate initialization code can be made much shorter and easier to read if you dispense with that local variable, which is doing nothing for you.

This:

List<SwitchCombination> listCombinations = new List<SwitchCombination>();
var switchCombination = new SwitchCombination( new HighCard(user, _reserve));
listCombinations.Add(switchCombination);
switchCombination = new SwitchCombination(new PairFromTable(user,_reserve));
listCombinations.Add(switchCombination);

can become

List<SwitchCombination> listCombinations = new List<SwitchCombination>();
listCombinations.Add(new SwitchCombination(new HighCard(user, _reserve)));
listCombinations.Add(new SwitchCombination(new PairFromTable(user,_reserve));

Just as you say

I created list of those Combinations so i can iterate through them this way I avoid typing switchCombinations.CheckCombination(user, _reserve); all the time.

which is good thinking, you could also change to creating a list of CombinationAnalyzer objects and map over it to create the list of SwitchCombination objects that you need, rather than making all those new SwitchCombination(… constructor calls manually.

More generally, I wonder if you are really using Strategy. Without the code of SwitchCombination it is difficult to know. But the motivation of Strategy is to allow a choice at runtime of one possibility at a time out of several different implementations of an algorithm. If I understand what you are doing here seems more like the intention of the Chain of Command pattern.

The local variable localType in your implementations of the Check method isn't doing much for you, either. I suggest you inline it.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.