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
Idable
provides a methodequalsId
that 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.