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 VotesProcess 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 VotesProcess, and based on these observations, apply Extract Interface, too.
| 1 | <?php | ||
| 19 | { | ||
| 20 | |||
| 21 | /////////// CONSTRUCTOR /////////// | ||
| 22 | |||
| 23 | // Data and global options | ||
| 24 | protected $_Votes; // Votes list | ||
| 25 | protected $_voteFastMode = false; // When parsing vote, avoid unnecessary checks | ||
| 26 | |||
| 27 | |||
| 28 | /////////// VOTES LIST /////////// | ||
| 29 | |||
| 30 | // How many votes are registered ? | ||
| 31 | 12 | public function countVotes ($tags = null, bool $with = true) : int | |
| 35 | |||
| 36 | 1 | public function countInvalidVoteWithConstraints () : int | |
| 40 | |||
| 41 | 1 | public function countValidVoteWithConstraints () : int | |
| 45 | |||
| 46 | // Sum votes weight | ||
| 47 | 2 | public function sumVotesWeight () : int | |
| 51 | |||
| 52 | 6 | public function sumValidVotesWeightWithConstraints () : int | |
| 56 | |||
| 57 | // Get the votes registered list | ||
| 58 | 13 | public function getVotesList ($tags = null, bool $with = true) : array | |
| 62 | |||
| 63 | 5 | public function getVotesListAsString () : string | |
| 67 | |||
| 68 | 102 | public function getVotesManager () : VotesManager | |
| 72 | |||
| 73 | 3 | public function getVotesListGenerator ($tags = null, bool $with = true) : \Generator | |
| 77 | |||
| 78 | 9 | public function getVoteKey (Vote $vote) : ?int | |
| 82 | |||
| 83 | |||
| 84 | /////////// ADD & REMOVE VOTE /////////// | ||
| 85 | |||
| 86 | // Add a single vote. Array key is the rank, each candidate in a rank are separate by ',' It is not necessary to register the last rank. | ||
| 87 | 129 | public function addVote ($vote, $tags = null) : Vote | |
| 99 | |||
| 100 | 7 | public function prepareUpdateVote (Vote $existVote) : void | |
| 104 | |||
| 105 | 7 | public function finishUpdateVote (Vote $existVote) : void | |
| 113 | |||
| 114 | 129 | public function checkVoteCandidate (Vote $vote) : bool | |
| 140 | |||
| 141 | 129 | public function convertRankingCandidates (array &$ranking) : bool | |
| 158 | |||
| 159 | // Write a new vote | ||
| 160 | 129 | protected function registerVote (Vote $vote, $tag = null) : Vote | |
| 176 | |||
| 177 | 4 | public function removeVotes (Vote $votes_input) : bool | |
| 194 | |||
| 195 | 4 | public function removeVotesByTags ($tags, bool $with = true) : array | |
| 214 | |||
| 215 | |||
| 216 | /////////// PARSE VOTE /////////// | ||
| 217 | |||
| 218 | // Return the well formated vote to use. | ||
| 219 | 129 | protected function prepareVoteInput (&$vote, $tag = null) : void | |
| 230 | |||
| 231 | 2 | public function addVotesFromJson (string $input) : int | |
| 281 | |||
| 282 | 91 | public function parseVotes (string $input, bool $isFile = false) : int | |
| 283 |     { | ||
| 284 | 91 | $input = CondorcetUtil::prepareParse($input, $isFile); | |
| 285 | |||
| 286 | 91 | $adding = []; | |
| 287 | 91 | $count = 0; | |
| 288 | |||
| 289 | 91 | foreach ($input as $line) : | |
| 290 | // Empty Line | ||
| 291 | 91 | if (empty($line)) : | |
| 292 | 79 | continue; | |
| 293 | endif; | ||
| 294 | |||
| 295 | // Multiples | ||
| 296 | 91 | $multiple = VoteUtil::parseAnalysingOneLine(mb_strpos($line, '*'),$line); | |
| 297 | |||
| 298 | // Vote Weight | ||
| 299 | 91 | $weight = VoteUtil::parseAnalysingOneLine(mb_strpos($line, '^'),$line); | |
| 300 | |||
| 301 | // Tags + vote | ||
| 302 | 91 | if (mb_strpos($line, '||') !== false) : | |
| 303 | 7 |                 $data = explode('||', $line); | |
| 304 | |||
| 305 | 7 | $vote = $data[1]; | |
| 306 | 7 | $tags = $data[0]; | |
| 307 | // Vote without tags | ||
| 308 | else : | ||
| 309 | 89 | $vote = $line; | |
| 310 | 89 | $tags = null; | |
| 311 | endif; | ||
| 312 | |||
| 313 | 91 | $adding_predicted_count = $count + $multiple; | |
| 314 | |||
| 315 | 91 | if (self::$_maxVoteNumber && self::$_maxVoteNumber < ($this->countVotes() + $adding_predicted_count)) : | |
| 316 | 1 | throw new CondorcetException(16, (string) self::$_maxParseIteration); | |
| 317 | endif; | ||
| 318 | |||
| 319 | 91 | if (self::$_maxParseIteration !== null && $adding_predicted_count >= self::$_maxParseIteration) : | |
| 320 | 2 | throw new CondorcetException(12, (string) self::$_maxParseIteration); | |
| 321 | endif; | ||
| 349 | 
This check looks for methods that are used by a trait but not required by it.
To illustrate, let’s look at the following code example
The trait
Idableprovides a methodequalsIdthat in turn relies on the methodgetId(). If this method does not exist on a class mixing in this trait, the method will fail.Adding the
getId()as an abstract method to the trait will make sure it is available.