I´m trying to implement the State-Pattern using C# following
The State it self sets the new sate in the context class. But here is my problem: I want to use Interfaces but the ChangeState-Method should not be present to the client but available for the state. But I don´t know how to implement a private interface within a class which I present for the client.
Here my first shot of the pattern Context-Class which should implement the IChangeState interface:
public interface IContext
{
string Input { get; set; }
void DoTransition();
}
public class Context : IContext
{
public string Input { get; set; }
private AbstractState _state;
public void DoTransition()
{
_state.DoTransition();
}
}
And the IChangeState interface which does the main trick but should not be visibile to the client. So I assumed to make it private but how to share this interface with the state or even implement it?
public interface IChangeState
{
void ChangeState(IState state);
}
And at least the States:
public interface IState
{
void DoTransition();
}
public abstract class AbstractState : IState
{
private IChangeState _stateChanger;
public AbstractState(IChangeState stateChanger) => _stateChanger = stateChanger;
public virtual void DoTransition()
{
_stateChanger.ChangeState(new NextState(_stateChanger));
}
}
public class NextState : AbstractState
{
public NextState(IChangeState stateChanger)
: base(stateChanger)
{ }
public override void DoTransition()
{
base.DoTransition();
}
}
CodePudding user response:
You can make the IChangeState internal if the client is in a different assembly. The solution should look as follow:
//Start Assembly MyAssembly1
public interface IContext
{
string Input { get; set; }
void DoTransition();
}
internal interface IChangeState
{
void ChangeState(IState state);
}
public class Context : IContext, IChangeState
{
public string Input { get; set; }
private IState _state;
public void DoTransition()
{
_state.DoTransition();
}
void IChangeState.ChangeState(IState state) {
_state = state;
}
}
public interface IState
{
void DoTransition();
}
internal abstract class AbstractState : IState
{
private IChangeState _stateChanger;
public AbstractState(IChangeState stateChanger) {
_stateChanger = stateChanger;
}
public virtual void DoTransition()
{
_stateChanger.ChangeState(new NextState(_stateChanger));
}
}
internal class NextState : AbstractState
{
public NextState(IChangeState stateChanger)
: base(stateChanger)
{ }
public override void DoTransition()
{
base.DoTransition();
}
}
// End Assembly MyAssembly1
// Start Assembly MyAssembly2
public class AClient {
private IContext _context;
public AClient(IContext context)
{
_context = context;
// ((Context)_context).ChangeState is not visible
}
}
// End Assembly MyAssembly2
In this way the AClient class cannot see the methods exposed by the interface IChangeState, even if you inject a Context object instead of the IContext interface
CodePudding user response:
I´ve solved the issue with partial classes. There are all States and there Interfaces private so the Context-Class has access to all the interfaces and classes but the client not. The IChangeState-Interface I´ve implemented in a seperate Class which is also private so only the Context can change the state. Example as followed:
public interface IContext
{
string Input { get; set; }
void DoTransition();
}
public partial class Context : IContext
{
public string Input { get; set; }
private IChangeState _stateChanger;
public void DoTransition()
{
_stateChanger.CurrentState.DoTransition();
}
}
public partial class Context
{
private interface IChangeState
{
IState CurrentState { get; }
void ChangeState(IState state);
}
}
public partial class Context
{
private class ChangeState
{
IState CurrentState { get; private set; }
void ChangeState(IState state)
{
CurrentState = state;
}
}
}
public partial class Context
{
private interface IState
{
void DoTransition();
}
}
public partial class Context
{
private abstract class AbstractState : IState
{
private IChangeState _stateChanger;
public AbstractState(IChangeState stateChanger) => _stateChanger = stateChanger;
public virtual void DoTransition()
{
_stateChanger.ChangeState(new NextState(_stateChanger));
}
}
}
public partial class Context
{
private class NextState : AbstractState
{
public NextState(IChangeState stateChanger)
: base(stateChanger)
{ }
public override void DoTransition()
{
base.DoTransition();
}
}
}