Actual combat - responsibility chain mode

Chain of responsibility

Recently, I am developing a more complex system. At present, the workflow module is written in the system (workflow is designed by architects in the company).

A workflow checks the attributes and parameters of an object, while different workflows may redundancy some of the check codes.

The verification of individual features of objects is reused in different workflows. Each verifier is developed separately by using the responsibility chain mode, and then linked to achieve the purpose of high reuse.

text

Design pattern is a very hot topic in our daily development. Development is not a rigid development according to the design pattern, but after development, it iterates according to the actual scene, and it is found that it meets a design pattern after iteration. So don't think about the dead set design patterns into your code, but think about what is most suitable for your business, so as to build a beautiful system.

The responsibility chain mode described in this paper is different from the rookie tutorial. It is a verification responsibility chain for verifying some objects in the system.

Scene description

Before we start, let's talk about my design scene

If you want to fly a kite, you should first consider the following factors:

  1. What's the weather like
  2. Are you free today
  3. Go out tool
  4. Is the kite still there
  5. Can kites still fly
  6. Is the time convenient

Of course, in addition to flying kites, when you find yourself doing other things, you need to consider some of these factors, such as the weather and going out tools on weekdays. So we set these factors as separate validators. Then create a chain (event) and directly execute the verification items in the joint commissioning every time.

We set up a check interface checkHandler, which has different implementations: weather check, idle check, object existence check and other interface methods: check(T data);

After creating validators, we create a responsibility chain class to store these validators.

For different events are different responsibility chains, we only need to put them into the verifier in order and execute the verification method of the verifier. Dochain (T) is to traverse the LinkedList chain in order to execute the verification method.

Create a kite flying Chain:

// Pseudo code
CheckChain<kite> chain = new CheckChain();
chain.addHandlers(new Idle check(), new Weather check(), Kite presence verification(), new Verification of travel tools());

Then run the chain

// Pseudo code
chain.doChain(kite);

Of course, in other affairs, you may also favor the verification of leisure, weather and travel tools, which can be directly inserted. This completes a practical version of the responsibility chain model.

actual combat

Let's write another example program to verify the User.

1 Dependence

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.4</version>
</dependency>

2 verification process

  1. Whether the user exists and whether its basic attributes exist
  2. Gender check (0: female 1: male)
  3. Age check (0 - 200)

3 entity

First create the User class, which has three properties:

  1. full name
  2. Gender
  3. Age
@Getter
public class User {

	private final String name;
	private final Integer age;
	private final Integer sex;

	public User(String name, Integer age, Integer sex) {
		this.name = name;
		this.age = age;
		this.sex = sex;
	}

	@Override
	public String toString() {
		return "User{" +
				"name='" + name + '\'' +
				", age=" + age +
				", sex=" + sex +
				'}';
	}
}

Then define the verifier interface < < interface > > checkhandler

4 tester interface

public interface CheckHandler<T> {

	/**
	 * Check data
	 * @param data data
	 */
	public void check(T data);
}

All verifiers need to implement the method: check(T data)

Three implementation classes are created here

Verifier - user attribute non null verification

@Slf4j
public class UserParamNotNullCheck<T extends User> implements CheckHandler<T> {

	@Override
	public void check(User user) {
		if (null == user || null == user.getSex() || null == user.getAge() || null == user.getName()) {
			throw new RuntimeException("xxx Parameter is null");
		} else {
			log.info("Non null validation passed, {}", user);
		}
	}
}

Verifier - user gender

@Slf4j
public class UserSexCheck<T extends User> implements CheckHandler<T> {
	@Override
	public void check(User user) {
		Integer sex = user.getSex();
		if (0 > sex || sex > 1) {
			throw new RuntimeException("Gender does not exist:" + sex);
		} else {
			log.info("Gender verification passed, {} nature", user.getSex() == 1 ? "male" : "female");
		}
	}
}

Verifier - user age

@Slf4j
public class UserAgeCheck<T extends User> implements CheckHandler<T> {

	private final Integer MIN_AGE = 0;
	private final Integer MAX_AGE = 200;

	@Override
	public void check(User user) {
		Integer age = user.getAge();
		if (MIN_AGE > age || MAX_AGE < age) {
			throw new RuntimeException("Age anomaly: " + age);
		} else {
			log.info("Age verification passed, {} year", user.getAge());
		}
	}
}

5 chain of responsibility - storage verifier

After creating the responsibility chain, the class that needs to be tested is stuffed into it. Finally, the doChain(Data) of the responsibility chain is invoked, and the verification method in the responsibility chain is executed sequentially.

public class CheckChain<T> {

	private final LinkedList<CheckHandler<T>> handlerChain;

	public CheckChain() {
		this.handlerChain = new LinkedList<>();
	}

	public void addHandler(CheckHandler<T> handler) {
		handlerChain.add(handler);
	}

	@SafeVarargs
	public final void addHandlers(CheckHandler<T>...handlers) {
		Arrays.stream(handlers).sequential().forEach(this::addHandler);
	}

	/**
	 * Perform the verification of the verifier in the responsibility chain
	 * @param t
	 */
	public void doChain(T t) {
		while (handlerChain.size() > 0) {
			CheckHandler<T> tCheckHandler = handlerChain.pollFirst();
			tCheckHandler.check(t);
		}
	}
}

6 test

public class ExecutorMain {
	public static void main(String[] args) {
		CheckChain<User> checkChain = new CheckChain<>();
		checkChain.addHandlers(new UserParamNotNullCheck<>(), new UserSexCheck<>(), new UserAgeCheck<>());
		checkChain.doChain(new User("Dcpnet", 22, 1));
	}
}

/**
output:
    15:03:18.490 [main] INFO cn.dcpnet.springstudy.check.UserParamNotNullCheck - Non null validation passed, User{name='Dcpnet', age=22, sex=1}
    15:03:18.492 [main] INFO cn.dcpnet.springstudy.check.UserSexCheck - Gender verification passed, male
    15:03:18.492 [main] INFO cn.dcpnet.springstudy.check.UserAgeCheck - Age verification passed, 22 years old
*/

Different from the rookie tutorial, in fact, the responsibility chain model in many places is also a chain held by a list like this. Therefore, do not develop with design patterns, but design patterns according to the actual business.

Come on, struggling youth, you must have a place in the light of the future and walk towards the light.

Keywords: Java Design Pattern

Added by shelbytll on Fri, 28 Jan 2022 10:32:11 +0200