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 WorkflowDescriptorContext 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 WorkflowDescriptorContext, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
22 | class WorkflowDescriptorContext implements Context, SnippetAcceptingContext |
||
23 | { |
||
24 | /** |
||
25 | * |
||
26 | * @var string |
||
27 | */ |
||
28 | protected $workflowDescriptorNamespace = 'OldTown\Workflow\Loader'; |
||
29 | |||
30 | /** |
||
31 | * Последний созданный дескриптор |
||
32 | * |
||
33 | * @var AbstractDescriptor |
||
34 | */ |
||
35 | protected $lastCreatedDescriptor; |
||
36 | |||
37 | /** |
||
38 | * @var |
||
39 | */ |
||
40 | protected $currentScenario; |
||
41 | |||
42 | /** |
||
43 | * @Given Create descriptor :nameDescriptor |
||
44 | * |
||
45 | * @param $nameDescriptor |
||
46 | * |
||
47 | * @return AbstractDescriptor |
||
48 | * |
||
49 | * @throws \RuntimeException |
||
50 | */ |
||
51 | public function createDescriptor($nameDescriptor) |
||
61 | |||
62 | |||
63 | /** |
||
64 | * @Given Create descriptor :nameDescriptor based on xml: |
||
65 | * |
||
66 | * @param string $nameDescriptor |
||
67 | * @param PyStringNode $xml |
||
68 | * |
||
69 | * @return AbstractDescriptor |
||
70 | * @throws \RuntimeException |
||
71 | */ |
||
72 | public function createDescriptorByNameBasedOnXml($nameDescriptor, PyStringNode $xml) |
||
98 | |||
99 | |||
100 | /** |
||
101 | * @When Create descriptor :nameDescriptor based on xml. I expect exception with the text :expectedExceptionMessage. Xml source: |
||
102 | * |
||
103 | * @param string $nameDescriptor |
||
104 | * @param string $expectedExceptionMessage |
||
105 | * @param PyStringNode $xml |
||
106 | * |
||
107 | * @return AbstractDescriptor |
||
108 | */ |
||
109 | public function createDescriptorByNameBasedOnXmlAndExpectToGetAnExceptionMessage($nameDescriptor, $expectedExceptionMessage, PyStringNode $xml) |
||
120 | |||
121 | /** |
||
122 | * @Then Call a method descriptor :nameMethod, I get the value of :expectedResult |
||
123 | * |
||
124 | * @param $nameMethod |
||
125 | * @param $expectedResult |
||
126 | * |
||
127 | * @throws \RuntimeException |
||
128 | */ |
||
129 | public function callAMethodDescriptorIGetTheValueOf($nameMethod, $expectedResult) |
||
154 | |||
155 | |||
156 | /** |
||
157 | * @Transform /^\(.+?\).+?$/ |
||
158 | * |
||
159 | * @param $expectedResult |
||
160 | * |
||
161 | * @return mixed |
||
162 | * @throws \RuntimeException |
||
163 | */ |
||
164 | public function intelligentTransformArgument($expectedResult) |
||
224 | |||
225 | /** |
||
226 | * @When Call a method descriptor :nameMethod. The arguments of the method: |
||
227 | * |
||
228 | * @param $nameMethod |
||
229 | * @param TableNode $table |
||
230 | * |
||
231 | * @throws RuntimeException |
||
232 | */ |
||
233 | public function callAMethodDescriptorTheArgumentsOfTheMethod($nameMethod, TableNode $table) |
||
262 | |||
263 | |||
264 | /** |
||
265 | * @When Call a method descriptor :nameMethod. I expect to get an exception message :expectedExceptionMessage. The arguments of the method: |
||
266 | * |
||
267 | * @param $nameMethod |
||
268 | * @param $expectedExceptionMessage |
||
269 | * @param TableNode $table |
||
270 | * |
||
271 | */ |
||
272 | public function callAMethodDescriptorIExpectToGetAnExceptionMessageTheArgumentsOfTheMethod($nameMethod, $expectedExceptionMessage, TableNode $table) |
||
304 | |||
305 | /** |
||
306 | * @Then Call a method descriptor :nameMethod. I expect to get an exception :expectedException |
||
307 | * |
||
308 | * @param string $nameMethod |
||
309 | * @param string $expectedException |
||
310 | */ |
||
311 | View Code Duplication | public function callAMethodDescriptorIExpectToGetAnException($nameMethod, $expectedException) |
|
330 | |||
331 | /** |
||
332 | * @Then Call a method descriptor :nameMethod. I expect to get an exception message :expectedExceptionMessage |
||
333 | * |
||
334 | * @param $nameMethod |
||
335 | * @param $expectedExceptionMessage |
||
336 | */ |
||
337 | View Code Duplication | public function callAMethodDescriptorIExpectToGetAnExceptionMessage($nameMethod, $expectedExceptionMessage) |
|
356 | |||
357 | |||
358 | |||
359 | |||
360 | /** |
||
361 | * @Then Call a method descriptor :nameMethod, I get the value of :expectedResult. The arguments of the method: |
||
362 | * |
||
363 | * @param string $nameMethod |
||
364 | * @param string $expectedResult |
||
365 | * @param TableNode $table |
||
366 | * |
||
367 | * @throws \RuntimeException |
||
368 | * |
||
369 | */ |
||
370 | public function callAMethodDescriptorIGetTheValueOfTheArgumentsOfTheMethod($nameMethod, $expectedResult, TableNode $table) |
||
408 | |||
409 | /** |
||
410 | * @When Call a method descriptor :nameMethod |
||
411 | * |
||
412 | * @param $nameMethod |
||
413 | * |
||
414 | * @throws RuntimeException |
||
415 | */ |
||
416 | public function callAMethodDescriptor($nameMethod) |
||
432 | |||
433 | |||
434 | /** |
||
435 | * @Then I save to descriptor xml. Not DomDocument. Compare with xml: |
||
436 | * |
||
437 | * @param PyStringNode $expectedXmlNode |
||
438 | * |
||
439 | * @throws \RuntimeException |
||
440 | * |
||
441 | */ |
||
442 | public function iSaveToDescriptorXmlNotDomDocumentCompareWithXml(PyStringNode $expectedXmlNode) |
||
469 | |||
470 | |||
471 | /** |
||
472 | * @Then I save to descriptor xml. Compare with xml: |
||
473 | * |
||
474 | * @param PyStringNode $expectedXmlNode |
||
475 | * |
||
476 | * @throws \RuntimeException |
||
477 | * |
||
478 | */ |
||
479 | public function iSaveToDescriptorXmlCompareWithXml(PyStringNode $expectedXmlNode) |
||
511 | |||
512 | /** |
||
513 | * @Then I save to descriptor xml. I expect to get an exception :expectedException |
||
514 | * |
||
515 | * @param string $expectedException |
||
516 | */ |
||
517 | View Code Duplication | public function iSaveToDescriptorXmlIExpectToGetAnException($expectedException) |
|
539 | |||
540 | /** |
||
541 | * @Then I save to descriptor xml. I expect to get an exception message :expectedException |
||
542 | * |
||
543 | * @param $expectedExceptionMessage |
||
544 | * |
||
545 | */ |
||
546 | View Code Duplication | public function iSaveToDescriptorXmlIExpectToGetAnExceptionMessage($expectedExceptionMessage) |
|
568 | |||
569 | |||
570 | |||
571 | /** |
||
572 | * @Then I validated descriptor. I expect to get an exception message :expectedExceptionMessage |
||
573 | * |
||
574 | * @param $expectedExceptionMessage |
||
575 | * |
||
576 | */ |
||
577 | View Code Duplication | public function iValidatedDescriptorIExpectToGetAnExceptionMessage($expectedExceptionMessage) |
|
594 | |||
595 | |||
596 | /** |
||
597 | * @Then I validated descriptor. I expect to get an exception :expectedException |
||
598 | * |
||
599 | * @param string $expectedException |
||
600 | */ |
||
601 | View Code Duplication | public function iValidatedDescriptorIExpectToGetAnException($expectedException) |
|
618 | |||
619 | |||
620 | /** |
||
621 | * @BeforeScenario @workflowDescriptor |
||
622 | */ |
||
623 | public function beforeScenarioWithTagWorkflowDescriptor() |
||
627 | |||
628 | /** |
||
629 | * @AfterStep |
||
630 | * |
||
631 | * @param AfterStepScope $scope |
||
632 | */ |
||
633 | public function afterStepWithTagWorkflowDescriptor(AfterStepScope $scope) |
||
645 | |||
646 | /** |
||
647 | * Возвращает последний созданный дескриптор |
||
648 | * |
||
649 | * @return AbstractDescriptor |
||
650 | * |
||
651 | * @throws \RuntimeException |
||
652 | */ |
||
653 | protected function getLastCreatedDescriptor() |
||
662 | |||
663 | |||
664 | /** |
||
665 | * @BeforeScenario |
||
666 | * |
||
667 | * @param BeforeScenarioScope $scope |
||
668 | */ |
||
669 | public function beforeScenario(BeforeScenarioScope $scope) |
||
673 | |||
674 | |||
675 | /** |
||
676 | * @AfterScenario |
||
677 | */ |
||
678 | public function afterScenario() |
||
682 | |||
683 | /** |
||
684 | * Наймспейс в котором находятся дескрипторы Workflow |
||
685 | * |
||
686 | * @return string |
||
687 | */ |
||
688 | public function getWorkflowDescriptorNamespace() |
||
692 | |||
693 | /** |
||
694 | * Фабрика по созданию дескрипторов |
||
695 | * |
||
696 | * @param string $name |
||
697 | * |
||
698 | * @param DOMElement $element |
||
699 | * |
||
700 | * @return AbstractDescriptor |
||
701 | * @throws RuntimeException |
||
702 | */ |
||
703 | protected function factoryDescriptor($name, DOMElement $element = null) |
||
729 | |||
730 | /** |
||
731 | * @Given Get the descriptor using the method of :methodName |
||
732 | * |
||
733 | * @param $methodName |
||
734 | * |
||
735 | * @return AbstractDescriptor |
||
736 | * @throws RuntimeException |
||
737 | */ |
||
738 | public function getTheDescriptorUsingTheMethodOf($methodName) |
||
766 | |||
767 | |||
768 | /** |
||
769 | * @When Get the descriptor using the method of :methodName. The arguments of the method: |
||
770 | * |
||
771 | * @param $methodName |
||
772 | * @param TableNode $table |
||
773 | * |
||
774 | * @return mixed |
||
775 | * @throws \RuntimeException |
||
776 | */ |
||
777 | public function getTheDescriptorUsingTheMethodOfTheArgumentsOfTheMethod($methodName, TableNode $table) |
||
822 | } |
||
823 |
It seems like the type of the argument is not accepted by the function/method which you are calling.
In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.
We suggest to add an explicit type cast like in the following example: