The term strategy may be too lofty for the concept. It's really more like a recipe. The goal is to make a meal, but if you substitute the Fettuccini Alfredo recipe with Chicken Cordon Bleu, the result will be quite different.
There are several ways of replacing strategies. Most common implementations are based on dependency injection. You can pass a strategy object to its consumer, perhaps in its constructor.
But an equally valid implementation is to use generics. Add a constraint that the type placeholder implements an interface. Because the consumer of the strategy needs to create an instance, you also need the default constructor constraint. Provide a concrete strategy type when you want to call the consumer.
Public Interface ILockingStrategy
Sub Lock()
Sub Unlock()
End Interface
Public Class MyLockableObject _
(Of LockingStrategy As _
{ILockingStrategy, New})
Private _lockingStrategy As _
New LockingStrategy
Public Sub BeginUse()
_lockingStrategy.Lock()
End Sub
Public Sub EndUse()
_lockingStrategy.Unlock()
End Sub
End Class
public interface ILockingStrategy
{
void Lock();
void Unlock();
}
public class MyLockableObject
<LockingStrategy>
where LockingStrategy :
ILockingStrategy, new()
{
private LockingStrategy _lockingStrategy
= new LockingStrategy();
public void BeginUse()
{
_lockingStrategy.Lock();
}
public void EndUse()
{
_lockingStrategy.Unlock();
}
}
The advantage of generics for this pattern is that the code is declarative. Instead of procedurally creating a strategy and handing it off, you declare that the consumer uses a specific strategy. This makes the code match the intent a little more closely.
The disadvantage of a generic implementation is that strategies cannot be chosen at run time. Generics are a compile-time construct, so they must be completely satisfied before the program runs. If you need to change strategies on-the-fly, use a more traditional dependency-injection approach.