规约模式(Specification Pattern),简单来说就是就是约束条件,例如从数据库中获取到满足约束条件的数据(where
条件之后即为约束条件):
1
| select * from `book` where `name` like `%java%` and `price` < 50;
|
如果我们要用java
实现类似约束,可能会想到:
1 2 3 4 5
| for (Book book : books) { if (book.name.contains("java") && book.price < 40) { System.out.println(book); } }
|
规约一般实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| public interface Specification<T> {
boolean isSatisfiedBy(T params);
Specification<T> and(Specification<T> other);
Specification<T> or(Specification<T> other);
Specification<T> not(); }
public abstract class CompositeSpecification<T> implements Specification<T> {
@Override public Specification<T> and(Specification<T> other) { return new AndSpecification<T>(this, other); }
@Override public Specification<T> not() { return new NotSpecification<T>(this); }
@Override public Specification<T> or(Specification<T> other) { return new OrSpecification<T>(this, other); }
}
public class AndSpecification<T> extends CompositeSpecification<T> {
private final Specification<T> b; private final Specification<T> a;
public AndSpecification(Specification<T> a, Specification<T> b) { this.a = a; this.b = b; }
@Override public boolean isSatisfiedBy(T params) { return a.isSatisfiedBy(params) && b.isSatisfiedBy(params); } }
public class NotSpecification<T> extends CompositeSpecification<T> {
private final Specification<T> a;
public NotSpecification(Specification<T> a) { this.a = a; }
@Override public boolean isSatisfiedBy(T params) { return !a.isSatisfiedBy(params); } }
public class OrSpecification<T> extends CompositeSpecification<T> {
private final Specification<T> b; private final Specification<T> a;
public OrSpecification(Specification<T> a, Specification<T> b) { this.a = a; this.b = b; }
@Override public boolean isSatisfiedBy(T params) { return a.isSatisfiedBy(params) || b.isSatisfiedBy(params); } }
|
上面即为规约模式的一般实现,可以看到在接口Specification
中定义了4个方法:
1 2 3 4
| isSatisfiedBy():是否满足指定条件 and():条件与 or():条件或 not():条件非
|
与,或,非3个条件的组合已经基本可以满足日常查询条件
创建约束条件
假设我们有如下Book
对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class Book {
public String name;
public int price;
public int year;
public Book(String name, int price, int year) { super(); this.name = name; this.price = price; this.year = year; }
@Override public String toString() { return "Book [name=" + name + ", price=" + price + ", year=" + year + "]"; }
}
|
如果要求的单查询条件为名称包含指定关键字,价格在指定区间内,指定出版年份,我们可以创建如下对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| public class BookNameSpecification extends CompositeSpecification<Book> {
private String name;
public BookNameSpecification(String name) { super(); this.name = name; }
@Override public boolean isSatisfiedBy(Book params) { return params.name.contains(name); }
}
public class BookPriceSpecification extends CompositeSpecification<Book> {
private int minPrice;
private int maxPrice;
public BookPriceSpecification(int minPrice, int maxPrice) { super(); this.minPrice = minPrice; this.maxPrice = maxPrice; }
@Override public boolean isSatisfiedBy(Book params) { return params.price >= minPrice && params.price <= maxPrice; }
}
public class BookTimeSpecification extends CompositeSpecification<Book> {
private int year;
public BookTimeSpecification(int year) { super(); this.year = year; }
@Override public boolean isSatisfiedBy(Book params) { return params.year == year; }
}
|
根据这三个约束条件,我们可以组合出多种不同的约束条件,比如我们希望查询名称包含Java,价格在30~60元,在2019年出版:
1 2 3
| Specification<Book> bookSpecification = new BookNameSpecification("java") .and(new BookPriceSpecification(30, 60)) .and(new BookTimeSpecification(2019));
|
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class Main {
public static void main(String[] args) { Book[] books = { new Book("java从入门到精通", 45, 2017), new Book("java从入门到放弃", 32, 2018), new Book("Spring核心", 67, 2019), new Book("Spring2.5", 23, 2007), new Book("Mysql原理", 88, 2017) };
Specification<Book> bookSpecification = (new BookNameSpecification("java") .and(new BookPriceSpecification(45, 60))).or(new BookNameSpecification("Mysql")) .or(new BookNameSpecification("Spring").and(new BookTimeSpecification(2007).not()));
for (Book book : books) { if (bookSpecification.isSatisfiedBy(book)) { System.out.println(book); } } }
}
|
总结
规约模式适用于内存过滤,不同的单约束条件可以相互组合,但是对每一种单约束都需要创建对应的约束类
参考
Martin Fowler & Eric Evans: Specifications
Specification Pattern in Java