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 Query 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 Query, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
14 | class Query |
||
15 | { |
||
16 | protected $fields = []; |
||
17 | protected $table = ""; |
||
18 | protected $where = []; |
||
19 | protected $groupBy = []; |
||
20 | protected $orderBy = []; |
||
21 | protected $join = []; |
||
22 | protected $limitStart = null; |
||
23 | protected $limitEnd = null; |
||
24 | protected $top = null; |
||
25 | |||
26 | protected $forUpdate = false; |
||
27 | |||
28 | 6 | public static function getInstance() |
|
32 | |||
33 | /** |
||
34 | * Example: |
||
35 | * $query->fields(['name', 'price']); |
||
36 | * |
||
37 | * @param array $fields |
||
38 | * @return $this |
||
39 | * @throws \Exception |
||
40 | */ |
||
41 | 5 | public function fields(array $fields) |
|
53 | |||
54 | /** |
||
55 | * @param \ByJG\MicroOrm\Mapper $mapper |
||
56 | * @throws \Exception |
||
57 | */ |
||
58 | 1 | private function addFieldFromMapper(Mapper $mapper) |
|
78 | |||
79 | /** |
||
80 | * Example |
||
81 | * $query->table('product'); |
||
82 | * |
||
83 | * @param string $table |
||
84 | * @return $this |
||
85 | */ |
||
86 | 24 | public function table($table) |
|
92 | |||
93 | /** |
||
94 | * Example: |
||
95 | * $query->join('sales', 'product.id = sales.id'); |
||
96 | * |
||
97 | * @param string $table |
||
98 | * @param string $filter |
||
99 | * @return $this |
||
100 | */ |
||
101 | 1 | public function join($table, $filter) |
|
106 | |||
107 | /** |
||
108 | * Example: |
||
109 | * $query->join('sales', 'product.id = sales.id'); |
||
110 | * |
||
111 | * @param string $table |
||
112 | * @param string $filter |
||
113 | * @return $this |
||
114 | */ |
||
115 | 1 | public function leftJoin($table, $filter) |
|
120 | |||
121 | /** |
||
122 | * Example: |
||
123 | * $query->filter('price > [[amount]]', [ 'amount' => 1000] ); |
||
124 | * |
||
125 | * @param string $filter |
||
126 | * @param array $params |
||
127 | * @return $this |
||
128 | */ |
||
129 | 22 | public function where($filter, array $params = []) |
|
134 | |||
135 | /** |
||
136 | * Example: |
||
137 | * $query->groupBy(['name']); |
||
138 | * |
||
139 | * @param array $fields |
||
140 | * @return $this |
||
141 | */ |
||
142 | 1 | public function groupBy(array $fields) |
|
148 | |||
149 | /** |
||
150 | * Example: |
||
151 | * $query->orderBy(['price desc']); |
||
152 | * |
||
153 | * @param array $fields |
||
154 | * @return $this |
||
155 | */ |
||
156 | 4 | public function orderBy(array $fields) |
|
162 | |||
163 | public function forUpdate() |
||
169 | |||
170 | 1 | public function limit($start, $end) |
|
179 | |||
180 | 1 | public function top($top) |
|
188 | |||
189 | 24 | View Code Duplication | protected function getFields() |
197 | |||
198 | 24 | protected function getJoin() |
|
206 | |||
207 | 24 | View Code Duplication | protected function getWhere() |
223 | |||
224 | /** |
||
225 | * @param \ByJG\AnyDataset\DbDriverInterface|null $dbDriver |
||
226 | * @return array |
||
227 | */ |
||
228 | 24 | public function build(DbDriverInterface $dbDriver = null) |
|
255 | |||
256 | 24 | private function addOrderBy() |
|
263 | |||
264 | 24 | private function addGroupBy() |
|
271 | |||
272 | /** |
||
273 | * @param DbDriverInterface $dbDriver |
||
274 | * @param string $sql |
||
275 | * @return string |
||
276 | */ |
||
277 | 24 | View Code Duplication | private function addforUpdate($dbDriver, $sql) |
289 | |||
290 | /** |
||
291 | * @param DbDriverInterface $dbDriver |
||
292 | * @param string $sql |
||
293 | * @return string |
||
294 | */ |
||
295 | 24 | View Code Duplication | private function addTop($dbDriver, $sql) |
307 | |||
308 | /** |
||
309 | * @param DbDriverInterface $dbDriver |
||
310 | * @param string $sql |
||
311 | * @return string |
||
312 | */ |
||
313 | 24 | private function addLimit($dbDriver, $sql) |
|
325 | } |
||
326 |
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.