-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpatterns.txt
More file actions
52 lines (52 loc) · 7.82 KB
/
patterns.txt
File metadata and controls
52 lines (52 loc) · 7.82 KB
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
1. Порождающие паттерны.
>> Используется паттерн Factory (класс UnitFactory в Units.py). Производит юнитов и ставит их в клетки. Использование
обсновано тем, что нужно инкапуслировать сложный процесс создния юнитов и разные проверки в отдельный класс.
2. Структурные паттерны.
>> Используется паттерн компоновщик. Ему соответствует древовидная система юнитов, в котором мораль каждого зависит от
того, сколько у него потомков и соседей в дереве. Метод cascade_morale_decrease_after_death(value, depth) вызывается
рекурсивно по поддереву, что удобно.
>> Декоратор: в использовании отказано
Паттерн не пригодился. Его смысл в том, чтобы обхекты можно было наделять произвольным подмножеством свойств из
некоторого множества свойств (избежав при этом большого количества кода). Так как у нас нет таких
больших множеств свойств, которыми может обладать какой-то объект, то и декоратор нигде не нужен. Юниты обладают
фиксированным набором (каждый - своим) и он описан в файле - наделять какими-то еще свойствами или эффектами возможности
нет, да и реализовывать это было бы удобнее через введение дополнительных полей у юнитов прямо во время рантайма.
Питон дает возможность это сделать, но и даже в с++ можно было бы просто использовать map.
Другие объекты - UnitFactory, Strategy (разного рода) тоже не кастомизируются болшим числом способов.
>> Адаптер/прокси:
Эти паттерны нужны, чтобы 2 объекта общались друг между другом. В случае адаптера - создается обертка одного из них,
берущая на себя роль переводчика, в случае прокси - берущая на себя роль посредника, который может что-то сделать до и
после вызова, разграничить права и при этом сохраняет интерфейс. Цель состоит в том, чтобы не менять существующие
классы и уменьшить дублиование кода.
Однако в нашем проекте нет объектов, которые должны быть связаны, но нуждаются в "переводе интерфейсов" или обработке
запросов, идущих в одну из сторон. Юниты связаны между собой и все являются одного класса; классы Game, Board,
CPanel, ... создаются в единственном экземпляре и имеют ссылки друг на друга, а также совместимый интерфейс,
так что нет смысла во введении дополнительной логики в их обращении друг к другу.
3. Поведенческие паттерны.
>> Используется паттерн Command.
Для того чтобы отображать действия боевого характера действия вроде атаки и хила соответствуют объектам, описанным в
BattleActions.py. При этом базовый класс - BattleAction имеет 2 обязательных поля - источник (source) и цель (dest).
Например, в случае атаки, source - атакующий, dest - получающий. Юниты имеют метод accept_battle_action, который как
раз вызывается у dest и меняет его состояние соответствующе. У класса Command также есть метод log(), выводящий в stdout
информацию о действии (source, dest, type).
Этот паттерн здесь помогает удобно описывать разные виды боевых действий в виде системы классов и наделять их разными
свойствами, вместо хардкодинга изменения состояния прямо в функцию, совершающую действие тем или иным юнитом. Также
он помогает в будущем расшиить систему, добавляя новые боевые действия.
>> CoR не используется
В нашей архитектуре нет сложных запросов, которые нужно было бы разрешать с помощью нескольких обработчиков. Как уже
было сказано в Adapter/Proxy, у нас фактически вообще нет запросов, нуждающихся в какой-то сложной надстроенной логике,
без которой дублировался бы код.
>> Visitor не используется
Вот этот паттерн нужен, когда есть много объектов, и все нужно обойти и как-то обработать, не меняя особо интерфейс
их классов. У нас, есть ровно одно место где есть много объектов, которые нужно обработать - массив юнитов
(board.units_array). Но юниты все одного класса, поэтому нужный метод можно просто добавить или даже просто посмотреть
на свойства, так как они все публичные (питон). Так, например, я в какой-то момент добавил метод act() в класс Unit,
чтобы нейтралы могли что-то делать, так что после этого метод act() в neutralPlayer стал вызывать его у всех нейтральных
юнитов.
>> Observer не используется.
Этот паттерн нужен, когда некоторые объекты должны реагировать на события, происходящие в других. Здесь события
происходят только между юнитами, ну и соответственно, внутри них же эта реакция и описана. Нет необходимости в такой
сложной абстракции, так как все реакции на события программируются просто. Например, если юнит умирает, обходим все
его поддерево и уменьшаем мораль (если бы мы заставили юнитов наблюдать за смертью каждого родителя, логика бы только
усложнилась). То же самое, если юнит обретает начальника, можно просто пройтись по нужным вершинам дерева, поменять
значения.