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 AbstractEntityService 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 AbstractEntityService, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
32 | abstract class AbstractEntityService extends AbstractListenerAggregate implements |
||
33 | EntityServiceInterface, |
||
34 | TransactionAwareInterface |
||
35 | { |
||
36 | /** |
||
37 | * The repository that is used for this entity service. |
||
38 | * |
||
39 | * @var EntityRepositoryInterface |
||
40 | */ |
||
41 | private $repository; |
||
42 | |||
43 | /** |
||
44 | * EventManager handling all events triggered by this service |
||
45 | * |
||
46 | * @var EventManagerInterface |
||
47 | */ |
||
48 | private $eventManager; |
||
49 | |||
50 | /** |
||
51 | * Initialized Event |
||
52 | * |
||
53 | * @var EntityEvent |
||
54 | */ |
||
55 | private $event; |
||
56 | |||
57 | /** |
||
58 | * Initializes a new instance of this class. |
||
59 | * |
||
60 | * @param EntityRepositoryInterface $repository The repository that is used to communicate with. |
||
61 | * @param string $entityClassName The FQCN of the entity. |
||
62 | * @throws \PolderKnowledge\EntityService\Exception\RuntimeException |
||
63 | */ |
||
64 | 108 | public function __construct(EntityRepositoryInterface $repository, $entityClassName) |
|
71 | |||
72 | /** |
||
73 | * Gets the repository that is used by the service. |
||
74 | * |
||
75 | * @return EntityRepositoryInterface |
||
76 | */ |
||
77 | 93 | protected function getRepository() |
|
81 | |||
82 | /** |
||
83 | * Get the pre initialized event object |
||
84 | * |
||
85 | * @return EntityEvent |
||
86 | */ |
||
87 | 108 | protected function getEvent() |
|
97 | |||
98 | /** |
||
99 | * Will create an EventManager when no EventManager was provided. |
||
100 | * The returned EventManager is used to handle events triggered by this service instance. |
||
101 | * |
||
102 | * @return EventManagerInterface |
||
103 | */ |
||
104 | 63 | public function getEventManager() |
|
112 | |||
113 | /** |
||
114 | * Set the EventManager used by this service instance to handle its events. |
||
115 | * It will take care of disabling the old EventManager and will subscribe the internal |
||
116 | * listeners to the new EventManager |
||
117 | * |
||
118 | * @param EventManagerInterface $eventManager |
||
119 | */ |
||
120 | 63 | public function setEventManager(EventManagerInterface $eventManager) |
|
140 | |||
141 | /** |
||
142 | * {@inheritDoc} |
||
143 | */ |
||
144 | public function attach(EventManagerInterface $events, $priority = 1) |
||
221 | |||
222 | /** |
||
223 | * Returns the FQCN of the entity handled by this service. |
||
224 | * |
||
225 | * @return string |
||
226 | */ |
||
227 | 99 | protected function getEntityServiceName() |
|
231 | |||
232 | /** |
||
233 | * Deletes the given object from the repository |
||
234 | * |
||
235 | * @param object $entity The entity to delete. |
||
236 | * @return mixed |
||
237 | * @throws \Zend\EventManager\Exception\InvalidArgumentException |
||
238 | * @throws \PolderKnowledge\EntityService\Exception\RuntimeException |
||
239 | */ |
||
240 | 6 | public function delete($entity) |
|
250 | |||
251 | /** |
||
252 | * Deletes all objects matching the criteria from the repository |
||
253 | * |
||
254 | * @param array|Criteria $criteria The criteria values to match on. |
||
255 | * @return mixed |
||
256 | * @throws \Zend\EventManager\Exception\InvalidArgumentException |
||
257 | * @throws \PolderKnowledge\EntityService\Exception\RuntimeException |
||
258 | */ |
||
259 | 6 | public function deleteBy($criteria) |
|
269 | |||
270 | /** |
||
271 | * Count the objects matching the criteria respecting the order, limit and offset. |
||
272 | * |
||
273 | * @param array|Criteria $criteria The criteria values to match on. |
||
274 | * @return int |
||
275 | * @throws \Zend\EventManager\Exception\InvalidArgumentException |
||
276 | * @throws \PolderKnowledge\EntityService\Exception\RuntimeException |
||
277 | */ |
||
278 | 12 | public function countBy($criteria) |
|
288 | |||
289 | /** |
||
290 | * Find one object in the repository matching the $id |
||
291 | * |
||
292 | * @param mixed $id The id of the entity. |
||
293 | * @return object|null |
||
294 | * @throws \Zend\EventManager\Exception\InvalidArgumentException |
||
295 | * @throws \PolderKnowledge\EntityService\Exception\RuntimeException |
||
296 | */ |
||
297 | 12 | public function find($id) |
|
307 | |||
308 | /** |
||
309 | * Finds all entities in the repository. |
||
310 | * |
||
311 | * @return array Returns the entities that exist. |
||
312 | * @throws \Zend\EventManager\Exception\InvalidArgumentException |
||
313 | * @throws \PolderKnowledge\EntityService\Exception\RuntimeException |
||
314 | */ |
||
315 | 15 | public function findAll() |
|
323 | |||
324 | /** |
||
325 | * Find one or more objects in the repository matching the criteria respecting the order, limit and offset |
||
326 | * |
||
327 | * @param array|Criteria $criteria The array with criteria to search on. |
||
328 | * @return array |
||
329 | * @throws \Zend\EventManager\Exception\InvalidArgumentException |
||
330 | * @throws \PolderKnowledge\EntityService\Exception\RuntimeException |
||
331 | */ |
||
332 | 9 | public function findBy($criteria) |
|
342 | |||
343 | /** |
||
344 | * Find one object in the repository matching the criteria |
||
345 | * |
||
346 | * @param array|Criteria $criteria The criteria values to match on. |
||
347 | * @return object|null |
||
348 | * @throws \Zend\EventManager\Exception\InvalidArgumentException |
||
349 | * @throws \PolderKnowledge\EntityService\Exception\RuntimeException |
||
350 | */ |
||
351 | 6 | public function findOneBy($criteria) |
|
361 | |||
362 | /** |
||
363 | * Persist the given entity |
||
364 | * |
||
365 | * @param object $entity |
||
366 | * @return mixed |
||
367 | * @throws \Zend\EventManager\Exception\InvalidArgumentException |
||
368 | * @throws RuntimeException |
||
369 | */ |
||
370 | 18 | public function persist($entity) |
|
380 | |||
381 | /** |
||
382 | * Persist the given object and flushes it to the storage device. |
||
383 | * |
||
384 | * @param array|Collection|Traversable $entities The entities to persist. |
||
385 | * @return mixed |
||
386 | * @throws RuntimeException |
||
387 | */ |
||
388 | 6 | public function multiPersist($entities) |
|
398 | |||
399 | /** |
||
400 | * will prepare the event object and trigger the event using the internal EventManager |
||
401 | * |
||
402 | * @param string $name |
||
403 | * @param array $params |
||
404 | * @return mixed |
||
405 | * @throws \Zend\EventManager\Exception\InvalidArgumentException |
||
406 | * @throws \PolderKnowledge\EntityService\Exception\RuntimeException |
||
407 | */ |
||
408 | 48 | protected function trigger($name, array $params) |
|
422 | |||
423 | /** |
||
424 | * Starts a new transaction. |
||
425 | * |
||
426 | * @throws ServiceException |
||
427 | */ |
||
428 | 6 | public function beginTransaction() |
|
439 | |||
440 | /** |
||
441 | * Commits a started transaction. |
||
442 | * |
||
443 | * @throws ServiceException |
||
444 | */ |
||
445 | 6 | public function commitTransaction() |
|
456 | |||
457 | /** |
||
458 | * Rolls back a started transaction. |
||
459 | * |
||
460 | * @throws ServiceException |
||
461 | */ |
||
462 | 6 | public function rollbackTransaction() |
|
473 | |||
474 | /** |
||
475 | * Returns true when possible to start an transaction |
||
476 | */ |
||
477 | 18 | public function isTransactionEnabled() |
|
481 | |||
482 | /** |
||
483 | * Returns true when the repository for $entityName is writable |
||
484 | * |
||
485 | * @return bool |
||
486 | */ |
||
487 | 24 | protected function isRepositoryWritable() |
|
491 | |||
492 | /** |
||
493 | * Returns true when the repository for $entityName is readable |
||
494 | * |
||
495 | * @return bool |
||
496 | */ |
||
497 | 51 | protected function isRepositoryReadable() |
|
501 | |||
502 | /** |
||
503 | * Returns true when the repository for $entityName has delete behavior |
||
504 | * |
||
505 | * @return bool |
||
506 | */ |
||
507 | 12 | protected function isRepositoryDeletable() |
|
511 | |||
512 | /** |
||
513 | * Throws an exception for cases where it's not possible to delete from the repository. |
||
514 | * |
||
515 | * @throws RuntimeException |
||
516 | */ |
||
517 | 6 | private function createNotDeletableException() |
|
524 | |||
525 | /** |
||
526 | * Throws an exception for cases where it's not possible to read from the repository. |
||
527 | * |
||
528 | * @throws RuntimeException |
||
529 | */ |
||
530 | 15 | private function createNotReadableException() |
|
537 | |||
538 | /** |
||
539 | * Throws an exception for cases where it's not possible to write to the repository. |
||
540 | * |
||
541 | * @throws RuntimeException |
||
542 | */ |
||
543 | 6 | private function createNotWritableException() |
|
550 | } |
||
551 |
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.