Home > Enterprise >  C# State-Pattern private ChangeState
C# State-Pattern private ChangeState

Time:08-12

I´m trying to implement the State-Pattern using C# following enter image description here

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();
        }
    }
}
  • Related