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 Command 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 Command, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
35 | abstract class Command |
||
36 | { |
||
37 | /** |
||
38 | * Telegram object |
||
39 | * |
||
40 | * @var \Longman\TelegramBot\Telegram |
||
41 | */ |
||
42 | protected $telegram; |
||
43 | |||
44 | /** |
||
45 | * Update object |
||
46 | * |
||
47 | * @var \Longman\TelegramBot\Entities\Update |
||
48 | */ |
||
49 | protected $update; |
||
50 | |||
51 | /** |
||
52 | * Name |
||
53 | * |
||
54 | * @var string |
||
55 | */ |
||
56 | protected $name = ''; |
||
57 | |||
58 | /** |
||
59 | * Description |
||
60 | * |
||
61 | * @var string |
||
62 | */ |
||
63 | protected $description = 'Command description'; |
||
64 | |||
65 | /** |
||
66 | * Usage |
||
67 | * |
||
68 | * @var string |
||
69 | */ |
||
70 | protected $usage = 'Command usage'; |
||
71 | |||
72 | /** |
||
73 | * Show in Help |
||
74 | * |
||
75 | * @var bool |
||
76 | */ |
||
77 | protected $show_in_help = true; |
||
78 | |||
79 | /** |
||
80 | * Version |
||
81 | * |
||
82 | * @var string |
||
83 | */ |
||
84 | protected $version = '1.0.0'; |
||
85 | |||
86 | /** |
||
87 | * If this command is enabled |
||
88 | * |
||
89 | * @var boolean |
||
90 | */ |
||
91 | protected $enabled = true; |
||
92 | |||
93 | /** |
||
94 | * If this command needs mysql |
||
95 | * |
||
96 | * @var boolean |
||
97 | */ |
||
98 | protected $need_mysql = false; |
||
99 | |||
100 | /* |
||
101 | * Make sure this command only executes on a private chat. |
||
102 | * |
||
103 | * @var bool |
||
104 | */ |
||
105 | protected $private_only = false; |
||
106 | |||
107 | /** |
||
108 | * Command config |
||
109 | * |
||
110 | * @var array |
||
111 | */ |
||
112 | protected $config = []; |
||
113 | |||
114 | /** |
||
115 | * Constructor |
||
116 | * |
||
117 | * @param \Longman\TelegramBot\Telegram $telegram |
||
118 | * @param \Longman\TelegramBot\Entities\Update $update |
||
119 | */ |
||
120 | 15 | public function __construct(Telegram $telegram, Update $update = null) |
|
126 | |||
127 | /** |
||
128 | * Set update object |
||
129 | * |
||
130 | * @param \Longman\TelegramBot\Entities\Update $update |
||
131 | * |
||
132 | * @return \Longman\TelegramBot\Commands\Command |
||
133 | */ |
||
134 | 15 | public function setUpdate(Update $update = null) |
|
142 | |||
143 | /** |
||
144 | * Pre-execute command |
||
145 | * |
||
146 | * @return \Longman\TelegramBot\Entities\ServerResponse |
||
147 | * @throws \Longman\TelegramBot\Exception\TelegramException |
||
148 | */ |
||
149 | public function preExecute() |
||
175 | |||
176 | /** |
||
177 | * Execute command |
||
178 | * |
||
179 | * @return \Longman\TelegramBot\Entities\ServerResponse |
||
180 | * @throws \Longman\TelegramBot\Exception\TelegramException |
||
181 | */ |
||
182 | abstract public function execute(); |
||
183 | |||
184 | /** |
||
185 | * Execution if MySQL is required but not available |
||
186 | * |
||
187 | * @return \Longman\TelegramBot\Entities\ServerResponse |
||
188 | * @throws \Longman\TelegramBot\Exception\TelegramException |
||
189 | */ |
||
190 | View Code Duplication | public function executeNoDb() |
|
|
|||
191 | { |
||
192 | //Preparing message |
||
193 | $message = $this->getMessage(); |
||
194 | $chat_id = $message->getChat()->getId(); |
||
195 | |||
196 | $data = [ |
||
197 | 'chat_id' => $chat_id, |
||
198 | 'text' => 'Sorry no database connection, unable to execute "' . $this->name . '" command.', |
||
199 | ]; |
||
200 | |||
201 | return Request::sendMessage($data); |
||
202 | } |
||
203 | |||
204 | /** |
||
205 | * Get update object |
||
206 | * |
||
207 | * @return \Longman\TelegramBot\Entities\Update |
||
208 | */ |
||
209 | 1 | public function getUpdate() |
|
213 | |||
214 | /** |
||
215 | * Relay any non-existing function calls to Update object. |
||
216 | * |
||
217 | * This is purely a helper method to make requests from within execute() method easier. |
||
218 | * |
||
219 | * @param string $name |
||
220 | * @param array $arguments |
||
221 | * |
||
222 | * @return Command |
||
223 | */ |
||
224 | 1 | public function __call($name, array $arguments) |
|
231 | |||
232 | /** |
||
233 | * Get command config |
||
234 | * |
||
235 | * Look for config $name if found return it, if not return null. |
||
236 | * If $name is not set return all set config. |
||
237 | * |
||
238 | * @param string|null $name |
||
239 | * |
||
240 | * @return array|mixed|null |
||
241 | */ |
||
242 | 1 | public function getConfig($name = null) |
|
253 | |||
254 | /** |
||
255 | * Get telegram object |
||
256 | * |
||
257 | * @return \Longman\TelegramBot\Telegram |
||
258 | */ |
||
259 | 1 | public function getTelegram() |
|
263 | |||
264 | /** |
||
265 | * Get usage |
||
266 | * |
||
267 | * @return string |
||
268 | */ |
||
269 | 1 | public function getUsage() |
|
273 | |||
274 | /** |
||
275 | * Get version |
||
276 | * |
||
277 | * @return string |
||
278 | */ |
||
279 | 1 | public function getVersion() |
|
283 | |||
284 | /** |
||
285 | * Get description |
||
286 | * |
||
287 | * @return string |
||
288 | */ |
||
289 | 1 | public function getDescription() |
|
293 | |||
294 | /** |
||
295 | * Get name |
||
296 | * |
||
297 | * @return string |
||
298 | */ |
||
299 | 1 | public function getName() |
|
303 | |||
304 | /** |
||
305 | * Get Show in Help |
||
306 | * |
||
307 | * @return bool |
||
308 | */ |
||
309 | 1 | public function showInHelp() |
|
313 | |||
314 | /** |
||
315 | * Check if command is enabled |
||
316 | * |
||
317 | * @return bool |
||
318 | */ |
||
319 | 1 | public function isEnabled() |
|
323 | |||
324 | /** |
||
325 | * If this command is intended for private chats only. |
||
326 | * |
||
327 | * @return bool |
||
328 | */ |
||
329 | public function isPrivateOnly() |
||
333 | |||
334 | /** |
||
335 | * If this is a SystemCommand |
||
336 | * |
||
337 | * @return bool |
||
338 | */ |
||
339 | public function isSystemCommand() |
||
343 | |||
344 | /** |
||
345 | * If this is an AdminCommand |
||
346 | * |
||
347 | * @return bool |
||
348 | */ |
||
349 | public function isAdminCommand() |
||
353 | |||
354 | /** |
||
355 | * If this is a UserCommand |
||
356 | * |
||
357 | * @return bool |
||
358 | */ |
||
359 | public function isUserCommand() |
||
363 | |||
364 | /** |
||
365 | * Delete the current message if it has been called in a non-private chat. |
||
366 | * |
||
367 | * @return bool |
||
368 | */ |
||
369 | protected function removeNonPrivateMessage() |
||
389 | |||
390 | /** |
||
391 | * Helper to reply to a chat directly. |
||
392 | * |
||
393 | * @param string $text |
||
394 | * @param array $data |
||
395 | * |
||
396 | * @return \Longman\TelegramBot\Entities\ServerResponse |
||
397 | */ |
||
398 | public function replyToChat($text, array $data = []) |
||
409 | |||
410 | /** |
||
411 | * Helper to reply to a user directly. |
||
412 | * |
||
413 | * @param string $text |
||
414 | * @param array $data |
||
415 | * |
||
416 | * @return \Longman\TelegramBot\Entities\ServerResponse |
||
417 | */ |
||
418 | public function replyToUser($text, array $data = []) |
||
429 | } |
||
430 |
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.