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 ConfigResolver 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 ConfigResolver, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
36 | class ConfigResolver implements VersatileScopeInterface, SiteAccessAware, ContainerAwareInterface |
||
37 | { |
||
38 | use ContainerAwareTrait; |
||
39 | |||
40 | const SCOPE_GLOBAL = 'global'; |
||
41 | const SCOPE_DEFAULT = 'default'; |
||
42 | |||
43 | const UNDEFINED_STRATEGY_EXCEPTION = 1; |
||
44 | const UNDEFINED_STRATEGY_NULL = 2; |
||
45 | |||
46 | /** |
||
47 | * @var \eZ\Publish\Core\MVC\Symfony\SiteAccess |
||
48 | */ |
||
49 | protected $siteAccess; |
||
50 | |||
51 | /** |
||
52 | * @var array Siteaccess groups, indexed by siteaccess name |
||
53 | */ |
||
54 | protected $groupsBySiteAccess; |
||
55 | |||
56 | /** |
||
57 | * @var string |
||
58 | */ |
||
59 | protected $defaultNamespace; |
||
60 | |||
61 | /** |
||
62 | * @var string |
||
63 | */ |
||
64 | protected $defaultScope; |
||
65 | |||
66 | /** |
||
67 | * @var int |
||
68 | */ |
||
69 | protected $undefinedStrategy; |
||
70 | |||
71 | /** |
||
72 | * @var string[] List of param => [services] loaded while siteAccess->matchingType was 'uninitialized' |
||
73 | */ |
||
74 | private $tooEarlyLoadedList = []; |
||
75 | |||
76 | /** |
||
77 | * @param array $groupsBySiteAccess SiteAccess groups, indexed by siteaccess. |
||
78 | * @param string $defaultNamespace The default namespace |
||
79 | * @param int $undefinedStrategy Strategy to use when encountering undefined parameters. |
||
80 | * Must be one of |
||
81 | * - ConfigResolver::UNDEFINED_STRATEGY_EXCEPTION (throw an exception) |
||
82 | * - ConfigResolver::UNDEFINED_STRATEGY_NULL (return null) |
||
83 | */ |
||
84 | public function __construct( |
||
93 | |||
94 | public function setSiteAccess(SiteAccess $siteAccess = null) |
||
98 | |||
99 | /** |
||
100 | * Sets the strategy to use if an undefined parameter is being asked. |
||
101 | * Can be one of: |
||
102 | * - ConfigResolver::UNDEFINED_STRATEGY_EXCEPTION (throw an exception) |
||
103 | * - ConfigResolver::UNDEFINED_STRATEGY_NULL (return null). |
||
104 | * |
||
105 | * Defaults to ConfigResolver::UNDEFINED_STRATEGY_EXCEPTION. |
||
106 | * |
||
107 | * @param int $undefinedStrategy |
||
108 | */ |
||
109 | public function setUndefinedStrategy($undefinedStrategy) |
||
113 | |||
114 | /** |
||
115 | * @return int |
||
116 | */ |
||
117 | public function getUndefinedStrategy() |
||
121 | |||
122 | /** |
||
123 | * Checks if $paramName exists in $namespace. |
||
124 | * |
||
125 | * @param string $paramName |
||
126 | * @param string $namespace If null, the default namespace should be used. |
||
127 | * @param string $scope The scope you need $paramName value for. It's typically the siteaccess name. |
||
128 | * If null, the current siteaccess name will be used. |
||
129 | * |
||
130 | * @return bool |
||
131 | */ |
||
132 | public function hasParameter($paramName, $namespace = null, $scope = null) |
||
159 | |||
160 | /** |
||
161 | * Returns value for $paramName, in $namespace. |
||
162 | * |
||
163 | * @param string $paramName The parameter name, without $prefix and the current scope (i.e. siteaccess name). |
||
164 | * @param string $namespace Namespace for the parameter name. If null, the default namespace will be used. |
||
165 | * @param string $scope The scope you need $paramName value for. It's typically the siteaccess name. |
||
166 | * If null, the current siteaccess name will be used. |
||
167 | * |
||
168 | * @throws \eZ\Publish\Core\MVC\Exception\ParameterNotFoundException |
||
169 | * |
||
170 | * @return mixed |
||
171 | */ |
||
172 | public function getParameter($paramName, $namespace = null, $scope = null) |
||
224 | |||
225 | /** |
||
226 | * Changes the default namespace to look parameter into. |
||
227 | * |
||
228 | * @param string $defaultNamespace |
||
229 | */ |
||
230 | public function setDefaultNamespace($defaultNamespace) |
||
234 | |||
235 | /** |
||
236 | * @return string |
||
237 | */ |
||
238 | public function getDefaultNamespace() |
||
242 | |||
243 | public function getDefaultScope() |
||
247 | |||
248 | /** |
||
249 | * @param string $scope The default "scope" aka siteaccess name, as opposed to the self::SCOPE_DEFAULT. |
||
250 | */ |
||
251 | public function setDefaultScope($scope) |
||
260 | |||
261 | private function warnAboutTooEarlyLoadedParams() |
||
281 | |||
282 | /** |
||
283 | * If in run-time debug mode, before SiteAccess is initialized, log getParameter() usages when considered "unsafe". |
||
284 | * |
||
285 | * @return string |
||
286 | */ |
||
287 | private function logTooEarlyLoadedListIfNeeded($paramName) |
||
339 | } |
||
340 |
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.