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: