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!