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:
1 | <?php |
||
17 | class Dispatcher implements DispatcherInterface |
||
18 | { |
||
19 | /** |
||
20 | * Dispatcher driver |
||
21 | * |
||
22 | * @var DriverInterface |
||
23 | */ |
||
24 | private $driver; |
||
25 | |||
26 | /** |
||
27 | * Logger |
||
28 | * |
||
29 | * @var LoggerInterface |
||
30 | */ |
||
31 | private $logger; |
||
32 | |||
33 | /** |
||
34 | * Restart |
||
35 | * |
||
36 | * @var RestartInterface |
||
37 | */ |
||
38 | private $restart; |
||
39 | |||
40 | /** |
||
41 | * All registered handlers |
||
42 | * |
||
43 | * @var HandlerInterface[][] |
||
44 | */ |
||
45 | private $handlers = []; |
||
46 | |||
47 | /** |
||
48 | * @var DateTime |
||
49 | */ |
||
50 | private $startTime; |
||
51 | |||
52 | /** |
||
53 | * Create new Dispatcher |
||
54 | * |
||
55 | * @param DriverInterface $driver |
||
56 | * @param LoggerInterface|null $logger |
||
57 | 18 | * @param RestartInterface|null $restart |
|
58 | */ |
||
59 | 18 | public function __construct(DriverInterface $driver, LoggerInterface $logger = null, RestartInterface $restart = null) |
|
72 | |||
73 | /** |
||
74 | * @param MessageInterface $message |
||
75 | * @return DispatcherInterface |
||
76 | * @deprecated - use Emitter::emit method instead |
||
77 | */ |
||
78 | View Code Duplication | public function emit(MessageInterface $message): DispatcherInterface |
|
89 | |||
90 | /** |
||
91 | * Basic method for background job to star listening. |
||
92 | 18 | * |
|
93 | 18 | * This method hook to driver wait() method and start listening events. |
|
94 | 18 | * Method is blocking, so when you call it all processing will stop. |
|
95 | 18 | * WARNING! Don't use it on web server calls. Run it only with cli. |
|
96 | 18 | * |
|
97 | 12 | * @return void |
|
98 | */ |
||
99 | 18 | public function handle(): void |
|
125 | 18 | ||
126 | 6 | /** |
|
127 | * Dispatch message |
||
128 | * |
||
129 | 15 | * @param MessageInterface $message |
|
130 | * |
||
131 | 15 | * @return bool |
|
132 | 15 | */ |
|
133 | private function dispatch(MessageInterface $message): bool |
||
153 | 15 | ||
154 | /** |
||
155 | * Handle given message with given handler |
||
156 | * |
||
157 | * @param HandlerInterface $handler |
||
158 | 15 | * @param MessageInterface $message |
|
159 | * |
||
160 | 12 | * @return bool |
|
161 | 12 | */ |
|
162 | 12 | private function handleMessage(HandlerInterface $handler, MessageInterface $message): bool |
|
193 | |||
194 | 18 | /** |
|
195 | 18 | * Helper function for sending retrying message back to driver |
|
196 | 12 | * |
|
197 | * @param MessageInterface $message |
||
198 | 18 | * @param HandlerInterface $handler |
|
199 | 18 | */ |
|
200 | private function retryMessage(MessageInterface $message, HandlerInterface $handler): void |
||
210 | |||
211 | 18 | /** |
|
212 | 18 | * Calculate next retry |
|
213 | 18 | * |
|
214 | 18 | * Inspired by ruby sidekiq (https://github.com/mperham/sidekiq/wiki/Error-Handling#automatic-job-retry) |
|
215 | 12 | * |
|
216 | * @param MessageInterface $message |
||
217 | * @return float |
||
218 | */ |
||
219 | private function nextRetry(MessageInterface $message): float |
||
223 | |||
224 | /** |
||
225 | * Check if actual dispatcher has handler for given type |
||
226 | * |
||
227 | 18 | * @param string $type |
|
228 | * |
||
229 | 18 | * @return bool |
|
230 | 3 | */ |
|
231 | 2 | private function hasHandlers(string $type): bool |
|
235 | |||
236 | /** |
||
237 | * {@inheritdoc} |
||
238 | */ |
||
239 | public function registerHandler(string $type, HandlerInterface $handler): DispatcherInterface |
||
248 | |||
249 | /** |
||
250 | * {@inheritdoc} |
||
251 | */ |
||
252 | public function registerHandlers(string $type, array $handlers): DispatcherInterface |
||
259 | |||
260 | /** |
||
261 | * Serialize message to logger context |
||
262 | * |
||
263 | * @param MessageInterface $message |
||
264 | * |
||
265 | * @return array |
||
266 | */ |
||
267 | View Code Duplication | private function messageLoggerContext(MessageInterface $message): array |
|
278 | |||
279 | /** |
||
280 | * Interal log method wrapper |
||
281 | * |
||
282 | * @param mixed $level |
||
283 | * @param string $message |
||
284 | * @param array $context |
||
285 | * |
||
286 | * @return void |
||
287 | */ |
||
288 | private function log($level, string $message, array $context = array()): void |
||
294 | } |
||
295 |
Let’s take a look at an example:
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.
Available Fixes
Change the type-hint for the parameter:
Add an additional type-check:
Add the method to the interface: