从管道符到Java编程

说明:在linux操作系统中,管道符用竖线(|)表示,表示将前一个命令的输出作为后面命令的输入。通过这样"流式"地组合操作,能极大地扩展linux命令功能,处理一些复杂场景。

如下,grep命令,能查看某文件中的关键字,tail命令,能查看文件末尾,通过用管道符连接,能实现查看某关键字,在文件中最后一次出现的位置

这种设计思想,像流水线一样,从源头到出口,层层处理,最终达到目的,很有启发。在Java的设计模式中,责任链设计模式,就能体现这种思想。

责任链模式介绍,参考下面这篇文章:

下面我通过两个在实际工作中的场景,来介绍这种思想的体现

参数校验

一般来说,接口进入Service的第一步是参数校验,如下,

java 复制代码
    @Transactional(rollbackFor = Exception.class)
    public List<UserDTO> insert1(UserDTO userDTO) {
        log.info("进入service");
        // 1.参数校验
        userDTO.checkParams();

        // 2.插入数据
        asyncService.insertDTO1(userDTO);

        // 3.返回查询
        return userMapper.selectAll();
    }

UserDTO对象里面,写校验方法,如下:

java 复制代码
import cn.hutool.core.util.StrUtil;
import lombok.Data;

import java.io.Serializable;

@Data
public class UserDTO implements Serializable {

    private String id;

    private String username;

    private String password;

    public void checkParams() {
        if (StrUtil.isBlank(username)) {
            throw new IllegalArgumentException("用户名不能为空");
        }

        if (StrUtil.isBlank(password)) {
            throw new IllegalArgumentException("密码不能为空");
        }

        if (username.length() > 25) {
            throw new IllegalArgumentException("用户名长度不能超过25个字符");
        }

        if (!validatePassword(password)) {
            throw new IllegalArgumentException("密码需要包含数字、大小写字符和特殊字符");
        }
    }

    /**
     * 校验密码是否符合要求
     * @param password 密码
     * @return true符合,false不符合
     */
    private boolean validatePassword(String password) {
        // 至少包含一个大写字母
        boolean hasUpperCase = password.matches("(?=.*[A-Z]).*");

        // 至少包含一个小写字母
        boolean hasLowerCase = password.matches("(?=.*[a-z]).*");

        // 至少包含一个数字
        boolean hasDigit = password.matches("(?=.*\\d).*");

        // 至少包含一个特殊字符
        boolean hasSpecialChar = password.matches("(?=.*[!@#$%^&*()\\-_+=\\[\\]{}|;':\",.<>/?]).*");

        return hasUpperCase && hasLowerCase && hasDigit && hasSpecialChar;
    }
}

参考管道符思想,可以设计成如下,将对属性的校验抽出来,解耦,并返回当前对象;

java 复制代码
import cn.hutool.core.util.StrUtil;
import lombok.Data;

import java.io.Serializable;

@Data
public class UserDTO implements Serializable {

    private String id;

    private String username;

    private String password;

    public UserDTO checkUsername() {
        if (StrUtil.isBlank(username)) {
            throw new IllegalArgumentException("用户名不能为空");
        }

        if (StrUtil.isBlank(password)) {
            throw new IllegalArgumentException("密码不能为空");
        }
        return this;
    }

    public UserDTO checkPassword() {
        if (username.length() > 25) {
            throw new IllegalArgumentException("用户名长度不能超过25个字符");
        }

        if (!validatePassword(password)) {
            throw new IllegalArgumentException("密码需要包含数字、大小写字符和特殊字符");
        }
        return this;
    }

    /**
     * 校验密码是否符合要求
     * @param password 密码
     * @return true符合,false不符合
     */
    private boolean validatePassword(String password) {
        // 至少包含一个大写字母
        boolean hasUpperCase = password.matches("(?=.*[A-Z]).*");

        // 至少包含一个小写字母
        boolean hasLowerCase = password.matches("(?=.*[a-z]).*");

        // 至少包含一个数字
        boolean hasDigit = password.matches("(?=.*\\d).*");

        // 至少包含一个特殊字符
        boolean hasSpecialChar = password.matches("(?=.*[!@#$%^&*()\\-_+=\\[\\]{}|;':\",.<>/?]).*");

        return hasUpperCase && hasLowerCase && hasDigit && hasSpecialChar;
    }
}

使用的时候,需要校验什么字段就接什么方法

java 复制代码
    @Transactional(rollbackFor = Exception.class)
    public List<UserDTO> insert1(UserDTO userDTO) {
        log.info("进入service");
        // 1.参数校验
        userDTO.checkUsername().checkPassword();

        // 2.插入数据
        asyncService.insertDTO1(userDTO);

        // 3.返回查询
        return userMapper.selectAll();
    }

流式组装数据

有个场景,在一个社交系统中,每个用户对象,集成了许多的数据,如

  • 基础信息(头像、昵称、ID、个性签名)
  • 好友数据(好友的基础信息、好友量、权限配置、分组)
  • 系统设置(消息设置、隐私设置、通用)

