Игра жизни Конвея увлекательна как с функциональной, так и с технической точки зрения.
Это может объяснить, почему это часто используется для отступления кода . Кодовые отступления — интересный способ изучения.
Удивительно, как работа с новыми парами дает вам новые идеи практически каждый раз.
На последнем ретрите кода, который я посетил, одна из моих пар предложила использовать шаблон Flyweight для ячеек:
Flyweight — это общий объект, который может использоваться одновременно в нескольких контекстах. Flyweight действует как независимый объект в каждом контексте — он неотличим от экземпляра объектов, которые не являются общими.
Когда вышла книга Design Patterns (которая содержит приведенную выше цитату), я помню, что у меня было много моментов ага. Было так круто увидеть все эти шаблоны, которые я использовал раньше, и, наконец, назвать их, чтобы я мог обсудить их с коллегами более эффективно!
У меня не было ага, когда я читал о весе в полете, однако. Пример, приведенный в книге о совместном использовании символьных объектов в текстовом редакторе, в то время казался немного надуманным. Однако этот пример мало чем отличается от ячеек в сетке Game of Life, поэтому я с радостью согласился с идеей моей пары исследовать применимость шаблона в этом контексте.
После того, как отступление кода было закончено, я немного подумал над этим шаблоном. (Это обычно, когда отступление кода действительно начинает окупаться.)
На самом деле мы все время используем потенциальный вес в полете: логические значения. Логический класс — это класс только с двумя экземплярами, и эти экземпляры могут легко использоваться совместно. В Java это не так: new Boolean(true) != new Boolean(true)
. Однако Boolean
класс предоставляет две константы, TRUE
и FALSE
, для экземпляров, которые вы можете использовать для совместного использования.
Это заставило меня задуматься об использовании Enum
s для навесок. Большую часть времени я использую перечисления для группировки связанных, но взаимоисключающих констант, например, дней недели. Однако Enum
s в Java может определять методы:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public enum Cell { ALIVE( true ), DEAD( false ); private final boolean alive; private Cell( boolean alive) { this .alive = alive; } public boolean isAlive() { return alive; } public Cell evolve( int numLiveNeighbors) { boolean aliveInNextGeneration = alive ? 2 <= numLiveNeighbors && numLiveNeighbors <= 3 : numLiveNeighbors == 3 ; return aliveInNextGeneration ? ALIVE : DEAD; } } |
Одна из забавных частей отступлений кода заключается в том, что на некоторых сессиях у вас будут ограничения на работу. Такие ограничения заставляют вас быть более креативными и думать не только о тех методах, которые вы обычно используете.
Одно ограничение, которое интересно в этом контексте, состоит в том, чтобы не использовать какие-либо условия, такие как операторы if
или switch
или троичные операторы. Идея этого ограничения состоит в том, чтобы заставить вас заменить условные выражения полиморфизмом , делая вашу программу более объектно-ориентированной.
Единственный способ сохранить текущее перечисление Cell
и не использовать условные выражения — это представить карту:
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
|
public enum Cell { ALIVE( true ), DEAD( false ); private final boolean alive; private static final Map<Boolean, Map<Integer, Cell>> NEXT = new HashMap<>(); static { Map<Integer, Cell> dead = new HashMap<>(); dead.put( 0 , DEAD); dead.put( 1 , DEAD); dead.put( 2 , DEAD); dead.put( 3 , ALIVE); dead.put( 4 , DEAD); dead.put( 5 , DEAD); dead.put( 6 , DEAD); dead.put( 7 , DEAD); dead.put( 8 , DEAD); dead.put( 9 , DEAD); NEXT.put( false , dead); Map<Integer, Cell> alive = new HashMap<>(); alive.put( 0 , DEAD); alive.put( 1 , DEAD); alive.put( 2 , ALIVE); alive.put( 3 , ALIVE); alive.put( 4 , DEAD); alive.put( 5 , DEAD); alive.put( 6 , DEAD); alive.put( 7 , DEAD); alive.put( 8 , DEAD); alive.put( 9 , DEAD); NEXT.put( true , alive); } private Cell( boolean alive) { this .alive = alive; } public boolean isAlive() { return alive; } public Cell evolve( int numLiveNeighbors) { return NEXT.get(alive).get(numLiveNeighbors); } } |
Этот подход работает, но он не очень элегантен и ломается, когда число возможностей растет. Очевидно, нам нужна лучшая альтернатива.
Единственный способ избавиться от условного — избавиться от логического состояния ячейки. Это означает, что нам нужно иметь разные классы для двух экземпляров, чтобы тип неявно воплощал состояние. Это, в свою очередь, означает, что нам нужна фабрика, чтобы скрыть эти классы от клиента:
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
|
public interface Cell { boolean isAlive(); Cell evolve( int numLiveNeighbors); } public class CellFactory { private static final Map<Boolean, Cell> CELLS = new HashMap<>(); static { CELLS.put( false , new DeadCell()); CELLS.put( true , new AliveCell()); } public static Cell dead() { return cell( false ); } public static Cell alive() { return cell( true ); } static Cell cell( boolean alive) { return CELLS.get(alive); } } class DeadCell implements Cell { @Override public boolean isAlive() { return false ; } @Override public Cell evolve( int numLiveNeighbors) { return CellFactory.cell(numLiveNeighbors == 3 ); } } class AliveCell implements Cell { @Override public boolean isAlive() { return true ; } @Override public Cell evolve( int numLiveNeighbors) { return CellFactory.cell(numLiveNeighbors == 2 || numLiveNeighbors == 3 ); } } |
Действительно, когда вы посмотрите шаблон Flyweight, вы увидите, что предлагаемая структура содержит фабрику flyweight, которая создает экземпляры конкретных классов flyweight, которые реализуют общий интерфейс flyweight.
Благодаря отступлению от кода и моему партнеру я теперь знаю почему.
Ссылка: | Игра жизни Конвея и шаблон Flyweight от нашего партнера JCG Ремона Синнема в блоге по разработке безопасного программного обеспечения . |