规格

规格(Specification)用来将业务规则(通常是隐式业务规则)封装成独立的逻辑单元,从而将隐式业务规则提炼为显示概念,并达到代码复用的目的。

常见的业务规则如下:

  • 校验业务对象的某些状态是否合法,例如当前账户是否启用,账户余额是否充足,事故日期是否在保险单的有效时间内。

  • 从业务对象的集合中筛选出符合条件的结果集,例如从用户的交易记录中找出购买打折产品的记录。

  • 检查一个新创建的业务对象是否符合某些业务条件,例如一张新创建的订单,它对应的客户与商户都应该是合法系统用户。

常见业务规则实现方式

public class Person {
    Integer age;

    public Person(Integer pAge) {
        this.age = pAge;
    }

    public boolean isUnderage() {
        return age < 18;
    }

    public boolean isSenior() {
        return age > 65;
    }
}

public class Discount {
    private static final Integer MAJORITY_AGE = 18;
    private static final Integer MINIMAL_SENIOR_AGE = 65;

    public static boolean isDiscount(Person person) {
        return person.getAge() < MAJORITY_AGE && isWeekend()
                || person.getAge() > MINIMAL_SENIOR_AGE;
    }

    private static boolean isWeekend() {
        DayOfWeek today = LocalDate.now().getDayOfWeek();
        return today == DayOfWeek.SATURDAY
                || today == DayOfWeek.SUNDAY;
    }
}

这种方式优点是开发简单,逻辑也都集中一个地方,但如果后期业务变复杂,判断逻辑会开始得错综复杂,维护和测试都会越来越困难,而且规则代码也无法复用,不满足DRY(Don’tRepeat Yourself)的要求。

如何实现Specification

定义接口:

/**
 * Specificaiton interface.
 *
 */
public interface Specification<T> {

  boolean isSatisfiedBy(T t);

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


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


  Specification<T> not(Specification<T> specification);
}

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

来看它的抽象实现:

/**
 * Abstract base implementation of composite {@link Specification} with default
 * implementations for {@code and}, {@code or} and {@code not}.
 */
public abstract class AbstractSpecification<T> implements Specification<T> {

  @Override
  public abstract boolean isSatisfiedBy(T t);

  @Override
  public Specification<T> and(final Specification<T> specification) {
    return new AndSpecification<T>(this, specification);
  }

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

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

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

/**
 * AND specification, used to create a new specifcation that is the AND of two other specifications.
 */
public class AndSpecification<T> extends AbstractSpecification<T> {

  private Specification<T> spec1;
  private Specification<T> spec2;

  /**
   * Create a new AND specification based on two other spec.
   *
   * @param spec1 Specification one.
   * @param spec2 Specification two.
   */
  public AndSpecification(final Specification<T> spec1, final Specification<T> spec2) {
    this.spec1 = spec1;
    this.spec2 = spec2;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isSatisfiedBy(final T t) {
    return spec1.isSatisfiedBy(t) && spec2.isSatisfiedBy(t);
  }
}


/**
 * OR specification, used to create a new specifcation that is the OR of two other specifications.
 */
public class OrSpecification<T> extends AbstractSpecification<T> {

  private Specification<T> spec1;
  private Specification<T> spec2;

  /**
   * Create a new OR specification based on two other spec.
   *
   * @param spec1 Specification one.
   * @param spec2 Specification two.
   */
  public OrSpecification(final Specification<T> spec1, final Specification<T> spec2) {
    this.spec1 = spec1;
    this.spec2 = spec2;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isSatisfiedBy(final T t) {
    return spec1.isSatisfiedBy(t) || spec2.isSatisfiedBy(t);
  }
}


/**
 * NOT decorator, used to create a new specifcation that is the inverse (NOT) of the given spec.
 */
public class NotSpecification<T> extends AbstractSpecification<T> {

  private Specification<T> spec1;

  /**
   * Create a new NOT specification based on another spec.
   *
   * @param spec1 Specification instance to not.
   */
  public NotSpecification(final Specification<T> spec1) {
    this.spec1 = spec1;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isSatisfiedBy(final T t) {
    return !spec1.isSatisfiedBy(t);
  }
}

代码示例

public class IsUnderageSpecification extends AbstractSpecification<Discount> {
    private static final Integer MAJORITY_AGE = 18;

    @Override
    public boolean isSatisfiedBy(Discount person) {
        return discount.getPerson().getAge() < MAJORITY_AGE;
    }
}

public class IsSeniorSpecification extends AbstractSpecification<Discount> {
    private static final Integer MINIMAL_SENIOR_AGE = 65;

    @Override
    public boolean isSatisfiedBy(Discount discount) {
        return discount.getPerson().getAge() > MINIMAL_SENIOR_AGE;
    }
}

public class IsWeekendSpecification extends AbstractSpecification<Discount> {

    @Override
    public boolean isSatisfiedBy(Discount discount) {
        DayOfWeek today = LocalDate.now().getDayOfWeek();
        return today == DayOfWeek.SATURDAY
                || today == DayOfWeek.SUNDAY;
    }
}

public class DiscountSpecification extends AbstractSpecification<Person> {

    CompositeSpecification<Person> isUnderage = new IsUnderageSpecification();
    CompositeSpecification<Person> isSenior = new IsSeniorSpecification();
    CompositeSpecification<Person> isWeekend = new IsWeekendSpecification();

    @Override
    public boolean isSatisfiedBy(Person person) {
        return isUnderage
                .and(isWeekend)
                .or(isSenior)
                .isSatisfiedBy();
    }
}

results matching ""

    No results matching ""