Многие из моих коллег рассказали о новых захватывающих технологиях и инфраструктурах, которые помогут вам в вашем программировании. Но в настоящее время я думаю, что важно вернуться назад и охватить важные основы программирования. На протяжении всей моей карьеры программиста одним из самых распространенных анти-паттернов, которые я видел, является «Строковое состояние». Он появился в той или иной форме в каждом проекте, над которым я работал. Проще говоря, анти-шаблон String State — это использование строки Java для представления состояния объекта. Конечно, строковые представления состояний важны. Вы хотите, чтобы код был читабельным, вы хотите, чтобы в отчетах было доступно читаемое значение для отчетов, и вы хотите, чтобы читаемое представление состояния отображалось на вашем интерфейсе. Все это хорошие вещи, и у вас должно быть некоторое строковое представление вашего состояния, но не допускайте, чтобы string была полной реализацией вашего состояния. Возьмем, к примеру, эту упрощенную версию корзины для покупок:
01
02
03
04
05
06
07
08
09
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
|
package stringstate; public class Item { private String name; private int price; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } } package stringstate; public interface OrderState { public static String OPEN = "Open" ; public static String CLOSED = "Closed" ; } package stringstate; import java.util.ArrayList; import java.util.List; public class Order extends Item implements OrderState { private List items = new ArrayList(); private String orderState; public List getItems() { return items; } protected void setItems(List items) { this.items = items; } public String getOrderState() { return orderState; } public void setOrderState(String orderState) { this.orderState = orderState; } public void addItem(Item anItem){ getItems().add(anItem); } } |
В этом примере состояние используется без бизнес-логики. В этом случае состояние строки не будет вредным. Однако ни один проект не является таким простым. Даже если состояние начинается только как отображение / сохранение, вы в конечном итоге будете использовать его для бизнес-логики. Поверьте мне — если состояние объекта достаточно важно для хранения, то в конечном итоге вам придется принимать решения с ним. Так что теперь ваша упрощенная корзина должна запретить людям добавлять товары, если она закрыта. Легко: добавить новый метод и изменить addItem .
01
02
03
04
05
06
07
08
09
10
11
|
public void addItem(Item anItem){ if (canModify()){ getItems().add(anItem); } else { throw new IllegalStateException( "Cannot add items to a closed order" ); } } public boolean canModify(){ return getOrderState().equals(OPEN); } |
Пока что не все так плохо. Вы спрашиваете, почему я так расстроен? Потому что теперь вы хотите добавить 2 новых состояния New и Finalized, а также дать пользователю возможность повторно открыть не завершенный заказ. Конечно, новый метод, модификация canModify , проверка setOrderState, и мы уже в пути. Но подождите, теперь пользователь хочет 2 завершенных состояния: отправлено и отменено . Еще несколько настроек, верно? Теперь ваш код заказа изобилует словами «if», касающимися состояний:
01
02
03
04
05
06
07
08
09
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
|
package stringstate; import java.util.ArrayList; import java.util.List; public class Order extends Item implements OrderState { private List items = new ArrayList(); private String orderState; public List getItems() { return items; } protected void setItems(List items) { this.items = items; } public String getOrderState() { return orderState; } public void setOrderState(String orderState) { if (!isFinalized()){ this.orderState = orderState; } } public void addItem(Item anItem){ if (canModify()){ getItems().add(anItem); } else { throw new IllegalStateException( "Cannot add items to a closed order" ); } } public boolean canModify(){ return getOrderState().equals(OPEN); } public void reOpen(){ if (!isFinalized()){ setOrderState(OPEN); } } public boolean isFinalized(){ return getOrderState().equals(SHIPPED) || getOrderState().equals(CANCELED); } } |
К сожалению, мы забыли добавить новое состояние в canModify () . Теперь возьмем пример состояния стратегии, в котором вся логика состояния делегирована объекту, умнее строки. Первое преимущество — сделать всю логику в заказе о заказе, а не о состоянии заказа. Следующее преимущество заключается в том, что каждый раз, когда вы добавляете новое решение, основанное на состоянии, вы можете применять его абстрактно для принудительной реализации во всех состояниях или для обеспечения реализации по умолчанию. Порядок:
01
02
03
04
05
06
07
08
09
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
|
package strategystate; import java.util.ArrayList; import java.util.List; public class Order { private List items = new ArrayList(); private OrderState orderState; public List getItems() { return items; } protected void setItems(List items) { this.items = items; } public OrderState getOrderState() { return orderState; } public void setOrderState(OrderState orderState) { if (!getOrderState().isFinalized()){ this.orderState = orderState; } } public void addItem(Item anItem){ if (getOrderState().canModify()){ getItems().add(anItem); } else { throw new IllegalStateException( "Cannot add items to a closed order" ); } } public void reOpen(){ if (!getOrderState().isFinalized()){ setOrderState(OrderState.OPEN); } } } public enum OrderState { NEW( "New" , true , false ), OPEN( "Open" , true , false ), CLOSED( "Closed" , false , false ), SHIPPED( "Shipped" , false , true ), CANCELED( "Canceled" , false , true ); protected String name; protected boolean finalized; protected boolean modify; private OrderState(String name, boolean canModify, boolean isFinalized){ this.name = name; this.finalized= isFinalized; this.modify = canModify; } public boolean canModify(){ return modify; } public boolean isFinalized(){ return finalized; } public String getName(){ return name; } } |
Чем больше состояний и больше логики у вас основано на состоянии, тем больше вы получаете выгоды от состояния стратегии. Стратегия State сохраняет ваши основные бизнес-объекты свободными от растущих «операторов if» на основе списков состояний. Кроме того, вы можете сохранить все удобства строки. Различие может быть небольшим в этом примере, но с ростом сложности домена увеличивается и преимущество состояния стратегии. Кроме того, над каким проектом вы когда-либо работали, где сложность не росла? Я рекомендую всегда начинать с состояния стратегии. Накладные расходы невелики, и вы будете благодарны, когда сложность проекта возрастет.