Let's design a turnstile. A turnstile is a type of gate which allows one person to pass at a time. Consider a turnstile at a subway station. There are two panels that rotate to allow a single person passage. The turnstile has two states, Locked and Unlocked, and the customer can either Push the turnstile or Insert a token/card. Here is the state transition table:
Event | Current State | Action | Next State |
---|---|---|---|
Push | Locked | Locked | |
Token | Locked | Unlock | Unlocked |
Push | Unlocked | Pass through | Locked |
Token | Unlocked | (discarded) | Unlocked |
Requirements:
insert_token()
push()
Example:
turnstile = Turnstile()
print(turnstile.state) # Output: Locked
turnstile.push()
print(turnstile.state) # Output: Locked
turnstile.insert_token()
print(turnstile.state) # Output: Unlocked
turnstile.push()
print(turnstile.state) # Output: Locked
How would you approach this design and implementation, keeping in mind scalability, maintainability, and potential future extensions such as different types of tokens or remote unlocking?
Let's walk through the design and implementation of a turnstile system, considering scalability, maintainability, and future extensions.
insert_token()
and push()
methods.The turnstile system can be modeled using a state machine. It has two primary states: Locked
and Unlocked
. The transitions between these states are triggered by two events: insert_token()
and push()
.
Here's a Python implementation of the turnstile:
class Turnstile:
def __init__(self):
self.state = "Locked"
def insert_token(self):
if self.state == "Locked":
self.state = "Unlocked"
print("Token accepted. Turnstile unlocked.")
else:
print("Token discarded. Turnstile already unlocked.")
def push(self):
if self.state == "Locked":
print("Cannot pass. Turnstile is locked.")
else:
self.state = "Locked"
print("Passed through. Turnstile locked.")
# Example usage
turnstile = Turnstile()
print(f"Initial state: {turnstile.state}")
turnstile.push()
print(f"State after push: {turnstile.state}")
turnstile.insert_token()
print(f"State after token: {turnstile.state}")
turnstile.push()
print(f"State after push: {turnstile.state}")
For this simple implementation, we only need a single attribute: state
. In a more complex system, we might need to track token types, transaction history, etc.
Field | Type | Description |
---|---|---|
state | string | Current state (Locked/Unlocked) |
In a real-world scenario, the turnstile might be controlled via an API. Here's a simplified API design:
{
"token_type": "subway_card",
"token_id": "1234567890"
}
* Response:
{
"status": "success",
"message": "Turnstile unlocked"
}
{
"status": "success",
"message": "Passed through. Turnstile locked."
}
Aspect | Approach | Pros | Cons |
---|---|---|---|
State Machine | Simple string-based state representation | Easy to understand, quick to implement | Not scalable for complex state transitions, error-prone |
API | REST API with JSON payloads | Standard, easy to integrate with other systems | Adds overhead, might not be necessary for simple implementations |
Error Handling | Basic print statements | Simple for demonstration | Not robust, lacks logging, monitoring, and alerting |
if/else
statements, a finite state machine library (e.g., transitions
in Python) can be used. This provides a more structured and maintainable approach for managing states and transitions.By considering these aspects, the turnstile system can be designed to be robust, scalable, and adaptable to future requirements.