Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like AbstractWorkflow often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use AbstractWorkflow, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
46 | abstract class AbstractWorkflow implements WorkflowInterface |
||
47 | { |
||
48 | /** |
||
49 | * @var string |
||
50 | */ |
||
51 | const CURRENT_STEPS = 'currentSteps'; |
||
52 | |||
53 | /** |
||
54 | * @var string |
||
55 | */ |
||
56 | const HISTORY_STEPS = 'historySteps'; |
||
57 | |||
58 | /** |
||
59 | * @var WorkflowContextInterface |
||
60 | */ |
||
61 | protected $context; |
||
62 | |||
63 | /** |
||
64 | * @var ConfigurationInterface |
||
65 | */ |
||
66 | protected $configuration; |
||
67 | |||
68 | |||
69 | /** |
||
70 | * @var TypeResolverInterface |
||
71 | */ |
||
72 | protected $typeResolver; |
||
73 | |||
74 | /** |
||
75 | * Логер |
||
76 | * |
||
77 | * @var LoggerInterface |
||
78 | */ |
||
79 | protected $log; |
||
80 | |||
81 | /** |
||
82 | * Резолвер для создания провайдеров отвечающих за исполнение функций, проверку условий, выполнение валидаторов и т.д. |
||
83 | * |
||
84 | * @var string |
||
85 | */ |
||
86 | protected $defaultTypeResolverClass = TypeResolver::class; |
||
87 | |||
88 | /** |
||
89 | * Карта переходов состояния процесса workflow |
||
90 | * |
||
91 | * @var null|array |
||
92 | */ |
||
93 | protected $mapEntryState; |
||
94 | |||
95 | /** |
||
96 | * Менеджер движков |
||
97 | * |
||
98 | * @var EngineManagerInterface |
||
99 | */ |
||
100 | protected $engineManager; |
||
101 | |||
102 | /** |
||
103 | * AbstractWorkflow constructor. |
||
104 | * |
||
105 | * @throws InternalWorkflowException |
||
106 | */ |
||
107 | 19 | public function __construct() |
|
112 | |||
113 | /** |
||
114 | * Устанавливает менеджер движков |
||
115 | * |
||
116 | * @return EngineManagerInterface |
||
117 | */ |
||
118 | 19 | public function getEngineManager() |
|
127 | |||
128 | /** |
||
129 | * Возвращает менеджер движков |
||
130 | * |
||
131 | * @param EngineManagerInterface $engineManager |
||
132 | * |
||
133 | * @return $this |
||
134 | */ |
||
135 | public function setEngineManager(EngineManagerInterface $engineManager) |
||
141 | |||
142 | /** |
||
143 | * Инициация карты переходов состояния процесса workflow |
||
144 | */ |
||
145 | 19 | protected function initMapEntryState() |
|
167 | |||
168 | /** |
||
169 | * Инициализация системы логирования |
||
170 | * |
||
171 | * @throws InternalWorkflowException |
||
172 | */ |
||
173 | 19 | protected function initLoger() |
|
181 | |||
182 | /** |
||
183 | * Инициализация workflow. Workflow нужно иницаилизровать прежде, чем выполнять какие либо действия. |
||
184 | * Workflow может быть инициализированно только один раз |
||
185 | * |
||
186 | * @param string $workflowName Имя workflow |
||
187 | * @param integer $initialAction Имя первого шага, с которого начинается workflow |
||
188 | * @param TransientVarsInterface $inputs Данные введеные пользователем |
||
189 | * |
||
190 | * @return integer |
||
191 | * |
||
192 | * @throws InternalWorkflowException |
||
193 | * @throws InvalidActionException |
||
194 | * @throws InvalidRoleException |
||
195 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
196 | * @throws InvalidArgumentException |
||
197 | * @throws WorkflowException |
||
198 | */ |
||
199 | 19 | public function initialize($workflowName, $initialAction, TransientVarsInterface $inputs = null) |
|
249 | |||
250 | |||
251 | /** |
||
252 | * Проверка того что данные могут быть использованы в цикле |
||
253 | * |
||
254 | * @param $data |
||
255 | * |
||
256 | * @throws InvalidArgumentException |
||
257 | */ |
||
258 | protected function validateIterateData($data) |
||
265 | |||
266 | |||
267 | |||
268 | /** |
||
269 | * @param $id |
||
270 | * @param $inputs |
||
271 | * |
||
272 | * @return array |
||
273 | * |
||
274 | */ |
||
275 | public function getAvailableActions($id, TransientVarsInterface $inputs = null) |
||
343 | |||
344 | /** |
||
345 | * Создает хранилище переменных |
||
346 | * |
||
347 | * @param $class |
||
348 | * |
||
349 | * @return TransientVarsInterface |
||
350 | */ |
||
351 | protected function transientVarsFactory($class = BaseTransientVars::class) |
||
356 | |||
357 | /** |
||
358 | * |
||
359 | * |
||
360 | * Осуществляет переходл в новое состояние, для заданного процесса workflow |
||
361 | * |
||
362 | * @param integer $entryId id запущенного процесса workflow |
||
363 | * @param integer $actionId id действия, доступного та текущем шаеге процессса workflow |
||
364 | * @param TransientVarsInterface $inputs Входные данные для перехода |
||
365 | * |
||
366 | * @return void |
||
367 | * |
||
368 | * @throws WorkflowException |
||
369 | * @throws InvalidActionException |
||
370 | * @throws InvalidArgumentException |
||
371 | * @throws InternalWorkflowException |
||
372 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
373 | */ |
||
374 | 8 | public function doAction($entryId, $actionId, TransientVarsInterface $inputs = null) |
|
460 | |||
461 | /** |
||
462 | * @param ActionDescriptor $action |
||
463 | * @param $id |
||
464 | * |
||
465 | * @return void |
||
466 | * |
||
467 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
468 | * @throws InvalidArgumentException |
||
469 | * @throws InternalWorkflowException |
||
470 | */ |
||
471 | protected function checkImplicitFinish(ActionDescriptor $action, $id) |
||
499 | |||
500 | |||
501 | |||
502 | /** |
||
503 | * |
||
504 | * Check if the state of the specified workflow instance can be changed to the new specified one. |
||
505 | * |
||
506 | * @param integer $id The workflow instance id. |
||
507 | * @param integer $newState The new state id. |
||
508 | * |
||
509 | * @return boolean true if the state of the workflow can be modified, false otherwise. |
||
510 | * |
||
511 | * @throws InternalWorkflowException |
||
512 | */ |
||
513 | 16 | public function canModifyEntryState($id, $newState) |
|
522 | |||
523 | |||
524 | /** |
||
525 | * |
||
526 | * Возвращает коллекцию объектов описывающие состояние для текущего экземпляра workflow |
||
527 | * |
||
528 | * @param integer $entryId id экземпляра workflow |
||
529 | * |
||
530 | * @return SplObjectStorage|StepInterface[] |
||
531 | * |
||
532 | * @throws InternalWorkflowException |
||
533 | */ |
||
534 | public function getCurrentSteps($entryId) |
||
538 | |||
539 | /** |
||
540 | * Возвращает информацию о том в какие шаги, были осуществленны переходы, для процесса workflow с заданным id |
||
541 | * |
||
542 | * @param integer $entryId уникальный идентификатор процесса workflow |
||
543 | * |
||
544 | * @return StepInterface[]|SplObjectStorage список шагов |
||
545 | * |
||
546 | * @throws InternalWorkflowException |
||
547 | */ |
||
548 | public function getHistorySteps($entryId) |
||
552 | |||
553 | /** |
||
554 | * Получение шагов информации о шагах процесса workflow |
||
555 | * |
||
556 | * @param $entryId |
||
557 | * @param $type |
||
558 | * |
||
559 | * @return Spi\StepInterface[]|SplObjectStorage |
||
560 | * |
||
561 | * @throws InternalWorkflowException |
||
562 | */ |
||
563 | protected function getStepFromStorage($entryId, $type) |
||
583 | |||
584 | |||
585 | |||
586 | /** |
||
587 | * |
||
588 | * |
||
589 | * Modify the state of the specified workflow instance. |
||
590 | * @param integer $id The workflow instance id. |
||
591 | * @param integer $newState the new state to change the workflow instance to. |
||
592 | * |
||
593 | * @throws InvalidArgumentException |
||
594 | * @throws InvalidEntryStateException |
||
595 | * @throws InternalWorkflowException |
||
596 | */ |
||
597 | 16 | public function changeEntryState($id, $newState) |
|
635 | |||
636 | |||
637 | |||
638 | |||
639 | |||
640 | /** |
||
641 | * Проверяет имеет ли пользователь достаточно прав, что бы иниициировать вызываемый процесс |
||
642 | * |
||
643 | * @param string $workflowName имя workflow |
||
644 | * @param integer $initialAction id начального состояния |
||
645 | * @param TransientVarsInterface $inputs |
||
646 | * |
||
647 | * @return bool |
||
648 | * |
||
649 | * @throws InvalidArgumentException |
||
650 | * @throws WorkflowException |
||
651 | * @throws InternalWorkflowException |
||
652 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
653 | */ |
||
654 | public function canInitialize($workflowName, $initialAction, TransientVarsInterface $inputs = null) |
||
697 | |||
698 | |||
699 | /** |
||
700 | * Проверяет имеет ли пользователь достаточно прав, что бы иниициировать вызываемый процесс |
||
701 | * |
||
702 | * @param string $workflowName имя workflow |
||
703 | * @param integer $initialAction id начального состояния |
||
704 | * @param TransientVarsInterface $transientVars |
||
705 | * |
||
706 | * @param PropertySetInterface $ps |
||
707 | * |
||
708 | * @return bool |
||
709 | * |
||
710 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
711 | * @throws InvalidActionException |
||
712 | * @throws InternalWorkflowException |
||
713 | * @throws WorkflowException |
||
714 | */ |
||
715 | 19 | protected function canInitializeInternal($workflowName, $initialAction, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
|
742 | |||
743 | /** |
||
744 | * Возвращает резолвер |
||
745 | * |
||
746 | * @return TypeResolverInterface |
||
747 | */ |
||
748 | 19 | public function getResolver() |
|
761 | |||
762 | /** |
||
763 | * Возвращает хранилище состояния workflow |
||
764 | * |
||
765 | * @return WorkflowStoreInterface |
||
766 | * |
||
767 | * @throws InternalWorkflowException |
||
768 | */ |
||
769 | 19 | protected function getPersistence() |
|
773 | |||
774 | /** |
||
775 | * Получить конфигурацию workflow. Метод также проверяет была ли иницилазированн конфигурация, если нет, то |
||
776 | * инициализирует ее. |
||
777 | * |
||
778 | * Если конфигурация не была установленна, то возвращает конфигурацию по умолчанию |
||
779 | * |
||
780 | * @return ConfigurationInterface|DefaultConfiguration Конфигурация которая была установленна |
||
781 | * |
||
782 | * @throws InternalWorkflowException |
||
783 | */ |
||
784 | 19 | public function getConfiguration() |
|
800 | |||
801 | /** |
||
802 | * @return LoggerInterface |
||
803 | */ |
||
804 | 17 | public function getLog() |
|
808 | |||
809 | /** |
||
810 | * @param LoggerInterface $log |
||
811 | * |
||
812 | * @return $this |
||
813 | * |
||
814 | * @throws InternalWorkflowException |
||
815 | */ |
||
816 | public function setLog($log) |
||
830 | |||
831 | |||
832 | /** |
||
833 | * Get the workflow descriptor for the specified workflow name. |
||
834 | * |
||
835 | * @param string $workflowName The workflow name. |
||
836 | * @return WorkflowDescriptor |
||
837 | * |
||
838 | * @throws InternalWorkflowException |
||
839 | */ |
||
840 | public function getWorkflowDescriptor($workflowName) |
||
850 | |||
851 | |||
852 | /** |
||
853 | * Executes a special trigger-function using the context of the given workflow instance id. |
||
854 | * Note that this method is exposed for Quartz trigger jobs, user code should never call it. |
||
855 | * |
||
856 | * @param integer $id The workflow instance id |
||
857 | * @param integer $triggerId The id of the special trigger-function |
||
858 | * |
||
859 | * @throws InvalidArgumentException |
||
860 | * @throws WorkflowException |
||
861 | * @throws InternalWorkflowException |
||
862 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
863 | */ |
||
864 | public function executeTriggerFunction($id, $triggerId) |
||
889 | |||
890 | |||
891 | /** |
||
892 | * @param WorkflowDescriptor $wf |
||
893 | * @param StepInterface $step |
||
894 | * @param TransientVarsInterface $transientVars |
||
895 | * @param PropertySetInterface $ps |
||
896 | * |
||
897 | * @return array |
||
898 | * |
||
899 | * @throws InternalWorkflowException |
||
900 | * @throws WorkflowException |
||
901 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
902 | */ |
||
903 | protected function getAvailableActionsForStep(WorkflowDescriptor $wf, StepInterface $step, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
||
946 | |||
947 | /** |
||
948 | * @param ConfigurationInterface $configuration |
||
949 | * |
||
950 | * @return $this |
||
951 | */ |
||
952 | 19 | public function setConfiguration(ConfigurationInterface $configuration) |
|
958 | |||
959 | /** |
||
960 | * Возвращает состояние для текущего экземпляра workflow |
||
961 | * |
||
962 | * @param integer $id id экземпляра workflow |
||
963 | * @return integer id текущего состояния |
||
964 | * |
||
965 | * @throws InternalWorkflowException |
||
966 | */ |
||
967 | public function getEntryState($id) |
||
983 | |||
984 | |||
985 | /** |
||
986 | * Настройки хранилища |
||
987 | * |
||
988 | * @return array |
||
989 | * |
||
990 | * @throws InternalWorkflowException |
||
991 | */ |
||
992 | public function getPersistenceProperties() |
||
996 | |||
997 | |||
998 | /** |
||
999 | * Get the PropertySet for the specified workflow instance id. |
||
1000 | * @param integer $id The workflow instance id. |
||
1001 | * |
||
1002 | * @return PropertySetInterface |
||
1003 | * @throws InternalWorkflowException |
||
1004 | */ |
||
1005 | public function getPropertySet($id) |
||
1021 | |||
1022 | /** |
||
1023 | * @return string[] |
||
1024 | * |
||
1025 | * @throws InternalWorkflowException |
||
1026 | */ |
||
1027 | public function getWorkflowNames() |
||
1038 | |||
1039 | /** |
||
1040 | * @param TypeResolverInterface $typeResolver |
||
1041 | * |
||
1042 | * @return $this |
||
1043 | */ |
||
1044 | public function setTypeResolver(TypeResolverInterface $typeResolver) |
||
1050 | |||
1051 | |||
1052 | /** |
||
1053 | * Get a collection (Strings) of currently defined permissions for the specified workflow instance. |
||
1054 | * @param integer $id id the workflow instance id. |
||
1055 | * @param TransientVarsInterface $inputs inputs The inputs to the workflow instance. |
||
1056 | * |
||
1057 | * @return array A List of permissions specified currently (a permission is a string name). |
||
1058 | * |
||
1059 | */ |
||
1060 | public function getSecurityPermissions($id, TransientVarsInterface $inputs = null) |
||
1121 | |||
1122 | |||
1123 | /** |
||
1124 | * Get the name of the specified workflow instance. |
||
1125 | * |
||
1126 | * @param integer $id the workflow instance id. |
||
1127 | * |
||
1128 | * @return string |
||
1129 | * |
||
1130 | * @throws InternalWorkflowException |
||
1131 | */ |
||
1132 | public function getWorkflowName($id) |
||
1151 | |||
1152 | /** |
||
1153 | * Удаляет workflow |
||
1154 | * |
||
1155 | * @param string $workflowName |
||
1156 | * |
||
1157 | * @return bool |
||
1158 | * |
||
1159 | * @throws InternalWorkflowException |
||
1160 | */ |
||
1161 | public function removeWorkflowDescriptor($workflowName) |
||
1165 | |||
1166 | /** |
||
1167 | * @param $workflowName |
||
1168 | * @param WorkflowDescriptor $descriptor |
||
1169 | * @param $replace |
||
1170 | * |
||
1171 | * @return bool |
||
1172 | * |
||
1173 | * @throws InternalWorkflowException |
||
1174 | */ |
||
1175 | public function saveWorkflowDescriptor($workflowName, WorkflowDescriptor $descriptor, $replace) |
||
1181 | |||
1182 | |||
1183 | /** |
||
1184 | * Query the workflow store for matching instances |
||
1185 | * |
||
1186 | * @param WorkflowExpressionQuery $query |
||
1187 | * |
||
1188 | * @return array |
||
1189 | * |
||
1190 | * @throws InternalWorkflowException |
||
1191 | */ |
||
1192 | public function query(WorkflowExpressionQuery $query) |
||
1196 | |||
1197 | /** |
||
1198 | * @return string |
||
1199 | */ |
||
1200 | 19 | public function getDefaultTypeResolverClass() |
|
1204 | |||
1205 | /** |
||
1206 | * @param string $defaultTypeResolverClass |
||
1207 | * |
||
1208 | * @return $this |
||
1209 | */ |
||
1210 | public function setDefaultTypeResolverClass($defaultTypeResolverClass) |
||
1216 | |||
1217 | |||
1218 | /** |
||
1219 | * @return WorkflowContextInterface |
||
1220 | */ |
||
1221 | 19 | public function getContext() |
|
1225 | } |
||
1226 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.