设计模式之规约模式

2023-10-11 08:31:41 浏览数 (2)

引言

规约模式的英文是Specification Pattern,Specification直译过来是要求、技术说明、明确的意思。光看名字估计大家都是一脸懵逼,根本不知道这个设计模式大概会是一个什么样子。这也是设计模式的一个通病,就是内涵比较晦涩,很难通过名称来概括。

我们先来简单说说这里规约的意思,它的含义其实很简单,就是把代码当中的代码逻辑以及业务规则区分开。这样的话我们可以在不影响全局的情况下自由地修改和组合这两个部分。

举个例子:

  • 我们都知道QQ里有各种钻石会员,什么绿钻、粉钻之类的。每种会员呢对应一些特殊的权益,假设现在我们新开发了某一个功能,要提供给同时拥有绿钻和粉钻的用户使用。
  • 我们来实现这个逻辑非常简单,只需要一个判断条件就可以了。
代码语言:javascript复制
    public boolean isSatisfied(User user) {
        return user.isGreenAuth() && user.isPinkAuth();
    }
  • 但是这里有一个问题,这里的逻辑是写死的,时间久了之后,如果这个代码转交给其他人维护了,那么接手的人估计会一脸懵。根本不知道这里为什么要这么判断,也不知道这个函数代表的功能是什么,可能需要去翻很多代码或者是找很多文档才能解决疑惑。
  • 这里的根本问题就是业务规则和实现逻辑混合在一起了,什么意思呢 ?
    • 这里的绿钻 粉钻的判断就是业务规则,是为了实现某项功能而制定的,产品经理要的是这个两者叠加的规则。
    • 而我们通过user.isGreenAuth() and user.isPinkAuth()来实现,相当于新建了一个规则,从功能上这当然没有问题。但是问题是这里的规则和代码是定死的。
    • 假设说某一天产品经理提了一个新的需求,不仅需要绿钻 粉钻还需要年龄大于18岁,并且年收入超过10w才可以享受这个功能。
    • 那么带来的结果就是我们需要在这个is_satisfied函数当中加上许多代码,随着时间的推移这个函数当中的代码会变得越来越臃肿,并且很有可能以后这当中的一些判断边界需要修改,比如18岁改成20岁,比如10w改成30w等等,这些都是有可能的。当要修改的时候你会发现由于代码的耦合和混乱,改起来非常麻烦。

为了解决这个问题就需要引入规约。


规约模式

规约的意思是把逻辑和规则区分开,规则的归规则,逻辑的归逻辑。

我们还用上面的例子来看,比如在新的需求当中,逻辑本身是很简单的。即粉钻 绿钻 年龄达标 收入达标,这个是规则,而年龄达标和收入达标则是具体的实现。

当我们把规则定好了之后,我们再去解构其中的组件,比如收入达标的定义是年收入大于10w,比如年龄达标的含义是大于18岁。我们可以把这些组件做成单独的模块,这样以后需要修改这些边界的时候,我们只需要在具体实现当中修改就可以了,对于规则的部分就不用动了。同样如果我们需要修改规则,我们也可以避免对实现的改动,这就是规约的意义。

首先,我们需要一个框架用来实现逻辑的组合。

在逻辑当中变量之间的关系只有三种,就是与或非。所以整个逻辑关系还是很清楚的。这里我们采取抽象的方法,先定义接口,再去做具体的实现。这是我们规约出来的条件,它当中有四个接口,分别是与或非的操作接口,以及一个is_satisfied的判断接口。

规约的一个更常用的用途是进行数据筛选,而我们的筛选条件通常是复杂的,因此规约还要实现链式操作。因此需要进行抽象,到达操作一致的目的。

代码语言:javascript复制
public interface ISpecification<T> {
    boolean isSatisfiedBy(T o);

    ISpecification<T> and(ISpecification<T> specification);

    ISpecification<T> or(ISpecification<T> specification);

    ISpecification<T> not(ISpecification<T> specification);

