规约模式(Specification Pattern)

规约模式(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
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
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) };

// 名称包含java并且价格在45~60之间
// 名称包含Mysql
// 名称包含Spring并且不是在2007出版
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