In this case, the important thing is that if you compare two objects, you get back a result.
So basically, you need to decouple this comparison in such a way that you can not only add a new type of object into the mix, but also add comparison rules into the mix that can handle this object.
Now, I also commented on your question saying that "Sometimes, questions can be too generic", and the problem here is that no matter how much I tell you how to do what you ask about, it won't help you one bit about the real problem.
Unless you are building a rock-paper-scissors-X game.
Personally I would do the following, with my own IoC container.
ServiceContainer.Global.RegisterFactory<IHandType>()
.FromDelegate(() => RandomRockScissorPaper());
ServiceContainer.Global.RegisterFactory<IHandComparison, DefaultHandComparison>();
(or rather, I would configure up the above in the app.config file or similar so that it could be changed after the project has been built).
Then, if a user/customer/end-point needs to override, to add another type, I would augment the registrations as follows (remember that the above are in app.config, so I would replace those with the ones below):
ServiceContainer.Global.RegisterFactory<IHandType>()
.FromDelegate(() => RandomRockScissorPaper())
.ForPolicy("original");
ServiceContainer.Global.RegisterFactory<IHandType>()
.FromDelegate((IHandType original) => RandomRockScissorPaperBlubb(original))
.WithParameters(
new Parameter<IHandType>("original").WithPolicy("original"))
.ForPolicy("new")
.AsDefaultPolicy();
Here I add a new way to resolve IHandType, by not only keeping the original way, but adding a new one as well. This new one will be given the result of calling the old one, and then would have to internally decide if a random chance should return the fourth type, or the original type (one of the three original ones).
I would then also override the original comparison rule:
ServiceContainer.Global.RegisterFactory<IHandComparison, DefaultHandComparison>()
.ForPolicy("original");
ServiceContainer.Global.RegisterFactory<IHandComparison, NewHandComparison>()
.ForPolicy("new")
.AsDefaultPolicy()
.WithParameters(
new Parameter<IHandType>("original"));
Here's how it would be used:
IHandType hand1 = ServiceContainer.Global.Resolve<IHandType>();
IHandType hand2 = ServiceContainer.Global.Resolve<IHandType>();
IHandComparison comparison = ServiceContainer.Global.Resolve<IHandComparison>();
if (comparison.Compare(hand1, hand2) < 0)
Console.Out.WriteLine("hand 1 wins");
else if (comparison.Compare(hand1, hand2) > 0)
Console.Out.WriteLine("hand 1 wins");
else
Console.Out.WriteLine("equal");
Here's how to implement:
public interface IHandComparison
{
Int32 Compare(IHandType hand1, IHandType hand2);
}
public class DefaultHandComparison : IHandComparison
{
public Int32 Compare(IHandType hand1, IHandType hand2)
{
... normal rules here
}
}
public class NewHandComparison : IHandComparison
{
private IHandComparison _Original;
public NewHandComparison(IHandComparison original)
{
_Original = original;
}
public Int32 Compare(IHandType hand1, IHandType hand2)
{
if hand1 is blubb or hand2 is blubb then ...
else
return _Original.Compare(hand1, hand2);
}
}
After writing all this I realize that the app.config configuration of mine won't be able to handle delegates, so a factory object would be needed, but the same still applies.
You need to both be able to resolve the way to obtain new hands, as well as to resolve the rules for what is the winning hands.