Allow an object to change its behavior when its internal state changes.
The Context class, which is the primary interface for clients, delegates state-specific requests to the current Concrete State object. The Context object can pass itself to the Concrete State object if it needs to access the Context object.
Note that either the context or the state object can decide which state succeeds another.
In Microsoft Transaction Server (MTS/COM+) roles are used to logically group users in order to determine which users can access components and/or interface methods. The following example uses the State pattern to change the dynamic behavior based on the user's role. Users in the role of Employee can invoke OpenAccount() and TrnasferMoney(). Users in the role of Manager can invoke all operations. The State pattern changes the dynamic behavior by throwing an AccessDenied exception when an Employee attempts to invoke methods for which he/she has no access.
/* Exception class */
class AccessDenied : public std::exception
{
private:
const std::string strWhat;
public:
AccessDenied( const std::string& str = "") : strWhat(str) {}
const char *what() const throw()
{
return strWhat.c_str();
}
};
/* State Classes */
/* State: Defines an interface for encapsulating state-dependent behavior. In this example,
MTSRole declares an interface for all role-based methods. Assume the MTS application contains
two security roles, Teller and Manager */
class MTSRole
{
// Constructors/Destructor
public:
virtual ~MTSRole(){}
// Public Interface
public:
virtual void OpenAccount() = 0;
// Manager or Teller
virtual void CloseAccount() = 0;
// Manager
virtual void TransferMoney() = 0;
// Manager or Teller
virtual void IncreaseCredit() = 0;
// Manager
virtual void SubmitLoanApplication() = 0; // Manager
};
/* ConcreteState subclasses: Each subclass (RoleEmployee, RoleManager) implements state-specific behavior. A
user in the role of Employee can only invoke OpenAccount and TransferMoney. A
user in the role of Manager can invoke any operation */
class RoleEmployee : public MTSRole
{
// Public Interface
public:
virtual void OpenAccount() // Manager or Teller
{
std::cout << "RoleEmployee::OpenAccount()" << std::endl;
// ...
}
virtual void TransferMoney()
{
std::cout << "RoleEmployee::TransferMoney()" << std::endl;
// ...
}
virtual void CloseAccount()
{
throw AccessDenied("CloseAccount can only be called by a Manager");
}
virtual void IncreaseCredit()
{
throw AccessDenied("IncreaseCredit can only be called by a Manager");
}
virtual void SubmitLoanApplication()
{
throw AccessDenied("SubmitLoanApplication can only be called by a Manager");
}
};
/* A user in the role of Manager can invoke all role-based operations */
class RoleManager : public MTSRole
{
// Public Interface
public:
virtual void OpenAccount()
{
std::cout << "RoleManager::OpenAccount()" << std::endl;
// ...
}
virtual void CloseAccount()
{
std::cout << "RoleManager::CloseAccount()" << std::endl;
// ...
}
virtual void TransferMoney()
{
std::cout << "RoleManager::TransferMoney()" << std::endl;
// ...
}
virtual void IncreaseCredit()
{
std::cout << "RoleManager::IncreaseCredit()" << std::endl;
// ...
}
virtual void SubmitLoanApplication()
{
std::cout << "RoleManager::SubmitLoanApplication()" << std::endl;
// ...
}
};
class RoleController
{
// Constructors/Destructor
public:
// On construction, initial state is Employee
RoleController() : m_spState(new RoleEmployee) {}
~RoleController(){}
// Public Interface
public:
void OpenAccount()
{ m_spState->OpenAccount(); }
void CloseAccount()
{ m_spState->CloseAccount(); }
void TransferMoney()
{ m_spState->TransferMoney(); }
void IncreaseCredit()
{ m_spState->IncreaseCredit(); }
void SubmitLoanApplication() { m_spState->SubmitLoanApplication(); }
void ChangeState( MTSRole* pRole)
{
// Always validate incoming pointers ...
m_spState = std::auto_ptr<MTSRole>(pRole);
// assign new one (old is deleted by auto_ptr)
// We can potentially return old role if required ...
}
// Data members
private:
std::auto_ptr<MTSRole> m_spState;
};
/* Main */
enum Role { EMPLOYEE, MANAGER };
bool IsCallerInRole( Role eRole )
{
// Simulate IsCallerInRole MTS SDK method. Assume user is in Employee role
return (eRole == EMPLOYEE);
}
void f();
void g();
int main( int argc, char* argv[] )
{
// Invoke operations assuming user is
in EMPLOYEE role
f();
// Invoke operations assuming user is
in MANAGER role
g();
return 0;
}
void f()
{
try
{
/* Assume an MTS (COM+) object defines obRC as a data member. */
RoleController obRC;
// default state is Employee
/* Assume the following code is inside an MTS object method. The MTS object is
configured with Role security. Use IsCallerInRole MTS SDK method to determine
in what role did a user invoke the COM method */
if ( IsCallerInRole( EMPLOYEE ) )
{
obRC.OpenAccount();
// OK
obRC.TransferMoney();
// OK
obRC.IncreaseCredit();
// Access denied!
}
} // try
catch ( const AccessDenied& e)
{
std::cout << e.what() << std::endl;
}
catch ( const std::exception &e)
{
std::cout << e.what() << std::endl;
}
}
void g()
{
try
{
/* Assume an MTS (COM+) object defines obRC as a data member. */
RoleController obRC;
// default state is Employee
obRC.ChangeState( new RoleManager );
/* Assume the following code is inside an MTS object method. The MTS object is
configured with Role security. Use IsCallerInRole MTS SDK method to determine
in what role did a user invoke the COM method */
if ( !IsCallerInRole( MANAGER ) )
{
obRC.OpenAccount();
// OK
obRC.TransferMoney();
// OK
obRC.IncreaseCredit();
// OK
}
} // try
catch ( const AccessDenied& e)
{
std::cout << e.what() << std::endl;
}
catch ( const std::exception &e)
{
std::cout << e.what() << std::endl;
}
}