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 AbstractQueue 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 AbstractQueue, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
16 | abstract class AbstractQueue implements QueueInterface |
||
17 | { |
||
18 | /** |
||
19 | * Maximum millisecond value to use for UTCDateTime creation. |
||
20 | * |
||
21 | * @var integer |
||
22 | */ |
||
23 | const MONGO_INT32_MAX = PHP_INT_MAX; |
||
24 | |||
25 | /** |
||
26 | * mongo collection to use for queue. |
||
27 | * |
||
28 | * @var \MongoDB\Collection |
||
29 | */ |
||
30 | protected $collection; |
||
31 | |||
32 | /** |
||
33 | * Ensure an index for the get() method. |
||
34 | * |
||
35 | * @param array $beforeSort Fields in get() call to index before the sort field in same format |
||
36 | * as \MongoDB\Collection::ensureIndex() |
||
37 | * @param array $afterSort Fields in get() call to index after the sort field in same format as |
||
38 | * \MongoDB\Collection::ensureIndex() |
||
39 | * |
||
40 | * @return void |
||
41 | * |
||
42 | * @throws \InvalidArgumentException value of $beforeSort or $afterSort is not 1 or -1 for ascending and descending |
||
43 | * @throws \InvalidArgumentException key in $beforeSort or $afterSort was not a string |
||
44 | */ |
||
45 | final public function ensureGetIndex(array $beforeSort = [], array $afterSort = []) |
||
60 | |||
61 | /** |
||
62 | * Ensure an index for the count() method. |
||
63 | * Is a no-op if the generated index is a prefix of an existing one. If you have a similar ensureGetIndex call, |
||
64 | * call it first. |
||
65 | * |
||
66 | * @param array $fields fields in count() call to index in same format as \MongoDB\Collection::createIndex() |
||
67 | * @param bool $includeRunning whether to include the running field in the index |
||
68 | * |
||
69 | * @return void |
||
70 | * |
||
71 | * @throws \InvalidArgumentException key in $fields was not a string |
||
72 | * @throws \InvalidArgumentException value of $fields is not 1 or -1 for ascending and descending |
||
73 | */ |
||
74 | final public function ensureCountIndex(array $fields, bool $includeRunning) |
||
86 | |||
87 | /** |
||
88 | * Get a non running message from the queue. |
||
89 | * |
||
90 | * @param array $query in same format as \MongoDB\Collection::find() where top level fields do not contain |
||
91 | * operators. Lower level fields can however. eg: valid {a: {$gt: 1}, "b.c": 3}, |
||
92 | * invalid {$and: [{...}, {...}]} |
||
93 | * @param int $runningResetDuration second duration the message can stay unacked before it resets and can be |
||
94 | * retreived again. |
||
95 | * @param int $waitDurationInMillis millisecond duration to wait for a message. |
||
96 | * @param int $pollDurationInMillis millisecond duration to wait between polls. |
||
97 | * |
||
98 | * @return array|null the message or null if one is not found |
||
99 | * |
||
100 | * @throws \InvalidArgumentException key in $query was not a string |
||
101 | */ |
||
102 | final public function get( |
||
167 | //@codeCoverageIgnoreEnd |
||
168 | |||
169 | /** |
||
170 | * Count queue messages. |
||
171 | * |
||
172 | * @param array $query in same format as \MongoDB\Collection::find() where top level fields do not contain |
||
173 | * operators. Lower level fields can however. eg: valid {a: {$gt: 1}, "b.c": 3}, |
||
174 | * invalid {$and: [{...}, {...}]} |
||
175 | * @param bool|null $running query a running message or not or all |
||
176 | * |
||
177 | * @return int the count |
||
178 | * |
||
179 | * @throws \InvalidArgumentException key in $query was not a string |
||
180 | */ |
||
181 | final public function count(array $query, bool $running = null) : int |
||
200 | |||
201 | /** |
||
202 | * Acknowledge a message was processed and remove from queue. |
||
203 | * |
||
204 | * @param array $message message received from get() |
||
205 | * |
||
206 | * @return void |
||
207 | * |
||
208 | * @throws \InvalidArgumentException $message does not have a field "id" that is a MongoDB\BSON\ObjectID |
||
209 | */ |
||
210 | final public function ack(array $message) |
||
223 | |||
224 | /** |
||
225 | * Atomically acknowledge and send a message to the queue. |
||
226 | * |
||
227 | * @param array $message the message to ack received from get() |
||
228 | * @param array $payload the data to store in the message to send. Data is handled same way |
||
229 | * as \MongoDB\Collection::insertOne() |
||
230 | * @param int $earliestGet earliest unix timestamp the message can be retreived. |
||
231 | * @param float $priority priority for order out of get(). 0 is higher priority than 1 |
||
232 | * @param bool $newTimestamp true to give the payload a new timestamp or false to use given message timestamp |
||
233 | * |
||
234 | * @return void |
||
235 | * |
||
236 | * @throws \InvalidArgumentException $message does not have a field "id" that is a ObjectID |
||
237 | * @throws \InvalidArgumentException $priority is NaN |
||
238 | */ |
||
239 | final public function ackSend( |
||
275 | |||
276 | /** |
||
277 | * Requeue message to the queue. Same as ackSend() with the same message. |
||
278 | * |
||
279 | * @param array $message message received from get(). |
||
280 | * @param int $earliestGet earliest unix timestamp the message can be retreived. |
||
281 | * @param float $priority priority for order out of get(). 0 is higher priority than 1 |
||
282 | * @param bool $newTimestamp true to give the payload a new timestamp or false to use given message timestamp |
||
283 | * |
||
284 | * @return void |
||
285 | * |
||
286 | * @throws \InvalidArgumentException $message does not have a field "id" that is a ObjectID |
||
287 | * @throws \InvalidArgumentException priority is NaN |
||
288 | */ |
||
289 | final public function requeue( |
||
299 | |||
300 | /** |
||
301 | * Send a message to the queue. |
||
302 | * |
||
303 | * @param array $payload the data to store in the message. Data is handled same way |
||
304 | * as \MongoDB\Collection::insertOne() |
||
305 | * @param int $earliestGet earliest unix timestamp the message can be retreived. |
||
306 | * @param float $priority priority for order out of get(). 0 is higher priority than 1 |
||
307 | * |
||
308 | * @return void |
||
309 | * |
||
310 | * @throws \InvalidArgumentException $priority is NaN |
||
311 | */ |
||
312 | final public function send(array $payload, int $earliestGet = 0, float $priority = 0.0) |
||
330 | |||
331 | /** |
||
332 | * Ensure index of correct specification and a unique name whether the specification or name already exist or not. |
||
333 | * Will not create index if $index is a prefix of an existing index |
||
334 | * |
||
335 | * @param array $index index to create in same format as \MongoDB\Collection::createIndex() |
||
336 | * |
||
337 | * @return void |
||
338 | * |
||
339 | * @throws \Exception couldnt create index after 5 attempts |
||
340 | */ |
||
341 | final private function ensureIndex(array $index) |
||
356 | |||
357 | private function isIndexIncludedInExistingIndex(array $index) : bool |
||
369 | |||
370 | private function tryCreateIndex(array $index) : bool |
||
380 | |||
381 | private function tryCreateNamedIndex(array $index, string $name) : bool |
||
395 | |||
396 | private function indexExists(array $index) : bool |
||
406 | |||
407 | /** |
||
408 | * Helper method to validate keys and values for the given sort array |
||
409 | * |
||
410 | * @param array $sort The proposed sort for a mongo index. |
||
411 | * @param string $label The name of the variable given to the public ensureXIndex method. |
||
412 | * @param array &$completedFields The final index array with payload. prefix added to fields. |
||
413 | * |
||
414 | * @return void |
||
415 | */ |
||
416 | final private static function verifySort(array $sort, string $label, array &$completeFields) |
||
432 | } |
||
433 |
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.