Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Eliminating coupling out of classes that have strong conceptual bonds with each other

I have the types Rock, Paper, and Scissors. These are components, or "hands" of the Rock, Paper, Scissors game. Given two players' hands the game must decide who wins. How do I solve the problem of storing this chain chart

Rock, Paper, Scissors chart

without coupling the various hands to each other? The goal is to allow adding a new hand to the game (Jon Skeet, for instance) without changing any of the others.

I am open to any idea of proxies, but not to large switch statements or duplication of code. For instance, introducing a new type that manages the chain's comparisons is fine as long as I don't need to change it for every new hand I add. Then again, if you can rationalize having a proxy that must change for every new hand or when a hand changes, that is welcome as well.

This is sort of a Design 101 problem, but I am curious what solutions people can come up with for this. Obviously, this problem can easily scale to much larger systems with many more components with any arbitrarily complex relationships among them. That's why I'm laying a very simple and concrete example to solve. Any paradigm used, OOP or otherwise, is welcome.

like image 553
wilhelmtell Avatar asked Dec 21 '08 00:12

wilhelmtell


2 Answers

Have a GameStrategy class that implements a Win method. The win method takes a list of Hands, and returns either a Hand -- if there is a winner -- or null if the game was a tie. I think the winning strategy is not really a property of the hand, but of the game. Incorporate the determination of a winner of a pair of hands into the GameStrategy class.

EDIT: potential strategy

public enum RPSEnum { Rock, Paper, Scissors }

private RPSEnum FirtRPS = RPSEnum.Rock;
private RPSEnum LastRPS = RPSEnum.Scissors;

public Hand Win( Hand firstPlayer, Hand secondPlayer )
{
    if (firstPlayer.Value == FirstRPS
        && secondPlayer.Value == LastRPS)
    {
       return firstPlayer;
    }
    else if (secondPlayer.Value == FirstRPS
             && firstPlayer.Value == LastRPS)
       return secondPlayer;
    }
    else
    {
       int compare = (int)firstPlayer.Value - (int)secondPlayer.Value;
       if (compare > 0)
       {
          return firstPlayer;
       }
       else if (compare < 0)
       {
          return secondPlayer;
       }
       else
       {
          return null;
       }       
    }
}

To add a new hand value, just add the value to RPSEnum in the proper sequence. If it is the new "lowest" hand, update FirstRPS. If it is the new "highest" hand, update LastRPS. You shouldn't need to change the actual algorithm at all.

NOTE: this is more complex than it needs to be for just three values, but the requirement was additional values be able to be added without updating much code.

like image 179
tvanfosson Avatar answered Oct 10 '22 16:10

tvanfosson


If they have sufficient conceptual similarity, you may not want to knock yourself out reducing the coupling.

"Coupling" is really just a metric of how much code breakage there will be if the internal implementation of one thing is changed. If the internal implementation of these things is inherently sensitive to the others, then it is; reducing coupling is fine, but the software, first of all, should reflect reality.

like image 40
Charlie Martin Avatar answered Oct 10 '22 17:10

Charlie Martin