问题
I'm implementing a finite-state machine, with each class representing a state. Each state knows which other states it can transition to, and this naturally leads to circular relationships. (See the State Design Pattern).
For this simplified example I'm creating two components, where the first component has a reference to the second component, and the second component has a reference to the first.
The problem is that the Windsor framework is correctly setting the references for the first created component, but not setting the references for the second:
Here are the two components:
// DefaultMouseHandler knows about NewLineMouseHandler
public class DefaultMouseHandler : MouseHandler
{
public DefaultMouseHandler()
{
}
public NewLineMouseHandler NewLineMouseHandler
{
get;
set;
}
internal override MouseHandler LeftButtonDown(System.Windows.Input.MouseButtonEventArgs e)
{
return this.NewLineMouseHandler;
}
}
// NewLineMouseHandler knows about DefaultMouseHandler
public class NewLineMouseHandler : MouseHandler
{
public NewLineMouseHandler()
{
}
public DefaultMouseHandler DefaultMouseHandler
{
get;
set;
}
internal override MouseHandler LeftButtonUp(System.Windows.Input.MouseButtonEventArgs e)
{
return this.DefaultMouseHandler;
}
}
I then register the components as so:
_windsorContainer.Register(Classes.FromThisAssembly()
.BasedOn<MouseHandler>()
);
But when I first attempt to create the DefaultMouseHandler
:
- The
DefaultMouseHandler
is constructed - The
NewLineMouseHandler
is constructed - The
NewLineMouseHandler
is set on theDefaultMouseHandler
But the DefaultMouseHandler
is NOT set on the NewLineMouseHandler
.
Can this be considered a defect in Castle Windsor?
What's the best way to have the two components referencing each other, without either component being aware of the Windsor container?
回答1:
I agree with @sll that it might be a sign the design could be improved. On the other hand there are valid reasons for doing what you're doing. If you never resolve NewLineMouseHandler
and it's only used as dependency of NewLineMouseHandler
, just set the dependency manually:
windsor.Register(Classes.FromThisAssembly()
.BasedOn<MouseHandler>()
.ConfigureFor<NewLineMouseHandler>(
h => h.OnCreate(
c =>
{
var @default = (DefaultMouseHandler) c;
@default.NewLineMouseHandler.DefaultMouseHandler = @default;
}))
);
回答2:
The solution I've come up with is to use the TypedFactoryFacility.
This allows objects to be created as they are needed, so we don't have the complications of them all being dependent upon one another at initialization time.
public interface IMouseHandlerFactory
{
T CreateHandler<T>() where T : MouseHandler;
}
// In the registration code
_windsorContainer.AddFacility<TypedFactoryFacility>();
_windsorContainer.Register(Classes.FromThisAssembly()
.BasedOn<MouseHandler>(),
Component.For<IMouseHandlerFactory>()
.AsFactory()
);
// The base class
public abstract class MouseHandler
{
// Every mousehandler will create at least one other mouse handler
// This is the factory that they will use for mouse handling creation
// The property will be automatically set by the DI container
public IMouseHandlerFactory MouseHandlerFactory { get; set; }
// Methods the concrete implementations will override
internal virtual MouseHandler LeftButtonDown(MouseButtonEventArgs e) { return this; }
internal virtual MouseHandler LeftButtonUp(MouseButtonEventArgs e) { return this; }
}
// The implementations
public class DefaultMouseHandler : MouseHandler
{
internal override MouseHandler LeftButtonDown(MouseButtonEventArgs e)
{
return this.MouseHandlerFactory.CreateHandler<NewLineMouseHandler>();
}
}
public class NewLineMouseHandler : MouseHandler
{
internal override MouseHandler LeftButtonUp(MouseButtonEventArgs e)
{
return this.MouseHandlerFactory.CreateHandler<DefaultMouseHandler>();
}
}
来源:https://stackoverflow.com/questions/9088534/create-components-with-circular-relationships-using-castle-windsor