Complex classes like Transition 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 Transition, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
37 | class Transition { |
||
38 | const RULE_TRUE = '\izzum\rules\TrueRule'; |
||
39 | const RULE_FALSE = '\izzum\rules\FalseRule'; |
||
40 | const RULE_EMPTY = ''; |
||
41 | const COMMAND_NULL = '\izzum\command\NullCommand'; |
||
42 | const COMMAND_EMPTY = ''; |
||
43 | const CALLABLE_NULL = null; |
||
44 | |||
45 | /** |
||
46 | * the state this transition starts from |
||
47 | * |
||
48 | * @var State |
||
49 | */ |
||
50 | protected $state_from; |
||
51 | |||
52 | /** |
||
53 | * the state this transition points to |
||
54 | * |
||
55 | * @var State |
||
56 | */ |
||
57 | protected $state_to; |
||
58 | |||
59 | /** |
||
60 | * an event code that can trigger this transitions |
||
61 | * |
||
62 | * @var string |
||
63 | */ |
||
64 | protected $event; |
||
65 | |||
66 | /** |
||
67 | * The fully qualified Rule class name of the |
||
68 | * Rule to be applied to check if we can transition. |
||
69 | * This can actually be a ',' seperated string of multiple rules. |
||
70 | * |
||
71 | * @var string |
||
72 | */ |
||
73 | protected $rule; |
||
74 | |||
75 | /** |
||
76 | * the fully qualified Command class name of the Command to be |
||
77 | * executed as part of the transition logic. |
||
78 | * This can actually be a ',' seperated string of multiple commands. |
||
79 | * |
||
80 | * @var string |
||
81 | */ |
||
82 | protected $command; |
||
83 | |||
84 | /** |
||
85 | * the callable to call as part of the transition logic |
||
86 | * @var callable |
||
87 | */ |
||
88 | protected $callable_transition; |
||
89 | |||
90 | /** |
||
91 | * the callable to call as part of the transition guard (should return a boolean) |
||
92 | * @var callable |
||
93 | */ |
||
94 | protected $callable_guard; |
||
95 | |||
96 | /** |
||
97 | * a description for the state |
||
98 | * |
||
99 | * @var string |
||
100 | */ |
||
101 | protected $description; |
||
102 | |||
103 | /** |
||
104 | * |
||
105 | * @param State $state_from |
||
106 | * @param State $state_to |
||
107 | * @param string $event |
||
108 | * optional: an event name by which this transition can be |
||
109 | * triggered |
||
110 | * @param string $rule |
||
111 | * optional: one or more fully qualified Rule (sub)class name(s) |
||
112 | * to check to see if we are allowed to transition. |
||
113 | * This can actually be a ',' seperated string of multiple rules |
||
114 | * that will be applied as a chained 'and' rule. |
||
115 | * @param string $command |
||
116 | * optional: one or more fully qualified Command (sub)class |
||
117 | * name(s) to execute for a transition. |
||
118 | * This can actually be a ',' seperated string of multiple |
||
119 | * commands that will be executed as a composite. |
||
120 | * @param callable $callable_guard |
||
121 | * optional: a php callable to call. eg: "function(){echo 'closure called';};" |
||
122 | * @param callable $callable_transition |
||
123 | * optional: a php callable to call. eg: "izzum\MyClass::myStaticMethod" |
||
124 | */ |
||
125 | 63 | public function __construct(State $state_from, State $state_to, $event = null, $rule = self::RULE_EMPTY, $command = self::COMMAND_EMPTY, $callable_guard = self::CALLABLE_NULL, $callable_transition = self::CALLABLE_NULL) |
|
141 | |||
142 | /** |
||
143 | * the callable to call as part of the transition logic |
||
144 | * @param callable $callable |
||
145 | */ |
||
146 | 63 | public function setTransitionCallable($callable) { |
|
150 | |||
151 | /** |
||
152 | * returns the callable for the transition logic. |
||
153 | * @return callable or null |
||
154 | */ |
||
155 | 34 | public function getTransitionCallable() |
|
159 | |||
160 | /** |
||
161 | * the callable to call as part of the transition guard |
||
162 | * @param callable $callable |
||
163 | */ |
||
164 | 63 | public function setGuardCallable($callable) { |
|
168 | |||
169 | /** |
||
170 | * returns the callable for the guard logic. |
||
171 | * @return callable or null |
||
172 | */ |
||
173 | 32 | public function getGuardCallable() |
|
177 | |||
178 | /** |
||
179 | * Can this transition be triggered by a certain event? |
||
180 | * This also matches on the transition name. |
||
181 | * |
||
182 | * @param string $event |
||
183 | * @return boolean |
||
184 | */ |
||
185 | 12 | public function isTriggeredBy($event) |
|
189 | |||
190 | /** |
||
191 | * is a transition possible? Check the guard Rule with the domain object |
||
192 | * injected. |
||
193 | * |
||
194 | * @param Context $context |
||
195 | * @return boolean |
||
196 | */ |
||
197 | 22 | public function can(Context $context) |
|
209 | |||
210 | /** |
||
211 | * Process the transition for the statemachine and execute the associated |
||
212 | * Command with the domain object injected. |
||
213 | * |
||
214 | * @param Context $context |
||
215 | * @return void |
||
216 | */ |
||
217 | 22 | public function process(Context $context) |
|
230 | |||
231 | /** |
||
232 | * calls the $callable as part of the transition |
||
233 | * @param callable $callable |
||
234 | * @param Context $context |
||
235 | */ |
||
236 | 23 | protected function callCallable($callable, Context $context) { |
|
243 | |||
244 | /** |
||
245 | * returns the associated Rule for this Transition, |
||
246 | * configured with a 'reference' (stateful) object |
||
247 | * |
||
248 | * @param Context $context |
||
249 | * the associated Context for a our statemachine |
||
250 | * @return IRule a Rule or chained AndRule if the rule input was a ',' |
||
251 | * seperated string of rules. |
||
252 | * @throws Exception |
||
253 | */ |
||
254 | 29 | public function getRule(Context $context) |
|
285 | |||
286 | |||
287 | /** |
||
288 | * returns the associated Command for this Transition. |
||
289 | * the Command will be configured with the 'reference' of the stateful |
||
290 | * object. |
||
291 | * In case there have been multiple commands as input (',' seperated), this |
||
292 | * method will return a Composite command. |
||
293 | * |
||
294 | * @param Context $context |
||
295 | * @return izzum\command\ICommand |
||
296 | * @throws Exception |
||
297 | */ |
||
298 | 29 | public function getCommand(Context $context) |
|
302 | |||
303 | /** |
||
304 | * |
||
305 | * @return string |
||
306 | */ |
||
307 | 7 | public function toString() |
|
311 | |||
312 | /** |
||
313 | * get the state this transition points from |
||
314 | * |
||
315 | * @return State |
||
316 | */ |
||
317 | 63 | public function getStateFrom() |
|
321 | |||
322 | /** |
||
323 | * get the state this transition points to |
||
324 | * |
||
325 | * @return State |
||
326 | */ |
||
327 | 63 | public function getStateTo() |
|
331 | |||
332 | /** |
||
333 | * get the transition name. |
||
334 | * the transition name is always unique for a statemachine |
||
335 | * since it constists of <state_from>_to_<state_to> |
||
336 | * |
||
337 | * @return string |
||
338 | */ |
||
339 | 63 | public function getName() |
|
344 | |||
345 | /** |
||
346 | * return the command name(s). |
||
347 | * one or more fully qualified command (sub)class name(s) to execute for a |
||
348 | * transition. |
||
349 | * This can actually be a ',' seperated string of multiple commands that |
||
350 | * will be executed as a composite. |
||
351 | * |
||
352 | * @return string |
||
353 | */ |
||
354 | 21 | public function getCommandName() |
|
358 | |||
359 | 63 | public function setCommandName($command) |
|
364 | |||
365 | 21 | public function getRuleName() |
|
369 | |||
370 | 63 | public function setRuleName($rule) |
|
375 | |||
376 | /** |
||
377 | * set the description of the transition (for uml generation for example) |
||
378 | * |
||
379 | * @param string $description |
||
380 | */ |
||
381 | 23 | public function setDescription($description) |
|
386 | |||
387 | /** |
||
388 | * get the description for this transition (if any) |
||
389 | * |
||
390 | * @return string |
||
391 | */ |
||
392 | 22 | public function getDescription() |
|
396 | |||
397 | /** |
||
398 | * set the event name by which this transition can be triggered. |
||
399 | * In case the event name is null or an empty string, it defaults to the |
||
400 | * transition name. |
||
401 | * |
||
402 | * @param string $event |
||
403 | */ |
||
404 | 63 | public function setEvent($event) |
|
412 | |||
413 | /** |
||
414 | * get the event name by which this transition can be triggered |
||
415 | * |
||
416 | * @return string |
||
417 | */ |
||
418 | 22 | public function getEvent() |
|
422 | |||
423 | /** |
||
424 | * for transitions that contain regex states, we need to be able to copy an |
||
425 | * existing (subclass of this) transition with all it's fields. |
||
426 | * We need to instantiate it with a different from and to state since either |
||
427 | * one of those states can be the regex states. All other fields need to be |
||
428 | * copied. |
||
429 | * |
||
430 | * Override this method in a subclass to add other fields. By using 'new |
||
431 | * static' we are already instantiating a possible subclass. |
||
432 | * |
||
433 | * |
||
434 | * @param State $from |
||
435 | * @param State $to |
||
436 | * @return Transition |
||
437 | */ |
||
438 | 19 | public function getCopy(State $from, State $to) |
|
444 | |||
445 | /** |
||
446 | * |
||
447 | * @return string |
||
448 | */ |
||
449 | 1 | public function __toString() |
|
453 | } |
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.