Question

I'm developing a DLL that is used to control a POS (Point of sale) terminal via LAN.

The DLL provides commands to perform operations such as:

  • Logon
  • Logoff
  • Authorisation
  • Read card data
  • Cancellation
  • Refund
  • Network diagnosis

Also the DLL provides Connect() and Disconnect() functions.

As the POS terminal can be in various states, I'm thinking the State pattern might be used in the DLL.

Pseudo code:

// My DLL
class Pos
{
public:
    Pos();
    ~Pos();

    bool Connect();
    bool Disconnect();

    bool DoLogon() { m_pCurrentPosState->DoLogon(this); }
    bool DoLogoff() { m_pCurrentPosState->DoLogoff(this); }
    bool DoAuthorisation() { m_pCurrentPosState->DoAuthorisation(this); }
    bool DoReadCardData() { m_pCurrentPosState->DoReadCardData(this); }
    bool DoCancellation() { m_pCurrentPosState->DoCancellation(this); }
    bool DoRefund() { m_pCurrentPosState->DoRefund(this); }
    bool DoNetworkDiagnosis() { m_pCurrentPosState->DoNetworkDiagnosis(this); }
    ...

private:
    void ChangeState(PosState *pPosState) { m_pCurrentPosState = pPosState; }

private:
    friend class PosState;
    PosState *m_pCurrentPosState;
    ...
};

class PosState
{
public:
    // Implement default behavior for all command requests.
    virtual bool DoLogon(Pos *pPos) {}
    virtual bool DoLogoff(Pos *pPos) {}
    virtual bool DoAuthorisation(Pos *pPos) {}
    virtual bool DoReadCardData(Pos *pPos) {}
    virtual bool DoCancellation(Pos *pPos) {}
    virtual bool DoRefund(Pos *pPos) {}
    virtual bool DoNetworkDiagnosis(Pos *pPos) {}

protected:
    void ChangeState(Pos *pPos, PosState *pPosState);
};

class PosLoggedon : public PosState
{
public:
    static PosState* Instance();

    // Implement state-specific behavior.
    bool DoLogoff(Pos *pPos);
    bool DoAuthorisation(Pos *pPos);
    bool DoReadCardData(Pos *pPos);
    bool DoCancellation(Pos *pPos);
    bool DoRefund(Pos *pPos);
    bool DoNetworkDiagnosis(Pos *pPos);
};

class PosLoggedoff : public PosState
{
public:
    static PosState* Instance();

    // Implement state-specific behavior.
    bool DoLogon(Pos *pPos);
    bool DoAuthorisation(Pos *pPos);
    bool DoReadCardData(Pos *pPos);
    bool DoCancellation(Pos *pPos);
    bool DoRefund(Pos *pPos);
    bool DoNetworkDiagnosis(Pos *pPos);
};

Both PosLoggedon and PosLoggedoff are two states I could think of as valid but I'm not sure how to determine others.

Does it make sense to create states like PosAuthorisation, PosReadCardData etc so that they correspond to the POS functions? Probably it doesn't make sense...

I'm confused as to how to use the State pattern correctly because I'm mixing "current command in progress" and "current POS state".

Perhaps I need a state like PosCommandInProgress?

Any advice would be appreciated.

Many thanks.

Était-ce utile?

La solution

A state is usually something described with a verb. For example, parsing sounds like a state. UserLogged does not sound as it is a state, it sounds more like an event that can change the state.

If you want a state machine, you need to determine which actions involve different states, and which don't. For example, there can be a state waiting for input. The machine turns into this state each time the user need to do something. There is probably no need to make a lot of states like waiting for address input, waiting for name input, and so on, because they all are actually waiting for input state.

Now, in each state there are two things. First is a list of things that the state machine can do while in the state. For example, if it is waiting for the user to input his name, while being in state waiting for input, it can show pictures of cute kittens, but it definitely can not perform operations with user bank account.

The second thing is the list of states to which the machine can change during the current one. For example, from the state waiting for input it can change to showing the greeting message.

So, do not mix states with commands. Commands can be executed in states, and commands can cause the machine to change it's state, but they are not the same.

If your DLL can

Logon
Logoff
Authorisation
Read card data
Cancellation
Refund
Network diagnosis

then you will probably have states like connecting, waiting for authorization information input, authorizing, waiting for account operation choice, executing account operation, logging off.

So in connecting you will probably need to run network diagnosis to check if it is possible to conect, then establish connection, get the server response, display some UI to request user data, and switch to waiting for authorization information state, then do something else, and so on.

Hope that helps.

Autres conseils

Usually it is good to implement real world paradigm reflections in the code. States are very appropriate, if you:

  • can name them
  • know limitations
  • know what states it can transit to

Majority of hardware modules are implemented as state machines. State pattern is easily understandable, so you are unlikely to have maintenance problems

I think you will especially like reimplementing inapplicable virtual members to throw errors without if (state == XX) ... else if (state == YY) ... :)

Some not-a-code for example:

interface AbstractBeing
{
    void sleep();
    void eat();
    void lieDownAndDie();
};


class ImAlive implements AbstractBeing
{
    void sleep() {doZzzzzzz();}
    void eat() {nomNomNomNom();}
    void lieDownAndDie() {turnUndead();}
};

class ImUndead implements AbstractBeing
{
    void sleep() {throw error("zombies aint need no sleep");}
    void eat() {seekBrainzzzz();}
    void lieDownAndDie() {throw error("no you!!");}
};

Hope, this helps

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top