This article is excerpted from This is How Design Patterns should Be Learned
UML class diagrams for state patterns
The UML class diagram for the state pattern is shown below.
2 Use the status mode to switch login status freely
When we read articles in the community and think they’re good, we comment and bookmark them. If you are logged in, you can make comments and favorites directly. Otherwise, the login page is displayed, and you can perform the previous operations after login. There are two states involved: logged in and unlogged; There are two kinds of behavior: commenting and collecting. To implement this logic, use the state pattern as follows. Start by creating the abstract state role UserState class.
public abstract class UserState {
protected AppContext context;
public void setContext(AppContext context) {
this.context = context;
}
public abstract void favorite(a);
public abstract void comment(String comment);
}
Copy the code
Then create the LogInState class.
public class LoginInState extends UserState {
@Override
public void favorite(a) {
System.out.println("Successful collection!");
}
@Override
public void comment(String comment) { System.out.println(comment); }}Copy the code
Create the UnloginState class.
public class UnLoginState extends UserState {
@Override
public void favorite(a) {
this.switch2Login();
this.context.getState().favorite();
}
@Override
public void comment(String comment) {
this.switch2Login();
this.context.getState().comment(comment);
}
private void switch2Login(a) {
System.out.println("Jump to login page!");
this.context.setState(this.context.STATE_LOGIN); }}Copy the code
Create the context role AppContext class.
public class AppContext {
public static final UserState STATE_LOGIN = new LoginInState();
public static final UserState STATE_UNLOGIN = new UnLoginState();
private UserState currentState = STATE_UNLOGIN;
{
STATE_LOGIN.setContext(this);
STATE_UNLOGIN.setContext(this);
}
public void setState(UserState state) {
this.currentState = state;
this.currentState.setContext(this);
}
public UserState getState(a) {
return this.currentState;
}
public void favorite(a) {
this.currentState.favorite();
}
public void comment(String comment) {
this.currentState.comment(comment); }}Copy the code
Finally write the client test code.
public static void main(String[] args) {
AppContext context = new AppContext();
context.favorite();
context.comment("Comment: Great article, 360 likes!");
}
Copy the code
The running result is shown in the figure below.
3. Use state machine to control order state flow
A state machine is an application of the state pattern, which is an updated version of the context role. It is widely used in various systems such as workflow or games, such as various workflow engines, which are almost subsets and implementations of state machines, encapsulating the rules of state change. Spring also provides a good solution. The name of the component in Spring is a StateMachine. State machines help developers simplify the development process of state control and make the structure of state machines more hierarchical. The following is a simulation of an order state flow using the Spring state machine.
3.1 Adding a Dependency.
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>2.01..RELEASE</version>
</dependency>
Copy the code
3.2 Create the Order entity Order class.
public class Order {
private int id;
private OrderStatus status;
public void setStatus(OrderStatus status) {
this.status = status;
}
public OrderStatus getStatus(a) {
return status;
}
public void setId(int id) {
this.id = id;
}
public int getId(a) {
return id;
}
@Override
public String toString(a) {
return Order No. : + id + ", order status:+ status; }}Copy the code
3.3 Create order state enumeration class and state transition enumeration class.
/** * Order status */
public enum OrderStatus {
// To be paid, to be shipped, to be received, end of order
WAIT_PAYMENT, WAIT_DELIVER, WAIT_RECEIVE, FINISH;
}
/** * Order status change event */
public enum OrderStatusChangeEvent {
// Confirm receipt of goods
PAYED, DELIVERY, RECEIVED;
}
Copy the code
3.4 Adding a state flow configuration.
/** * Order state machine configuration */
@Configuration
@EnableStateMachine(name = "orderStateMachine")
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatus.OrderStatusChangeEvent> {
/** * Configuration status *@param states
* @throws Exception
*/
public void configure(StateMachineStateConfigurer
states)
,> throws Exception {
states
.withStates()
.initial(OrderStatus.WAIT_PAYMENT)
.states(EnumSet.allOf(OrderStatus.class));
}
/** * Configures the state transition event relationship *@param transitions
* @throws Exception
*/
public void configure(StateMachineTransitionConfigurer
transitions)
,> throws Exception {
transitions
.withExternal().source(OrderStatus.WAIT_PAYMENT).target(OrderStatus.WAIT_DELIVER)
.event(OrderStatusChangeEvent.PAYED)
.and()
.withExternal().source(OrderStatus.WAIT_DELIVER).target(OrderStatus.WAIT_RECEIVE)
.event(OrderStatusChangeEvent.DELIVERY)
.and()
.withExternal().source(OrderStatus.WAIT_RECEIVE).target(OrderStatus.FINISH)
.event(OrderStatusChangeEvent.RECEIVED);
}
/** * Persistent configuration * In practice, you can cooperate with Redis and other persistent operations *@return* /
@Bean
public DefaultStateMachinePersister persister(a){
return new DefaultStateMachinePersister<>(new StateMachinePersist<Object, Object, Order>() {
@Override
public void write(StateMachineContext<Object, Object> context, Order order) throws Exception {
// There is no persistence here
}
@Override
public StateMachineContext<Object, Object> read(Order order) throws Exception {
// The state in Order is directly retrieved, and no persistent reads are actually performed
return new DefaultStateMachineContext(order.getStatus(), null.null.null); }}); }}Copy the code
3.5 Adding an order Status Listener.
@Component("orderStateListener")
@WithStateMachine(name = "orderStateMachine")
public class OrderStateListenerImpl{
@OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
public boolean payTransition(Message<OrderStatusChangeEvent> message) {
Order order = (Order) message.getHeaders().get("order");
order.setStatus(OrderStatus.WAIT_DELIVER);
System.out.println("Payment, state machine feedback:" + message.getHeaders().toString());
return true;
}
@OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")
public boolean deliverTransition(Message<OrderStatusChangeEvent> message) {
Order order = (Order) message.getHeaders().get("order");
order.setStatus(OrderStatus.WAIT_RECEIVE);
System.out.println("Delivery, state machine feedback:" + message.getHeaders().toString());
return true;
}
@OnTransition(source = "WAIT_RECEIVE", target = "FINISH")
public boolean receiveTransition(Message<OrderStatusChangeEvent> message){
Order order = (Order) message.getHeaders().get("order");
order.setStatus(OrderStatus.FINISH);
System.out.println("Received goods, state machine feedback information:" + message.getHeaders().toString());
return true; }}Copy the code
3.6 Creating the IOrderService Interface.
public interface IOrderService {
// Create a new order
Order create(a);
// Initiate payment
Order pay(int id);
// Order shipped
Order deliver(int id);
// Order received
Order receive(int id);
// Get all order information
Map<Integer, Order> getOrders(a);
}
Copy the code
3.7 Application in Service Logic.
@Service("orderService")
public class OrderServiceImpl implements IOrderService {
@Autowired
private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
@Autowired
private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, Order> persister;
private int id = 1;
private Map<Integer, Order> orders = new HashMap<>();
public Order create(a) {
Order order = new Order();
order.setStatus(OrderStatus.WAIT_PAYMENT);
order.setId(id++);
orders.put(order.getId(), order);
return order;
}
public Order pay(int id) {
Order order = orders.get(id);
System.out.println("Thread name:" + Thread.currentThread().getName() + "Attempt payment, Order No. :" + id);
Message message = MessageBuilder.withPayload(OrderStatusChangeEvent.PAYED).
setHeader("order", order).build();
if(! sendEvent(message, order)) { System.out.println("Thread name:" + Thread.currentThread().getName() + "Payment failed, status abnormal, order No. :" + id);
}
return orders.get(id);
}
public Order deliver(int id) {
Order order = orders.get(id);
System.out.println("Thread name:" + Thread.currentThread().getName() + "Attempted shipment, Order No. :" + id);
if(! sendEvent(MessageBuilder.withPayload(OrderStatusChangeEvent.DELIVERY) .setHeader("order", order).build(), orders.get(id))) {
System.out.println("Thread name:" + Thread.currentThread().getName() + "Delivery failure, abnormal status, order No. :" + id);
}
return orders.get(id);
}
public Order receive(int id) {
Order order = orders.get(id);
System.out.println("Thread name:" + Thread.currentThread().getName() + "Try to receive goods, order no. :" + id);
if(! sendEvent(MessageBuilder.withPayload(OrderStatusChangeEvent.RECEIVED) .setHeader("order", order).build(), orders.get(id))) {
System.out.println("Thread name:" + Thread.currentThread().getName() + "Failed to receive, abnormal status, order No. :" + id);
}
return orders.get(id);
}
public Map<Integer, Order> getOrders(a) {
return orders;
}
/** * Send the order status transition event **@param message
* @param order
* @return* /
private synchronized boolean sendEvent(Message<OrderStatusChangeEvent> message, Order order) {
boolean result = false;
try {
orderStateMachine.start();
// Try to restore the state machine
persister.restore(orderStateMachine, order);
// Add delay for thread safety testing
Thread.sleep(1000);
result = orderStateMachine.sendEvent(message);
// Persist state machine state
persister.persist(orderStateMachine, order);
} catch (Exception e) {
e.printStackTrace();
} finally {
orderStateMachine.stop();
}
returnresult; }}Copy the code
3.8 Write client test code.
@SpringBootApplication
public class Test {
public static void main(String[] args) {
Thread.currentThread().setName("Main thread");
ConfigurableApplicationContext context = SpringApplication.run(Test.class,args);
IOrderService orderService = (IOrderService)context.getBean("orderService");
orderService.create();
orderService.create();
orderService.pay(1);
new Thread("Client thread") {@Override
public void run(a) {
orderService.deliver(1);
orderService.receive(1);
}
}.start();
orderService.pay(2);
orderService.deliver(2);
orderService.receive(2);
System.out.println("Status of all orders:"+ orderService.getOrders()); }}Copy the code
Through this real business case, I believe that partners have a very deep understanding of the state mode. Pay attention to “Tom play architecture” reply to “design pattern” can obtain the complete source code.
Tom play architecture: 30 real cases of design patterns (attached source code), the challenge of annual salary 60W is not a dream
This article is “Tom play structure” original, reproduced please indicate the source. Technology is to share, I share my happiness! If this article is helpful to you, welcome to follow and like; If you have any suggestions can also leave a comment or private letter, your support is my motivation to adhere to the creation. Pay attention to “Tom bomb architecture” for more technical dry goods!