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 QueryProcessor 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 QueryProcessor, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
33 | class QueryProcessor { |
||
34 | |||
35 | /** |
||
36 | * The schema plugin manager. |
||
37 | * |
||
38 | * @var \Drupal\graphql\Plugin\SchemaPluginManager |
||
39 | */ |
||
40 | protected $pluginManager; |
||
41 | |||
42 | /** |
||
43 | * The cache backend for caching query results. |
||
44 | * |
||
45 | * @var \Drupal\Core\Cache\CacheBackendInterface |
||
46 | */ |
||
47 | protected $cacheBackend; |
||
48 | |||
49 | /** |
||
50 | * The cache contexts manager service. |
||
51 | * |
||
52 | * @var \Drupal\Core\Cache\Context\CacheContextsManager |
||
53 | */ |
||
54 | protected $contextsManager; |
||
55 | |||
56 | /** |
||
57 | * The request stack. |
||
58 | * |
||
59 | * @var \Symfony\Component\HttpFoundation\RequestStack |
||
60 | */ |
||
61 | protected $requestStack; |
||
62 | |||
63 | /** |
||
64 | * Processor constructor. |
||
65 | * |
||
66 | * @param \Drupal\Core\Cache\Context\CacheContextsManager $contextsManager |
||
67 | * The cache contexts manager service. |
||
68 | * @param \Drupal\graphql\Plugin\SchemaPluginManager $pluginManager |
||
69 | * The schema plugin manager. |
||
70 | * @param \Drupal\Core\Cache\CacheBackendInterface $cacheBackend |
||
71 | * The cache backend for caching query results. |
||
72 | * @param \Symfony\Component\HttpFoundation\RequestStack $requestStack |
||
73 | * The request stack. |
||
74 | */ |
||
75 | public function __construct( |
||
86 | |||
87 | /** |
||
88 | * Processes one or multiple graphql operations. |
||
89 | * |
||
90 | * @param string $schema |
||
91 | * The plugin id of the schema to use. |
||
92 | * @param \GraphQL\Server\OperationParams|\GraphQL\Server\OperationParams[] $params |
||
93 | * The graphql operation(s) to execute. |
||
94 | * |
||
95 | * @return \Drupal\graphql\GraphQL\Execution\QueryResult|\Drupal\graphql\GraphQL\Execution\QueryResult[] |
||
96 | * The query result. |
||
97 | * |
||
98 | * @throws \Drupal\Component\Plugin\Exception\PluginException |
||
99 | */ |
||
100 | public function processQuery($schema, $params) { |
||
111 | |||
112 | /** |
||
113 | * @param \GraphQL\Server\ServerConfig $config |
||
114 | * @param \GraphQL\Server\OperationParams $params |
||
115 | * |
||
116 | * @return mixed |
||
117 | */ |
||
118 | public function executeSingle(ServerConfig $config, OperationParams $params) { |
||
123 | |||
124 | /** |
||
125 | * @param \GraphQL\Server\ServerConfig $config |
||
126 | * @param array $params |
||
127 | * |
||
128 | * @return mixed |
||
129 | */ |
||
130 | public function executeBatch(ServerConfig $config, array $params) { |
||
139 | |||
140 | /** |
||
141 | * @param \GraphQL\Executor\Promise\PromiseAdapter $adapter |
||
142 | * @param \GraphQL\Server\ServerConfig $config |
||
143 | * @param \GraphQL\Server\OperationParams $params |
||
144 | * @param bool $batching |
||
145 | * |
||
146 | * @return \GraphQL\Executor\Promise\Promise |
||
147 | */ |
||
148 | protected function executeOperationWithReporting(PromiseAdapter $adapter, ServerConfig $config, OperationParams $params, $batching = FALSE) { |
||
164 | |||
165 | /** |
||
166 | * @param \GraphQL\Executor\Promise\PromiseAdapter $adapter |
||
167 | * @param \GraphQL\Server\ServerConfig $config |
||
168 | * @param \GraphQL\Server\OperationParams $params |
||
169 | * @param bool $batching |
||
170 | * |
||
171 | * @return \GraphQL\Executor\Promise\Promise |
||
172 | */ |
||
173 | protected function executeOperation(PromiseAdapter $adapter, ServerConfig $config, OperationParams $params, $batching = FALSE) { |
||
221 | |||
222 | /** |
||
223 | * @param \GraphQL\Executor\Promise\PromiseAdapter $adapter |
||
224 | * @param \GraphQL\Server\ServerConfig $config |
||
225 | * @param \GraphQL\Server\OperationParams $params |
||
226 | * @param \GraphQL\Language\AST\DocumentNode $document |
||
227 | * @param bool $validate |
||
228 | * |
||
229 | * @return \GraphQL\Executor\Promise\Promise|mixed |
||
230 | */ |
||
231 | protected function executeCacheableOperation(PromiseAdapter $adapter, ServerConfig $config, OperationParams $params, DocumentNode $document, $validate = TRUE) { |
||
256 | |||
257 | /** |
||
258 | * @param \GraphQL\Executor\Promise\PromiseAdapter $adapter |
||
259 | * @param \GraphQL\Server\ServerConfig $config |
||
260 | * @param \GraphQL\Server\OperationParams $params |
||
261 | * @param \GraphQL\Language\AST\DocumentNode $document |
||
262 | * @param bool $validate |
||
263 | * |
||
264 | * @return \GraphQL\Executor\Promise\Promise |
||
265 | */ |
||
266 | protected function executeUncachableOperation(PromiseAdapter $adapter, ServerConfig $config, OperationParams $params, DocumentNode $document, $validate = TRUE) { |
||
274 | |||
275 | /** |
||
276 | * @param \GraphQL\Executor\Promise\PromiseAdapter $adapter |
||
277 | * @param \GraphQL\Server\ServerConfig $config |
||
278 | * @param \GraphQL\Server\OperationParams $params |
||
279 | * @param \GraphQL\Language\AST\DocumentNode $document |
||
280 | * @param bool $validate |
||
281 | * |
||
282 | * @return \GraphQL\Executor\Promise\Promise |
||
283 | */ |
||
284 | protected function doExecuteOperation(PromiseAdapter $adapter, ServerConfig $config, OperationParams $params, DocumentNode $document, $validate = TRUE) { |
||
323 | |||
324 | /** |
||
325 | * @param \GraphQL\Server\OperationParams $params |
||
326 | * |
||
327 | * @return array |
||
328 | */ |
||
329 | protected function validateOperationParams(OperationParams $params) { |
||
335 | |||
336 | /** |
||
337 | * @param \GraphQL\Server\ServerConfig $config |
||
338 | * @param \GraphQL\Server\OperationParams $params |
||
339 | * @param \GraphQL\Language\AST\DocumentNode $document |
||
340 | * |
||
341 | * @return \GraphQL\Error\Error[] |
||
342 | * @throws \Exception |
||
343 | */ |
||
344 | protected function validateOperation(ServerConfig $config, OperationParams $params, DocumentNode $document) { |
||
370 | |||
371 | /** |
||
372 | * @param \GraphQL\Server\ServerConfig $config |
||
373 | * @param \GraphQL\Server\OperationParams $params |
||
374 | * @param \GraphQL\Language\AST\DocumentNode $document |
||
375 | * @param $operation |
||
376 | * |
||
377 | * @return mixed |
||
378 | */ |
||
379 | View Code Duplication | protected function resolveRootValue(ServerConfig $config, OperationParams $params, DocumentNode $document, $operation) { |
|
387 | |||
388 | /** |
||
389 | * @param \GraphQL\Server\ServerConfig $config |
||
390 | * @param \GraphQL\Server\OperationParams $params |
||
391 | * @param \GraphQL\Language\AST\DocumentNode $document |
||
392 | * @param $operation |
||
393 | * |
||
394 | * @return mixed |
||
395 | */ |
||
396 | View Code Duplication | protected function resolveContextValue(ServerConfig $config, OperationParams $params, DocumentNode $document, $operation) { |
|
404 | |||
405 | /** |
||
406 | * @param \GraphQL\Server\ServerConfig $config |
||
407 | * @param \GraphQL\Server\OperationParams $params |
||
408 | * @param \GraphQL\Language\AST\DocumentNode $document |
||
409 | * @param $operation |
||
410 | * |
||
411 | * @return array |
||
412 | * @throws \GraphQL\Server\RequestError |
||
413 | */ |
||
414 | protected function resolveValidationRules(ServerConfig $config, OperationParams $params, DocumentNode $document, $operation) { |
||
426 | |||
427 | /** |
||
428 | * @param \GraphQL\Server\ServerConfig $config |
||
429 | * @param \GraphQL\Server\OperationParams $params |
||
430 | * |
||
431 | * @return mixed |
||
432 | * @throws \GraphQL\Server\RequestError |
||
433 | */ |
||
434 | protected function loadPersistedQuery(ServerConfig $config, OperationParams $params) { |
||
446 | |||
447 | /** |
||
448 | * @param \GraphQL\Language\AST\DocumentNode $document |
||
449 | * |
||
450 | * @return array |
||
451 | */ |
||
452 | protected function serializeDocument(DocumentNode $document) { |
||
455 | |||
456 | /** |
||
457 | * @param array $item |
||
458 | * |
||
459 | * @return array |
||
460 | */ |
||
461 | protected function sanitizeRecursive(array $item) { |
||
472 | |||
473 | /** |
||
474 | * @param \GraphQL\Server\OperationParams $params |
||
475 | * @param \GraphQL\Language\AST\DocumentNode $document |
||
476 | * @param array $contexts |
||
477 | * |
||
478 | * @return string |
||
479 | */ |
||
480 | protected function cacheIdentifier(OperationParams $params, DocumentNode $document, array $contexts = []) { |
||
497 | |||
498 | /** |
||
499 | * Filter unused contexts. |
||
500 | * |
||
501 | * Removes the language contexts from a list of context ids. |
||
502 | * |
||
503 | * @param string[] $contexts |
||
504 | * The list of context id's. |
||
505 | * |
||
506 | * @return string[] |
||
507 | * The filtered list of context id's. |
||
508 | */ |
||
509 | protected function filterCacheContexts(array $contexts) { |
||
514 | |||
515 | /** |
||
516 | * Maps a cache max age value to an "expire" value for the Cache API. |
||
517 | * |
||
518 | * @param int $maxAge |
||
519 | * |
||
520 | * @return int |
||
521 | * A corresponding "expire" value. |
||
522 | * |
||
523 | * @see \Drupal\Core\Cache\CacheBackendInterface::set() |
||
524 | */ |
||
525 | protected function maxAgeToExpire($maxAge) { |
||
529 | } |
||
530 |
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.