-
-
Notifications
You must be signed in to change notification settings - Fork 72
Description
Strategy Chains
Historically, each Unity Container had a chain of build strategies that determines resolution algorithm. To manage these chains Unity provided two interfaces:
- Interface
IStagedStrategyChain - Interface
IStrategyChain
Interface IStagedStrategyChain
Interface IStagedStrategyChain allowed creation and storage of a chain of strategies required to operate the container.
IStagedStrategyChain in Unity v4.0.1
Unity 4.0.1 defined this interface as follows:
/// <summary>
/// This interface defines a standard method to convert any <see cref="StagedStrategyChain{TStageEnum}"/> regardless
/// of the stage enum into a regular, flat strategy chain.
/// </summary>
public interface IStagedStrategyChain
{
/// <summary>
/// Convert this <see cref="StagedStrategyChain{TStageEnum}"/> into
/// a flat <see cref="IStrategyChain"/>.
/// </summary>
/// <returns>The flattened <see cref="IStrategyChain"/>.</returns>
IStrategyChain MakeStrategyChain();
}Management of the strategies was implemented in StagedStrategyChain<TStageEnum> class.
The container had two of these chains, one for strategies such as array resolution, lifetime management, or building types.
The other chain was used inside BuilderStrategy to build types.
As you can see above the main purpose of the interface was to MakeStrategyChain once ready or changed.
IStagedStrategyChain in Unity v5.x
Unity v5 expanded the interface to include a method and an event:
/// <summary>
/// This interface defines a standard method to create multi staged strategy chain.
/// </summary>
/// <typeparam name="TStrategyType">The <see cref="System.Type"/> of strategy</typeparam>
/// <typeparam name="TStageEnum">The stage enum</typeparam>
public interface IStagedStrategyChain<in TStrategyType, in TStageEnum>
{
/// <summary>
/// Adds a strategy to the chain at a particular stage.
/// </summary>
/// <param name="strategy">The strategy to add to the chain.</param>
/// <param name="stage">The stage to add the strategy.</param>
void Add(TStrategyType strategy, TStageEnum stage);
/// <summary>
/// Signals that chain has been changed
/// </summary>
event EventHandler<EventArgs> Invalidated;
}Interface IStrategyChain
Interface IStrategyChain allowed execution of created chain.
IStrategyChain in Unity v4.0.1
Unity 4.0.1 defined the interface as follows:
/// <summary>
/// Represents a chain of responsibility for builder strategies.
/// </summary>
public interface IStrategyChain : IEnumerable<IBuilderStrategy>
{
/// <summary>
/// Reverse the order of the strategy chain.
/// </summary>
/// <returns>The reversed strategy chain.</returns>
IStrategyChain Reverse();
/// <summary>
/// Execute this strategy chain against the given context,
/// calling the Buildup methods on the strategies.
/// </summary>
/// <param name="context">Context for the build process.</param>
/// <returns>The build up object</returns>
object ExecuteBuildUp(IBuilderContext context);
/// <summary>
/// Execute this strategy chain against the given context,
/// calling the TearDown methods on the strategies.
/// </summary>
/// <param name="context">Context for the teardown process.</param>
void ExecuteTearDown(IBuilderContext context);
}IStrategyChain in Unity v5.x
With advent of LINQ the interface became redundant and was deprecated in favor of simple ToArray().
Problem
With new features being implemented these interfaces do not provide adequate functionality and require either expansion or replacement.
Solution
Instead of creating custom solution and adding missing API to the interface the decision has been made to derive IStagedStrategyChain from IDictionary. The interface is defined as follows:
public interface IStagedStrategyChain : IDictionary<UnityBuildStage, BuilderStrategy>
{
/// <summary>
/// Signals that the chain has been changed
/// </summary>
event StagedChainChagedHandler Invalidated;
/// <summary>
/// Add a stage
/// </summary>
/// <param name="stage"><see cref="ValueTuple"/> pairs to add</param>
void Add((UnityBuildStage, BuilderStrategy) stage);
/// <summary>
/// Add a range of build stages
/// </summary>
/// <remarks>
/// This method adds stages and fires notifications only once
/// </remarks>
/// <param name="stages">Array of <see cref="ValueTuple"/> pairs to add</param>
void Add((UnityBuildStage, BuilderStrategy)[] stages);
}and change event handler is declared like this:
public delegate void StagedChainChagedHandler(object sender, Type type);Breaking Change
Hierarchy
Unity v4 and v5 allowed each container to have its own chain of strategies with child containers inheriting strategies from parent and adding their own as required.
This feature added significant overhead to overall performance and was replaced with simplified approach with only one strategy chain for all containers within child hierarchy.
In new implementation only root container has the chain of strategies and all child containers reuse the same chain.
To allow functionality similar to these available in previous version and custom strategy could check a Name of the container or the Level of the hierarchy to perform required initialization.
List vs Dictionary
The chain created in Unity v4 or v5 would be a list of strategies. The strategies could be added to the chain multiple times and the chain would accumulate them all.
Removing of added strategies was not possible with provided API.
Implementation of the chain as a dictionary places several restrictions on how strategies are managed:
-
Only one strategy is allowed per step
1.1 Adding the same step twice throws an exception
1.2 Setting the step with indexerchain[step] = strategyreplaces the strategy -
Number of steps is determined by the number of elements in
TStepenumerable, it can not be expanded. When expansion of existing step is required consider deriving from provided strategy and replacing it with custom solution.
See Also #219