Total Complexity | 41 |
Total Lines | 191 |
Duplicated Lines | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Complex classes like NameGenerator 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.
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 NameGenerator, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
11 | class NameGenerator |
||
12 | { |
||
13 | private array $firstLetters; |
||
14 | private array $possibleNextLetters; |
||
15 | private array $nameList; |
||
16 | |||
17 | private Parameter $parameter; |
||
18 | |||
19 | public function __construct(?int ... $example) |
||
20 | { |
||
21 | $this->firstLetters = []; |
||
22 | $this->possibleNextLetters = []; |
||
23 | $this->parameter = new Parameter(); |
||
24 | $this->nameList = []; |
||
25 | |||
26 | foreach (func_get_args() as $args) { |
||
27 | $this->generateFromInternalFile($args); |
||
28 | } |
||
29 | |||
30 | if (!empty($this->nameList)) { |
||
31 | $this->analyseFromArray($this->nameList); |
||
32 | } |
||
33 | } |
||
34 | |||
35 | public function getNameListLength(): int |
||
36 | { |
||
37 | return count($this->nameList); |
||
38 | } |
||
39 | |||
40 | private function generateFromInternalFile(int $example): void |
||
41 | { |
||
42 | $exampleDirectory = __DIR__.'/examples'; |
||
43 | |||
44 | switch ($example) { |
||
45 | case Type::MALE_40K_ARCHAIC: |
||
46 | $handle = fopen($exampleDirectory.'/40k_male_archaic.csv', 'r'); |
||
47 | break; |
||
48 | case Type::MALE_40K_LOWER_GOTHIC: |
||
49 | $handle = fopen($exampleDirectory.'/40k_male_lower_gothic.csv', 'r'); |
||
50 | break; |
||
51 | case Type::MALE_40K_HIGHER_GOTHIC: |
||
52 | $handle = fopen($exampleDirectory.'/40k_male_higher_gothic.csv', 'r'); |
||
53 | break; |
||
54 | case Type::MALE_40K_PRIMITIVE: |
||
55 | $handle = fopen($exampleDirectory.'/40k_male_primitive.csv', 'r'); |
||
56 | break; |
||
57 | case Type::FEMALE_40K_ARCHAIC: |
||
58 | $handle = fopen($exampleDirectory.'/40k_female_archaic.csv', 'r'); |
||
59 | break; |
||
60 | case Type::FEMALE_40K_LOWER_GOTHIC: |
||
61 | $handle = fopen($exampleDirectory.'/40k_female_lower_gothic.csv', 'r'); |
||
62 | break; |
||
63 | case Type::FEMALE_40K_HIGHER_GOTHIC: |
||
64 | $handle = fopen($exampleDirectory.'/40k_female_higher_gothic.csv', 'r'); |
||
65 | break; |
||
66 | case Type::FEMALE_40K_PRIMITIVE: |
||
67 | $handle = fopen($exampleDirectory.'/40k_female_primitive.csv', 'r'); |
||
68 | break; |
||
69 | case Type::MALE_ELVES: |
||
70 | $handle = fopen($exampleDirectory.'/male_elves.csv', 'r'); |
||
71 | break; |
||
72 | case Type::FEMALE_ELVES: |
||
73 | $handle = fopen($exampleDirectory.'/female_elves.csv', 'r'); |
||
74 | break; |
||
75 | default: |
||
76 | throw new GenerateFromFileException(sprintf('There is no file configured for ID %s', $example)); |
||
77 | } |
||
78 | |||
79 | while (($data = fgetcsv($handle)) !== false) { |
||
80 | $this->nameList[] = $data[0]; |
||
81 | } |
||
82 | fclose($handle); |
||
83 | } |
||
84 | |||
85 | public function analyseFromArray(array $nameList): void |
||
86 | { |
||
87 | $this->getParameter()->setLengthsFromNameList($nameList); |
||
88 | |||
89 | $this->firstLetters = $this->getFirstLetters($nameList); |
||
90 | $this->possibleNextLetters = $this->getSequences($nameList); |
||
91 | } |
||
92 | |||
93 | public function analyseFromJson(string $nameList): void |
||
94 | { |
||
95 | $array = json_decode($nameList, true); |
||
96 | |||
97 | $this->analyseFromArray($array); |
||
98 | } |
||
99 | |||
100 | public function generate(): string |
||
101 | { |
||
102 | $name = ''; |
||
103 | $nextLetter = ''; |
||
104 | |||
105 | $name.= $this->firstLetters[rand(0, count($this->firstLetters)-1)]; |
||
106 | |||
107 | while ($nextLetter !== '*' || strlen($name) < 4) { |
||
108 | $nextLetter = $this->possibleNextLetters[strtoupper(substr($name, -1))][rand(0, count($this->possibleNextLetters[strtoupper(substr($name,-1))])-1)]; |
||
109 | if ($nextLetter !== '*') { |
||
110 | $name.= strtolower($nextLetter); |
||
111 | } |
||
112 | } |
||
113 | |||
114 | try { |
||
115 | IntegrityChecker::check($this->parameter, $name); |
||
116 | } catch (IntegrityException $exception) { |
||
117 | return $this->generate(); |
||
118 | } |
||
119 | |||
120 | return $name; |
||
121 | } |
||
122 | |||
123 | public function getFirstLetters(array $nameList): array |
||
124 | { |
||
125 | asort($nameList); |
||
126 | $stats = []; |
||
127 | $availableLetters = []; |
||
128 | |||
129 | foreach ($nameList as $name) { |
||
130 | $firstLetter = strtoupper($name[0]); |
||
131 | |||
132 | $stats[$firstLetter] = array_key_exists($firstLetter, $stats) ? |
||
133 | (int) round($stats[$firstLetter]+100/count($nameList)) : |
||
134 | (int) round(100/count($nameList)); |
||
135 | } |
||
136 | |||
137 | foreach ($stats as $letter => $stat) { |
||
138 | for ($i = 0; $i < $stat; $i++) { |
||
139 | $availableLetters[] = $letter; |
||
140 | } |
||
141 | } |
||
142 | |||
143 | return $availableLetters; |
||
144 | } |
||
145 | |||
146 | public function getSequences(array $nameList): array |
||
192 | } |
||
193 | |||
194 | public function getParameter(): Parameter |
||
195 | { |
||
196 | return $this->parameter; |
||
197 | } |
||
198 | |||
199 | public function setParameter(Parameter $parameter): void |
||
202 | } |
||
203 | } |