定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换,策略模式的目的就是将算法的使用与算法的实现分离开来,避免使用多重条件判断。
策略类封装了具体的算法,并负责具体的计算过程。第二个部分是环境类Context,Context 接受客户的请求,随后把请求委托给某一个策略类。
const submit = () => {
const {
age,
name,
date
} = formData;
if (!name) {
console.log('将label变为红色');
return toast('姓名不能为空');
}
if (name.length > 10) {
console.log('将label变为红色');
return toast('姓名长度不能超过10');
}
if (!age) {
return toast('年龄不能为空');
}
if (date) {
const dateVal = new Date(date);
const dateMax = new Date();
if (dateVal > dateMax) {
return toast('不能大于当前日期');
}
}
}
开始编码之前需要清楚的是,我们预期它有什么功能,知道需要什么才能去设计去开发。很多同学写程序没有思路就在于没有想好自己想要的功能,或者使用方式。
校验器的作用
1.封装校验逻辑,相同场景可复用,并且职责单一,用来判断属性值是否合法。
需要实现的功能
1.支持对属性添加单个规则,或者多个对规则
2.支持对检测到的非法值执行相应的回调
使用方式:
// 对属性只加多个校验规则 对name的值进行校验 校验是否为空 长度是否超过10
validator.add(formDate.name, [
{
type: TYPES.isEmpty,
rules: {
errorMsg: '姓名不能为空',
errCallBack: (errMsg) => {console.log(errMsg, '校验失败的回调 处理name的label变红色')}
}
},{
type: TYPES.maxLength,
rules: {
errorMsg: '姓名长度不能超过10',
maxlength: 10
}
}
]);
// 对属性值加单个校验规则 校验age是否为空
validator.add(formDate.age, [
{
type: TYPES.isEmpty,
rules: {
errorMsg: '年龄不能为空'
}
}
]);
// 开始校验
const errorMsg = validator.start();
// 判断是否通过校验
if (errorMsg) {
console.log(errorMsg, '未通过校验')
}
// 所有的校验规则
const strategies = {
// 空值校验
isEmpty: (value, rules) => {
// rules使用对象传参 可以使得不同规则下定义不同的参数
const {errorMsg, errCallBack} = rules;
if (!value && value !== 0) {
errCallBack && errCallBack(errorMsg);
return errorMsg;
}
},
// 长度校验
maxLength: (value, rules) => {
const {maxlength, errorMsg, errCallBack} = rules;
if (value.length > maxlength) {
errCallBack && errCallBack(errorMsg);
return errorMsg;
}
},
// 大于指定时间校验
gtDate: (value, rules) => {
const {maxDate, errorMsg, errCallBack} = rules;
const dateVal = new Date(value);
const dateMax = new Date(maxDate);
if (dateVal > dateMax) {
errCallBack && errCallBack(errorMsg);
return errorMsg;
}
}
};
// 校验器
class Validator {
constructor() {
// 用来存储校验策略的队列
this.cach = [];
}
// 支持添加多个和单个
add (value, rulesArgs = []) {
// 兼容单个规则的写法
if (!(rulesArgs instanceof Array)) {
rulesArgs = [rulesArgs];
}
rulesArgs.forEach(ruleArg => {
const {type , rules} = ruleArg;
this.cach.push(() => {
// 返回对应规则策略的执行
return strategies[type] && strategies[type](value, rules);
});
})
}
// 开始校验
start () {
// 根据add的顺序逐一校验
for (const strategyFn of this.cach) {
const msg = strategyFn();
// 遇到非法结果 退出循环
if (msg) {
return msg;
}
}
}
}
export const validator = () => {
return new Validator();
}
import styles from './index.module.less';
import { Input, Form, Button, Toast, DatePicker } from 'antd-mobile';
import { useState } from 'react';
import moment from 'moment';
import { validator as getValidator, TYPES } from '../tools/v';
// 方便多个类名的书写
const classNames = (classNames) => {
let result = '';
classNames.forEach(name => {
if (styles[name]) {
result += ` ${styles[name]}`;
}
});
return result;
}
function Test() {
// 表单数据
const [formData, setformData] = useState({
age: '',
name: '',
date: ''
});
// 针对某个数据的校验做特殊样式处理
const [formErr, setFormErr] = useState({
name: false
});
// 控制时间选择器的显示
const [visible, setVisible] = useState(false);
const toast = (msg) => {
Toast.show({
content: msg
});
}
// 校验表单数据
const validateForm = (formData) => {
const validator = getValidator();
const {age, name, date} = formData;
// 添加校验规则
validator.add(name, [
{
type: TYPES.isEmpty,
rules: {
errorMsg: '姓名不能为空',
// 一些定制化的错误处理
errCallBack: () => {
setFormErr(state => ({...state, name: true}));
}
}
}, {
type: TYPES.maxLength,
rules: {
maxlength: 10,
errorMsg: '姓名不得超过10个字',
errCallBack: () => {
setFormErr(state => ({...state, name: true}));
}
}
}
]);
// 校验年龄
validator.add(age, {
type: TYPES.isEmpty,
rules: {
errorMsg: '年龄不能为空'
}
});
// 校验日期
validator.add(date, {
type: TYPES.gtDate,
rules: {
errorMsg: '日期不能大与当前时间',
maxDate: moment(new Date()).format('YYYY-MM-DD')
}
});
const errMsg = validator.start();
// 统一处理toast
if (errMsg) {
toast(errMsg);
return true;
}
}
// 提交表单
const submit = () => {
const isIllegal = validateForm(formData);
if (isIllegal) {
return;
}
toast('提交成功');
}
return (
<div className={styles.wrap}>
<Form
layout='horizontal'
footer={
<Button block type='submit' color='primary' size='large' onClick={submit}>
提交
</Button>
}
mode='card'
>
<Form.Item
className={classNames(['name', formErr.name ? 'error' : '']) }
label='姓名'
>
<Input
maxLength='20'
placeholder='请输入姓名'
value={formData.name}
onChange={val => {
setFormErr(state => ({...state, name: false}))
setformData(state => ({...state, name: val}))
}}
/>
</Form.Item>
<Form.Item
label='年龄'
>
<Input
placeholder='请输入年龄'
max={120}
type='number'
value={formData.age}
onChange={val => {
setformData(state => ({...state, age: val}))
}}
/>
</Form.Item>
<Form.Item>
<Button onClick={() => setVisible(true)}>{formData.date ? formData.date : '时间选择'}</Button>
</Form.Item>
</Form>
<DatePicker
title='时间选择'
visible={visible}
onClose={() => {
setVisible(false);
}}
onConfirm={val => {
setformData(state => ({...state, date: moment(val).format('YYYY-MM-DD')}))
}
}
/>
</div>
);
}
export default Test;