    default ISpecification<T> and(Function<T, Boolean> function) {
        return and(new CompositeSpecification<T>() {
            @Override
            public boolean isSatisfiedBy(T o) {
                return function.apply(o);
            }
        });
    }

    default ISpecification<T> or(Function<T, Boolean> function) {
        return or(new CompositeSpecification<T>() {
            @Override
            public boolean isSatisfiedBy(T o) {
                return function.apply(o);
            }
        });
    }

    default ISpecification<T> not(Function<T, Boolean> function) {
        return not(new CompositeSpecification<T>() {
            @Override
            public boolean isSatisfiedBy(T o) {
                return function.apply(o);
            }
        });
    }

    public static <T> ISpecification<T> create(Function<T, Boolean> function){
         return new CompositeSpecification<T>() {
             @Override
             public boolean isSatisfiedBy(T o) {
                 return function.apply(o);
             }
         };
    }
}

每个规约实现四个方法:IsSatisfiedBy()、And()、Or()、Not()。IsSatisfiedBy()方法主要实现业务规则,而其它三个则用来将复合业务规则连在一起。

在Specification的基础上,我们进一步实现与或非执行的逻辑:

代码语言:javascript复制
public abstract class CompositeSpecification<T> implements ISpecification<T>{
    @Override
    public ISpecification<T> and(ISpecification<T> specification) {
        return new AndSpecification<T>(this,specification);
    }

    @Override
    public ISpecification<T> or(ISpecification<T> specification) {
        return new OrSpecification<T>(this,specification);
    }

    @Override
    public ISpecification<T> not(ISpecification<T> specification) {
        return new NotSpecification<T>(specification);
    }
}

对于所有复合规约来说,And()、Or()、Not()方法都是相同的,只有IsSatisfiedBy()方法会有区别。接来下看一下链式规约的实现,分别对应And()、Or()、Not()方法:

代码语言:javascript复制
public class AndSpecification<T> extends CompositeSpecification<T> {
    private final ISpecification<T> left;
    private final ISpecification<T> right;

    public AndSpecification(ISpecification<T> left, ISpecification<T> right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public boolean isSatisfiedBy(T o) {
        return left.isSatisfiedBy(o) && right.isSatisfiedBy(o);
    }
}
代码语言:javascript复制
public class OrSpecification<T> extends CompositeSpecification<T> {
    private final ISpecification<T> left;
    private final ISpecification<T> right;

    public OrSpecification(CompositeSpecification<T> left, ISpecification<T> right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public boolean isSatisfiedBy(T o) {
        return left.isSatisfiedBy(o) || right.isSatisfiedBy(o);
    }
}
代码语言:javascript复制
public class NotSpecification<T> extends CompositeSpecification<T> {
    private final ISpecification<T> specification;

    public NotSpecification(ISpecification<T> specification) {
        this.specification=specification;
    }

    @Override
    public boolean isSatisfiedBy(T o) {
        return !specification.isSatisfiedBy(o);
    }
}

案例改造

用规约模式改造上面给出的案例:

代码语言:javascript复制
public class SpecificationTest {


    @Test
    public void test(){
        User user = new User(1, 20, 100000, true, false, true);
        //条件: age>18 && inComing>10000 && (greenAuth || pinkAuth)
        ISpecification<User> specification = new UserSpecification().and(u -> u.age > 18).and(u -> u.inComing > 10000)
                .and(ISpecification.create(User::getGreenAuth).or(User::getPinkAuth));
        boolean satisfiedBy = specification.isSatisfiedBy(user);
        System.out.println("是否满足条件: " satisfiedBy);
    }


    public static class UserSpecification extends CompositeSpecification<User> {

        @Override
        public boolean isSatisfiedBy(User user) {
            return user.enable;
        }
    }

    @Data
    @AllArgsConstructor
    private static class User {
        private Integer id;
        private Integer age;
        private Integer inComing;
        private Boolean greenAuth;
        private Boolean pinkAuth;
        private Boolean enable;
    }
}

参考

规约模式,颤抖吧产品经理!再也不怕你乱改需求了

规约模式

0 人点赞