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:
1 | <?php |
||
5 | class Money |
||
6 | { |
||
7 | const NUMBER_OF_DECIMALS = 4; |
||
8 | |||
9 | /** @var Currency */ |
||
10 | private $currency; |
||
11 | |||
12 | /** @var int */ |
||
13 | private $value; |
||
14 | |||
15 | /** |
||
16 | * @param Currency $currency |
||
17 | * @param int $value |
||
18 | */ |
||
19 | public function __construct(Currency $currency, $value) |
||
20 | { |
||
21 | $this->validateValue($value); |
||
22 | $this->currency = $currency; |
||
23 | $this->value = $value; |
||
24 | } |
||
25 | |||
26 | /** |
||
27 | * @return Currency |
||
28 | */ |
||
29 | public function currency() |
||
33 | |||
34 | /** |
||
35 | * @return int |
||
36 | */ |
||
37 | public function value() |
||
41 | |||
42 | /** |
||
43 | * @return int |
||
44 | */ |
||
45 | public function decimals() |
||
49 | |||
50 | /** |
||
51 | * @param Money $moneyToAdd |
||
52 | * @return Money |
||
53 | * @throw \InvalidArgumentException |
||
54 | */ |
||
55 | View Code Duplication | public function add(Money $moneyToAdd) |
|
|
|||
56 | { |
||
57 | if (!$this->isSameCurrency($moneyToAdd)) { |
||
58 | throw new \InvalidArgumentException('Currencies does not match'); |
||
59 | } |
||
60 | return new Money($this->currency(), $this->value() + $moneyToAdd->value()); |
||
61 | } |
||
62 | |||
63 | /** |
||
64 | * @param Money $moneyToSubtract |
||
65 | * @return Money |
||
66 | * @throw \InvalidArgumentException |
||
67 | */ |
||
68 | View Code Duplication | public function subtract(Money $moneyToSubtract) |
|
69 | { |
||
70 | if (!$this->isSameCurrency($moneyToSubtract)) { |
||
71 | throw new \InvalidArgumentException('Currencies does not match'); |
||
72 | } |
||
73 | return new Money($this->currency(), $this->value() - $moneyToSubtract->value()); |
||
74 | } |
||
75 | |||
76 | /** |
||
77 | * @param Money $moneyToCompare |
||
78 | * @return bool |
||
79 | * @throw \InvalidArgumentException |
||
80 | */ |
||
81 | public function equals(Money $moneyToCompare) |
||
82 | { |
||
83 | if ($this->isSameCurrency($moneyToCompare)) { |
||
84 | return $this->value() === $moneyToCompare->value(); |
||
85 | } |
||
86 | return false; |
||
87 | } |
||
88 | |||
89 | /** |
||
90 | * @param CurrencyPair $currencyPair |
||
91 | * @return Money |
||
92 | * @throw \InvalidArgumentException |
||
93 | */ |
||
94 | public function exchange(CurrencyPair $currencyPair) |
||
95 | { |
||
96 | if (!$this->isSameCurrencyInCurrencyPair($currencyPair)) { |
||
97 | throw new \InvalidArgumentException('CurrencyFrom in the CurrencyPair does not match the money currency'); |
||
98 | } |
||
99 | return new Money($currencyPair->currencyTo(), (int)($this->value() * $currencyPair->ratio())); |
||
100 | } |
||
101 | |||
102 | /** |
||
103 | * @param CurrencyPair $currencyPair |
||
104 | * @return bool |
||
105 | */ |
||
106 | private function isSameCurrencyInCurrencyPair(CurrencyPair $currencyPair) |
||
110 | |||
111 | /** |
||
112 | * @param Money $money |
||
113 | * @return bool |
||
114 | */ |
||
115 | private function isSameCurrency(Money $money) |
||
119 | |||
120 | /** |
||
121 | * @param $value |
||
122 | * @thrown \InvalidArgumentException |
||
123 | */ |
||
124 | private function validateValue($value) |
||
130 | } |
||
131 |
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.