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 ShapeRecord 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 ShapeRecord, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
24 | class ShapeRecord { |
||
25 | private $SHPFile = null; |
||
26 | private $DBFFile = null; |
||
27 | |||
28 | public $recordNumber = null; |
||
29 | private $shapeType = null; |
||
30 | |||
31 | public $lastError = ''; |
||
32 | |||
33 | public $SHPData = array(); |
||
34 | public $DBFData = array(); |
||
35 | |||
36 | /** |
||
37 | * @param integer $shapeType |
||
38 | */ |
||
39 | public function __construct($shapeType) { |
||
42 | |||
43 | public function loadFromFile(&$SHPFile, &$DBFFile) { |
||
94 | |||
95 | public function saveToFile(&$SHPFile, &$DBFFile, $recordNumber) { |
||
147 | |||
148 | public function updateDBFInfo($header) { |
||
156 | |||
157 | private function _loadHeaders() { |
||
163 | |||
164 | private function _saveHeaders() { |
||
169 | |||
170 | private function _loadPoint() { |
||
178 | |||
179 | private function _loadPointM() { |
||
188 | |||
189 | private function _loadPointZ() { |
||
199 | |||
200 | private function _savePoint($data) { |
||
204 | |||
205 | private function _savePointM($data) { |
||
210 | |||
211 | private function _savePointZ($data) { |
||
217 | |||
218 | private function _loadNullRecord() { |
||
221 | |||
222 | private function _loadPointRecord() { |
||
225 | |||
226 | private function _loadPointMRecord() { |
||
229 | |||
230 | private function _loadPointZRecord() { |
||
233 | |||
234 | private function _savePointRecord() { |
||
237 | |||
238 | private function _savePointMRecord() { |
||
241 | |||
242 | private function _savePointZRecord() { |
||
245 | |||
246 | private function _loadMultiPointRecord() { |
||
247 | $this->SHPData = array(); |
||
248 | $this->SHPData['xmin'] = Util::loadData('d', fread($this->SHPFile, 8)); |
||
249 | $this->SHPData['ymin'] = Util::loadData('d', fread($this->SHPFile, 8)); |
||
250 | $this->SHPData['xmax'] = Util::loadData('d', fread($this->SHPFile, 8)); |
||
251 | $this->SHPData['ymax'] = Util::loadData('d', fread($this->SHPFile, 8)); |
||
252 | |||
253 | $this->SHPData['numpoints'] = Util::loadData('V', fread($this->SHPFile, 4)); |
||
254 | |||
255 | View Code Duplication | for ($i = 0; $i <= $this->SHPData['numpoints']; $i++) { |
|
|
|||
256 | $this->SHPData['points'][] = $this->_loadPoint(); |
||
257 | } |
||
258 | } |
||
259 | |||
260 | /** |
||
261 | * @param string $type |
||
262 | */ |
||
263 | private function _loadMultiPointMZRecord($type) { |
||
272 | |||
273 | private function _loadMultiPointMRecord() { |
||
278 | |||
279 | private function _loadMultiPointZRecord() { |
||
285 | |||
286 | private function _saveMultiPointRecord() { |
||
287 | fwrite($this->SHPFile, pack('dddd', $this->SHPData['xmin'], $this->SHPData['ymin'], $this->SHPData['xmax'], $this->SHPData['ymax'])); |
||
288 | |||
289 | fwrite($this->SHPFile, pack('V', $this->SHPData['numpoints'])); |
||
290 | |||
291 | View Code Duplication | for ($i = 0; $i <= $this->SHPData['numpoints']; $i++) { |
|
292 | $this->_savePoint($this->SHPData['points'][$i]); |
||
293 | } |
||
294 | } |
||
295 | |||
296 | /** |
||
297 | * @param string $type |
||
298 | */ |
||
299 | private function _saveMultiPointMZRecord($type) { |
||
307 | |||
308 | private function _saveMultiPointMRecord() { |
||
313 | |||
314 | private function _saveMultiPointZRecord() { |
||
320 | |||
321 | private function _loadPolyLineRecord() { |
||
350 | |||
351 | /** |
||
352 | * @param string $type |
||
353 | */ |
||
354 | private function _loadPolyLineMZRecord($type) { |
||
370 | |||
371 | private function _loadPolyLineMRecord() { |
||
376 | |||
377 | private function _loadPolyLineZRecord() { |
||
383 | |||
384 | private function _savePolyLineRecord() { |
||
399 | |||
400 | /** |
||
401 | * @param string $type |
||
402 | */ |
||
403 | private function _savePolyLineMZRecord($type) { |
||
412 | |||
413 | private function _savePolyLineMRecord() { |
||
418 | |||
419 | private function _savePolyLineZRecord() { |
||
425 | |||
426 | private function _loadPolygonRecord() { |
||
429 | |||
430 | private function _loadPolygonMRecord() { |
||
433 | |||
434 | private function _loadPolygonZRecord() { |
||
437 | |||
438 | private function _savePolygonRecord() { |
||
441 | |||
442 | private function _savePolygonMRecord() { |
||
445 | |||
446 | private function _savePolygonZRecord() { |
||
449 | |||
450 | private function _adjustBBox($point) { |
||
467 | |||
468 | public function addPoint($point, $partIndex = 0) { |
||
469 | switch ($this->shapeType) { |
||
470 | case 0: |
||
471 | //Don't add anything |
||
472 | break; |
||
473 | case 1: |
||
474 | case 11: |
||
475 | case 21: |
||
476 | View Code Duplication | if (in_array($this->shapeType, array(11, 21)) && !isset($point['m'])) { |
|
477 | $point['m'] = 0.0; // no_value |
||
478 | } |
||
479 | View Code Duplication | if (in_array($this->shapeType, array(11)) && !isset($point['z'])) { |
|
1 ignored issue
–
show
|
|||
480 | $point['z'] = 0.0; // no_value |
||
481 | } |
||
482 | |||
483 | //Substitutes the value of the current point |
||
484 | $this->SHPData = $point; |
||
485 | $this->_adjustBBox($point); |
||
486 | break; |
||
487 | case 3: |
||
488 | case 5: |
||
489 | case 13: |
||
490 | case 15: |
||
491 | case 23: |
||
492 | case 25: |
||
493 | View Code Duplication | if (in_array($this->shapeType, array(13, 15, 23, 25)) && !isset($point['m'])) { |
|
1 ignored issue
–
show
|
|||
494 | $point['m'] = 0.0; // no_value |
||
495 | } |
||
496 | View Code Duplication | if (in_array($this->shapeType, array(13, 15)) && !isset($point['z'])) { |
|
1 ignored issue
–
show
|
|||
497 | $point['z'] = 0.0; // no_value |
||
498 | } |
||
499 | |||
500 | $this->_adjustBBox($point); |
||
501 | |||
502 | //Adds a new point to the selected part |
||
503 | $this->SHPData['parts'][$partIndex]['points'][] = $point; |
||
504 | |||
505 | $this->SHPData['numparts'] = count($this->SHPData['parts']); |
||
506 | $this->SHPData['numpoints'] = 1 + (isset($this->SHPData['numpoints']) ? $this->SHPData['numpoints'] : 0); |
||
507 | break; |
||
508 | case 8: |
||
509 | case 18: |
||
510 | case 28: |
||
511 | View Code Duplication | if (in_array($this->shapeType, array(18, 28)) && !isset($point['m'])) { |
|
1 ignored issue
–
show
|
|||
512 | $point['m'] = 0.0; // no_value |
||
513 | } |
||
514 | View Code Duplication | if (in_array($this->shapeType, array(18)) && !isset($point['z'])) { |
|
1 ignored issue
–
show
|
|||
515 | $point['z'] = 0.0; // no_value |
||
516 | } |
||
517 | |||
518 | $this->_adjustBBox($point); |
||
519 | |||
520 | //Adds a new point |
||
521 | $this->SHPData['points'][] = $point; |
||
522 | $this->SHPData['numpoints'] = 1 + (isset($this->SHPData['numpoints']) ? $this->SHPData['numpoints'] : 0); |
||
523 | break; |
||
524 | default: |
||
525 | $this->setError(sprintf('The Shape Type "%s" is not supported.', $this->shapeType)); |
||
526 | break; |
||
527 | } |
||
528 | } |
||
529 | |||
530 | public function deletePoint($pointIndex = 0, $partIndex = 0) { |
||
585 | |||
586 | public function getContentLength() { |
||
587 | // The content length for a record is the length of the record contents section measured in 16-bit words. |
||
588 | // one coordinate makes 4 16-bit words (64 bit double) |
||
589 | switch ($this->shapeType) { |
||
590 | case 0: |
||
591 | $result = 0; |
||
592 | break; |
||
593 | case 1: |
||
594 | $result = 10; |
||
595 | break; |
||
596 | case 21: |
||
597 | $result = 10 + 4; |
||
598 | break; |
||
599 | case 11: |
||
600 | $result = 10 + 8; |
||
601 | break; |
||
602 | case 3: |
||
603 | case 5: |
||
604 | $count = count($this->SHPData['parts']); |
||
605 | $result = 22 + 2 * $count; |
||
606 | for ($i = 0; $i < $count; $i++) { |
||
607 | $result += 8 * count($this->SHPData['parts'][$i]['points']); |
||
608 | } |
||
609 | break; |
||
610 | case 23: |
||
611 | View Code Duplication | case 25: |
|
612 | $count = count($this->SHPData['parts']); |
||
613 | $result = 22 + (2 * 4) + 2 * $count; |
||
614 | for ($i = 0; $i < $count; $i++) { |
||
615 | $result += (8 + 4) * count($this->SHPData['parts'][$i]['points']); |
||
616 | } |
||
617 | break; |
||
618 | case 13: |
||
619 | View Code Duplication | case 15: |
|
620 | $count = count($this->SHPData['parts']); |
||
621 | $result = 22 + (4 * 4) + 2 * $count; |
||
622 | for ($i = 0; $i < $count; $i++) { |
||
623 | $result += (8 + 8) * count($this->SHPData['parts'][$i]['points']); |
||
624 | } |
||
625 | break; |
||
626 | case 8: |
||
627 | $result = 20 + 8 * count($this->SHPData['points']); |
||
628 | break; |
||
629 | View Code Duplication | case 28: |
|
630 | $result = 20 + (2 * 4) + (8 + 4) * count($this->SHPData['points']); |
||
631 | break; |
||
632 | View Code Duplication | case 18: |
|
633 | $result = 20 + (4 * 4) + (8 + 8) * count($this->SHPData['points']); |
||
634 | break; |
||
635 | default: |
||
636 | $result = false; |
||
637 | $this->setError(sprintf('The Shape Type "%s" is not supported.', $this->shapeType)); |
||
638 | break; |
||
639 | } |
||
640 | return $result; |
||
641 | } |
||
642 | |||
643 | private function _loadDBFData() { |
||
647 | |||
648 | private function _saveDBFData() { |
||
660 | |||
661 | /** |
||
662 | * @param string $error |
||
663 | */ |
||
664 | public function setError($error) { |
||
668 | } |
||
669 |
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.