A design pattern with a few conceptual aspects:
- The total number of states a system can be in are finite, and enumerable.
- State transitions are limited to predefined rules.
- The state of a system can be determined by an object’s properties.
Consider the following state machine concept in Kotlin: a payment processing system.
First, let’s define some statuses for the state machine:
1
2
3
4
5
6
7
8
| enum Status {
Requested,
Processing,
Approved,
Declined,
Error,
Completed,
}
|
Second, we define a payment object:
1
2
3
4
5
6
7
| class PaymentRequest(private val card: Card) {
private var status: Status = Status.Requested
fun getStatus(): Status {
return status
}
}
|
Third, we define an interface for different states:
1
2
3
| interface PaymentProcessingState {
fun process(paymentRequest: PaymentRequest): Status
}
|
And implementations for each state:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
| class RequestedPaymentProcessingState(private val userService: UserService) : PaymentProcessingState {
fun process(paymentRequest: PaymentRequest): Status {
if (!userService.userLoggedIn(paymentRequest)) {
return Status.Error
}
// do some more logic
..
}
}
class ProcessingPaymentProcessingState(private val paymentGateway: PaymentGateway) : PaymentProcessingState {
fun process(paymentRequest: PaymentRequest): Status {
try {
paymentGateway.charge(paymentRequest)
return Status.Approved
} catch (e: ChargeFailureException) {
return Status.Declined
}
}
}
class ApprovedPaymentProcessingState : PaymentProcessingState {
fun process(paymentRequest: PaymentRequest): Status {
// mark a database record as approved
..
return Status.Completed
}
}
class DeclinedPaymentProcessingState : PaymentProcessingState {
fun process(paymentRequest: PaymentRequest): Status {
// mark a database record as declined
..
// evaluate retry logic
..
return Status.Completed
}
}
class ErrorPaymentProcessingState : PaymentProcessingState {
fun process(paymentRequest: PaymentRequest): Status {
// mark a database record as error
..
return Status.Completed
}
}
class CompletedPaymentProcessingState : PaymentProcessingState {
fun process(paymentRequest: PaymentRequest): Status {
// log some analytics
..
return Status.Completed
}
}
|
Finally, we can define a payment processing service, which ties all the states together:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| class PaymentProcessingService(
private val userService: UserService,
private val paymentGateway: PaymentGateway,
) {
fun processPayment(paymentRequest: PaymentRequest) {
when (paymentRequest.getStatus()) {
Status.Requested -> RequestedPaymentProcessingState(userService).process(paymentRequest)
Status.Processing -> ProcessingPaymentProcessingState(paymentGateway).process(paymentRequest)
Status.Approved -> ApprovedPaymentProcessingState().process(paymentRequest)
Status.Declined -> DeclinedPaymentProcessingState().process(paymentRequest)
Status.Error -> ErrorPaymentProcessingState().process(paymentRequest)
Status.Completed -> CompletedProcessingState().process(paymentRequest)
}
}
}
|