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 AbstractCommand 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 AbstractCommand, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
21 | abstract class AbstractCommand extends Collection implements CommandInterface |
||
22 | { |
||
23 | // @deprecated: Option used to specify custom headers to add to the generated request |
||
24 | const HEADERS_OPTION = 'command.headers'; |
||
25 | // @deprecated: Option used to add an onComplete method to a command |
||
26 | const ON_COMPLETE = 'command.on_complete'; |
||
27 | // @deprecated: Option used to change the entity body used to store a response |
||
28 | const RESPONSE_BODY = 'command.response_body'; |
||
29 | |||
30 | // Option used to add request options to the request created by a command |
||
31 | const REQUEST_OPTIONS = 'command.request_options'; |
||
32 | // command values to not count as additionalParameters |
||
33 | const HIDDEN_PARAMS = 'command.hidden_params'; |
||
34 | // Option used to disable any pre-sending command validation |
||
35 | const DISABLE_VALIDATION = 'command.disable_validation'; |
||
36 | // Option used to override how a command result will be formatted |
||
37 | const RESPONSE_PROCESSING = 'command.response_processing'; |
||
38 | // Different response types that commands can use |
||
39 | const TYPE_RAW = 'raw'; |
||
40 | const TYPE_MODEL = 'model'; |
||
41 | const TYPE_NO_TRANSLATION = 'no_translation'; |
||
42 | |||
43 | /** @var ClientInterface Client object used to execute the command */ |
||
44 | protected $client; |
||
45 | |||
46 | /** @var RequestInterface The request object associated with the command */ |
||
47 | protected $request; |
||
48 | |||
49 | /** @var mixed The result of the command */ |
||
50 | protected $result; |
||
51 | |||
52 | /** @var OperationInterface API information about the command */ |
||
53 | protected $operation; |
||
54 | |||
55 | /** @var mixed callable */ |
||
56 | protected $onComplete; |
||
57 | |||
58 | /** @var ValidatorInterface Validator used to prepare and validate properties against a JSON schema */ |
||
59 | protected $validator; |
||
60 | |||
61 | /** |
||
62 | * @param array|Collection $parameters Collection of parameters to set on the command |
||
63 | * @param OperationInterface $operation Command definition from description |
||
64 | */ |
||
65 | public function __construct($parameters = array(), OperationInterface $operation = null) |
||
101 | |||
102 | /** |
||
103 | * Custom clone behavior |
||
104 | */ |
||
105 | public function __clone() |
||
110 | |||
111 | /** |
||
112 | * Execute the command in the same manner as calling a function |
||
113 | * |
||
114 | * @return mixed Returns the result of {@see AbstractCommand::execute} |
||
115 | */ |
||
116 | public function __invoke() |
||
120 | |||
121 | public function getName() |
||
125 | |||
126 | /** |
||
127 | * Get the API command information about the command |
||
128 | * |
||
129 | * @return OperationInterface |
||
130 | */ |
||
131 | public function getOperation() |
||
135 | |||
136 | public function setOnComplete($callable) |
||
146 | |||
147 | public function execute() |
||
155 | |||
156 | public function getClient() |
||
160 | |||
161 | public function setClient(ClientInterface $client) |
||
167 | |||
168 | public function getRequest() |
||
176 | |||
177 | public function getResponse() |
||
185 | |||
186 | public function getResult() |
||
202 | |||
203 | public function setResult($result) |
||
209 | |||
210 | public function isPrepared() |
||
214 | |||
215 | public function isExecuted() |
||
219 | |||
220 | public function prepare() |
||
262 | |||
263 | /** |
||
264 | * Set the validator used to validate and prepare command parameters and nested JSON schemas. If no validator is |
||
265 | * set, then the command will validate using the default {@see SchemaValidator}. |
||
266 | * |
||
267 | * @param ValidatorInterface $validator Validator used to prepare and validate properties against a JSON schema |
||
268 | * |
||
269 | * @return self |
||
270 | */ |
||
271 | public function setValidator(ValidatorInterface $validator) |
||
277 | |||
278 | public function getRequestHeaders() |
||
282 | |||
283 | /** |
||
284 | * Initialize the command (hook that can be implemented in subclasses) |
||
285 | */ |
||
286 | protected function init() {} |
||
287 | |||
288 | /** |
||
289 | * Create the request object that will carry out the command |
||
290 | */ |
||
291 | abstract protected function build(); |
||
292 | |||
293 | /** |
||
294 | * Hook used to create an operation for concrete commands that are not associated with a service description |
||
295 | * |
||
296 | * @return OperationInterface |
||
297 | */ |
||
298 | protected function createOperation() |
||
302 | |||
303 | /** |
||
304 | * Create the result of the command after the request has been completed. |
||
305 | * Override this method in subclasses to customize this behavior |
||
306 | */ |
||
307 | protected function process() |
||
313 | |||
314 | /** |
||
315 | * Validate and prepare the command based on the schema and rules defined by the command's Operation object |
||
316 | * |
||
317 | * @throws ValidationException when validation errors occur |
||
318 | */ |
||
319 | protected function validate() |
||
362 | |||
363 | /** |
||
364 | * Get the validator used to prepare and validate properties. If no validator has been set on the command, then |
||
365 | * the default {@see SchemaValidator} will be used. |
||
366 | * |
||
367 | * @return ValidatorInterface |
||
368 | */ |
||
369 | protected function getValidator() |
||
377 | |||
378 | /** |
||
379 | * Get array of any validation errors |
||
380 | * If no validator has been set then return false |
||
381 | */ |
||
382 | public function getValidationErrors() |
||
390 | } |
||
391 |
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.