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 Package 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 Package, and based on these observations, apply Extract Interface, too.
1 | <?php namespace SimpleUPS\Track\SmallPackage; |
||
15 | class Package extends \SimpleUPS\Package |
||
16 | { |
||
17 | |||
18 | private |
||
19 | $SIGNATURE_REQUIRED_ADULT = 'A', |
||
20 | $SIGNATURE_REQUIRED = 'S', |
||
21 | $LOCATION_ASSURED = 1; |
||
22 | |||
23 | private |
||
24 | /* @var string $trackingNumber */ |
||
25 | $trackingNumber, |
||
26 | |||
27 | /* @var Activity[] $activity */ |
||
28 | $activity, |
||
29 | /* @var \DateTime $scheduledDeliveryTime */ |
||
30 | $scheduledDeliveryTime, |
||
31 | |||
32 | /* @var \DateTime $rescheduledDeliveryTime */ |
||
33 | $rescheduledDeliveryTime, |
||
34 | /* @var \SimpleUPS\Address $rerouteAddress */ |
||
35 | $rerouteAddress, |
||
36 | |||
37 | /* @var \SimpleUPS\Address $returnToAddress */ |
||
38 | $returnToAddress, |
||
39 | /* @var string $signatureType */ |
||
40 | $signatureType, |
||
41 | |||
42 | /* @var ReferenceNumber[] $referenceNumbers */ |
||
43 | $referenceNumbers, |
||
44 | /* @var ProductType $productType */ |
||
45 | $productType, |
||
46 | |||
47 | /* @var integer $locationAssured */ |
||
48 | $locationAssured, |
||
49 | |||
50 | /* @var Accessorial[] $accessorials */ |
||
51 | $accessorials; |
||
52 | |||
53 | /** |
||
54 | * @internal |
||
55 | * |
||
56 | * @param \SimpleUPS\Address $rerouteAddress |
||
57 | * |
||
58 | * @return Package |
||
59 | */ |
||
60 | public function setRerouteAddress(Address $rerouteAddress) |
||
65 | |||
66 | /** |
||
67 | * Reroute address when an package has been rerouted |
||
68 | * When a requester to intercept US50/PR package at the destination center at |
||
69 | * any time before it has been delivered, |
||
70 | * @return \SimpleUPS\Address |
||
71 | */ |
||
72 | public function getRerouteAddress() |
||
76 | |||
77 | /** |
||
78 | * @internal |
||
79 | * |
||
80 | * @param Activity $activity |
||
81 | * |
||
82 | * @return Package |
||
83 | */ |
||
84 | public function addActivity(Activity $activity) |
||
93 | |||
94 | /** |
||
95 | * @return Activity[] |
||
96 | */ |
||
97 | public function getActivity() |
||
101 | |||
102 | /** |
||
103 | * @internal |
||
104 | * |
||
105 | * @param \DateTime $scheduledDeliveryTime |
||
106 | * |
||
107 | * @return Package |
||
108 | */ |
||
109 | public function setScheduledDeliveryTime(\DateTime $scheduledDeliveryTime) |
||
114 | |||
115 | /** |
||
116 | * Date shipment was originally scheduled for delivery. |
||
117 | * @return \DateTime|null |
||
118 | */ |
||
119 | public function getScheduledDeliveryTime() |
||
123 | |||
124 | /** |
||
125 | * @internal |
||
126 | * |
||
127 | * @param \DateTime $rescheduledDeliveryTime |
||
128 | * |
||
129 | * @return Package |
||
130 | */ |
||
131 | public function setRescheduledDeliveryTime(\DateTime $rescheduledDeliveryTime) |
||
136 | |||
137 | /** |
||
138 | * The delivery is rescheduled to this date |
||
139 | * @return \DateTime|null |
||
140 | */ |
||
141 | public function getRescheduledDeliveryTime() |
||
145 | |||
146 | /** |
||
147 | * @internal |
||
148 | * |
||
149 | * @param \SimpleUPS\Address $returnToAddress |
||
150 | * |
||
151 | * @return Package |
||
152 | */ |
||
153 | public function setReturnToAddress(Address $returnToAddress) |
||
158 | |||
159 | /** |
||
160 | * If the package is returned, the address to whom it was returned |
||
161 | * @return \SimpleUPS\Address|null |
||
162 | */ |
||
163 | public function getReturnToAddress() |
||
167 | |||
168 | /** |
||
169 | * @internal |
||
170 | * |
||
171 | * @param string $signatureType |
||
172 | * |
||
173 | * @return Package |
||
174 | */ |
||
175 | public function setSignatureType($signatureType) |
||
180 | |||
181 | /** |
||
182 | * @internal |
||
183 | * @return string |
||
184 | */ |
||
185 | public function getSignatureType() |
||
189 | |||
190 | /** |
||
191 | * @internal |
||
192 | * |
||
193 | * @param string $trackingNumber |
||
194 | * |
||
195 | * @return Package |
||
196 | */ |
||
197 | public function setTrackingNumber($trackingNumber) |
||
202 | |||
203 | /** |
||
204 | * Tracking number |
||
205 | * @return string |
||
206 | */ |
||
207 | public function getTrackingNumber() |
||
211 | |||
212 | /** |
||
213 | * @internal |
||
214 | * |
||
215 | * @param ReferenceNumber $referenceNumber |
||
216 | * |
||
217 | * @return Package |
||
218 | */ |
||
219 | public function addReferenceNumber(ReferenceNumber $referenceNumber) |
||
228 | |||
229 | /** |
||
230 | * Reference numbers |
||
231 | * @return Package[]|null |
||
232 | */ |
||
233 | public function getReferenceNumbers() |
||
237 | |||
238 | /** |
||
239 | * @internal |
||
240 | * |
||
241 | * @param Weight $weight |
||
242 | * |
||
243 | * @return Package |
||
244 | */ |
||
245 | public function setWeight(Weight $weight) |
||
250 | |||
251 | /** |
||
252 | * @internal |
||
253 | * |
||
254 | * @param ProductType $productType |
||
255 | * |
||
256 | * @return Package |
||
257 | */ |
||
258 | public function setProductType(ProductType $productType) |
||
263 | |||
264 | /** |
||
265 | * The product type of product |
||
266 | * @return ProductType |
||
267 | */ |
||
268 | public function getProductType() |
||
272 | |||
273 | /** |
||
274 | * @internal |
||
275 | * |
||
276 | * @param Accessorial $accessorial |
||
277 | * |
||
278 | * @return Package |
||
279 | */ |
||
280 | public function addAccessorial(Accessorial $accessorial) |
||
289 | |||
290 | /** |
||
291 | * @return Accessorial[] |
||
292 | */ |
||
293 | public function getAccessorials() |
||
297 | |||
298 | /** |
||
299 | * @internal |
||
300 | * |
||
301 | * @param integer $locationAssured |
||
302 | * |
||
303 | * @return Package |
||
304 | */ |
||
305 | public function setLocationAssured($locationAssured) |
||
310 | |||
311 | /** |
||
312 | * @internal |
||
313 | * @return integer |
||
314 | */ |
||
315 | public function getLocationAssured() |
||
319 | |||
320 | /** |
||
321 | * Indication of Location Assured Service. |
||
322 | * @return bool |
||
323 | */ |
||
324 | public function isLocationAssured() |
||
328 | |||
329 | /** |
||
330 | * Does package require a signature for delivery |
||
331 | * @return bool |
||
332 | */ |
||
333 | public function isSignatureRequired() |
||
337 | |||
338 | /** |
||
339 | * Does package require an adult signature for delivery |
||
340 | * @return bool |
||
341 | */ |
||
342 | public function isAdultSignatureRequired() |
||
346 | |||
347 | /** |
||
348 | * Create an address from XML. SimpleXMLElement passed must have immediate children like AddressLine1, City, etc. |
||
349 | * @internal |
||
350 | * |
||
351 | * @param \SimpleXMLElement $xml |
||
352 | * |
||
353 | * @return Package |
||
354 | */ |
||
355 | public static function fromXml(\SimpleXMLElement $xml) |
||
420 | } |
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.