这些都隶属于一个用户,现在需要获取某个用户的所有信息,通常的做法是分别取查对应的表,然后组装。这里可以使用一种高级的方式,如下:

先写一个接口,定义一个获取用户数据的方法

java 复制代码
import org.example.pojo.UserData;

/**
 * 获取用户数据接口
 */
public interface UserDataService {

    /**
     * 获取用户数据
     * @param userData 用户数据
     * @return 用户数据对象
     */
    UserData getUserData(UserData userData);
}

UserData类如下:

java 复制代码
import lombok.Data;

import java.io.Serializable;
import java.util.Map;

/**
 * 用户数据
 */
@Data
public class UserData implements Serializable {

    private String id;

    /**
     * 用户数据
     */
    private Map<String, String> data;
}

接着,写三个实现类,分别实现获取基础信息、好友数据、系统设置的方法,如下:

(获取用户基础信息)

java 复制代码
import org.example.pojo.UserData;
import org.example.service.UserDataService;
import org.springframework.stereotype.Service;

/**
 * 获取用户基础信息
 */
@Service
public class BaseUserDataService implements UserDataService {

    @Override
    public UserData getUserData(UserData userData) {
        userData.getData().put("基础信息1", "头像");
        userData.getData().put("基础信息2", "昵称");
        userData.getData().put("基础信息3", "ID");
        userData.getData().put("基础信息4", "个性签名");
        return userData;
    }
}

(获取用户好友数据)

java 复制代码
import org.example.pojo.UserData;
import org.example.service.UserDataService;
import org.springframework.stereotype.Service;

/**
 * 获取用户好友数据
 */
@Service
public class FriendUserDataService implements UserDataService {

    @Override
    public UserData getUserData(UserData userData) {
        userData.getData().put("好友数据1", "好友的基础信息");
        userData.getData().put("好友数据2", "好友量");
        userData.getData().put("好友数据3", "权限配置");
        userData.getData().put("好友数据4", "分组");
        return userData;
    }
}

(获取用户设置数据)

java 复制代码
import org.example.pojo.UserData;
import org.example.service.UserDataService;
import org.springframework.stereotype.Service;

/**
 * 获取用户设置数据
 */
@Service
public class SettingUserDataService implements UserDataService {

    @Override
    public UserData getUserData(UserData userData) {
        userData.getData().put("系统设置1", "消息设置");
        userData.getData().put("系统设置2", "隐私设置");
        userData.getData().put("系统设置3", "通用");
        return userData;
    }
}

使用起来就很方便了,如下:

java 复制代码
import org.example.pojo.UserData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;

@Service
public class UserServiceImpl {

    /**
     * 自动装配
     * 把所有 UserDataService 类型的实现类都拿过来
     */
    @Autowired
    private List<UserDataService> userDataServiceList;

    public UserData getUserData() {
        UserData userData = new UserData();
        userData.setId("123456");
        userData.setData(new HashMap<>());
        
        // 调用实现类的方法,去拿对应的数据
        for (UserDataService userDataService : userDataServiceList) {
            userDataService.getUserData(userData);
        }
        return userData;
    }
}

调用接口,查看结果,如下:

以上方式,利用了Spring自动装配和Java多态(调用接口的方法,实际走的是子类实现的方法)的特性,非常优雅地实现了数据组装。架构搭好,现在只需要针对不同的数据,去实现具体的逻辑即可。个人觉得这种场景在实际业务中应该是比较常见的。

总结

本文从linux管道符的设计思想出发,介绍了这种思想在Java编程上的应用,希望能对大家有启发。

相关推荐
努力进修22 分钟前
【Java-数据结构】Java 链表面试题下 “最后一公里”:解决复杂链表问题的致胜法宝
java
小蒜学长38 分钟前
校园网上店铺的设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端·美食
xiao--xin43 分钟前
LeetCode100之子集(78)--Java
java·算法·leetcode·回溯
我惠依旧1 小时前
安卓程序作为web服务端的技术实现(二):Room 实现数据存储
android·java·开发语言
Future_yzx1 小时前
Spring Boot应用中实现基于JWT的登录拦截器,以保证未登录用户无法访问指定的页面
hive·spring boot·后端
你爱写程序吗(新H)1 小时前
基于微信小程序游泳馆管理系统 游泳馆管理系统小程序 (设计与实现)
java·spring boot·微信小程序·小程序
大叔_爱编程1 小时前
wx043基于springboot+vue+uniapp的智慧物流小程序
vue.js·spring boot·小程序·uni-app·毕业设计·源码·课程设计
LNsupermali1 小时前
二叉树的最大深度(遍历思想+分解思想)
java·数据结构
码农小灰1 小时前
Spring MVC中HandlerInterceptor的作用及应用场景
java·spring boot·后端·spring·mvc
爱是小小的癌2 小时前
Java-数据结构-二叉树习题(3)
java·数据结构·算法