開放封閉原則( Open Closed Principle, OCP)

目錄

design pattern 以java 為例

開放封閉原則 (OCP: Open Closed Principle)

通常簡稱為 SOLID 原則中的 “O”。這個原則指出軟件實體(類、模塊、函數等)應該對擴展開放,對修改封閉

實現方式

  • interface
  • abstract

code 翻立

基礎資料

加設現在要做一個商品的filter, 並且依顏色形狀等做分類,我們會有基礎class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
enum Color {
RED, GREEN, BLUE
}

enum Size {
SMALL, MEDIUM, LARGE, YUGE
}

class Product {
public String name;
public Color color;
public Size size;

public Product(String name, Color color, Size size) {
this.name = name;
this.color = color;
this.size = size;
}
}

非OCP 設計原則,硬解

我們可以把每個條件去做比較,但可以而知隨著參數變多,條件變多,就會變成可怕的code

1
2
3
4
5
6
7
8
9
10
11
12
13
class ProductFilter {
public Stream<Product> filterByColor(List<Product> products, Color color) {
return products.stream().filter(p -> p.color == color);
}

public Stream<Product> filterBySize(List<Product> products, Size size) {
return products.stream().filter(p -> p.size == size);
}

public Stream<Product> filterBySizeAndColor(List<Product> products, Size size, Color color) {
return products.stream().filter(p -> p.size == size && p.color == color);
}
}

OCP 設計模式

interface

我們可以建立滿足條件及過濾的interface

1
2
3
4
5
6
7
8
interface Specification<T> {
boolean isSatisfied(T item);
}

interface Filter<T> {
Stream<T> filter(List<T> items, Specification<T> spec);
}

實作

中間時做一個and 來做連接

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
class ColorSpecification implements Specification<Product> {
private Color color;

public ColorSpecification(Color color) {
this.color = color;
}

@Override
public boolean isSatisfied(Product p) {
return p.color == color;
}
}

class SizeSpecification implements Specification<Product> {
private Size size;

public SizeSpecification(Size size) {
this.size = size;
}

@Override
public boolean isSatisfied(Product p) {
return p.size == size;
}
}

class AndSpecification<T> implements Specification<T> {
private Specification<T> first, second;

public AndSpecification(Specification<T> first, Specification<T> second) {
this.first = first;
this.second = second;
}

@Override
public boolean isSatisfied(T item) {
return first.isSatisfied(item) && second.isSatisfied(item);
}

}

或是可以透過list 結合stream

1
2
3
4
5
6
class BetterFilter implements Filter<Product> {
@Override
public Stream<Product> filter(List<Product> items, Specification<Product> spec) {
return items.stream().filter(p -> spec.isSatisfied(p));
}
}

結果

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
class OCPDemo {
public static void main(String[] args) {
Product apple = new Product("Apple", Color.GREEN, Size.SMALL);
Product tree = new Product("Tree", Color.GREEN, Size.LARGE);
Product house = new Product("House", Color.BLUE, Size.LARGE);

List<Product> products = List.of(apple, tree, house);

ProductFilter pf = new ProductFilter();
System.out.println("Green products (old):");
pf.filterByColor(products, Color.GREEN)
.forEach(p -> System.out.println(" - " + p.name + " is green"));

// ^^ BEFORE

// vv AFTER
BetterFilter bf = new BetterFilter();
System.out.println("Green products (new):");
bf.filter(products, new ColorSpecification(Color.GREEN))
.forEach(p -> System.out.println(" - " + p.name + " is green"));

System.out.println("Large products:");
bf.filter(products, new SizeSpecification(Size.LARGE))
.forEach(p -> System.out.println(" - " + p.name + " is large"));

System.out.println("Large blue items:");
bf.filter(products,
new AndSpecification<>(
new ColorSpecification(Color.BLUE),
new SizeSpecification(Size.LARGE)
))
.forEach(p -> System.out.println(" - " + p.name + " is large and blue"));
}
}
1
2
3
4
5
6
7
8
9
10
11
Green products (old):
- Apple is green
- Tree is green
Green products (new):
- Apple is green
- Tree is green
Large products:
- Tree is large
- House is large
Large blue items:
- House is large and blue

結論

這種做法可以有效地重新利用,避免過多的重複程式產生


開放封閉原則( Open Closed Principle, OCP)
https://shengshengyang.github.io/2024/03/30/ocp/
作者
Dean Yang
發布於
2024年3月30日
許可協議