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 CustomerGateway 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 CustomerGateway, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
18 | class CustomerGateway |
||
19 | { |
||
20 | private $_gateway; |
||
21 | private $_config; |
||
22 | private $_http; |
||
23 | |||
24 | public function __construct($gateway) |
||
31 | |||
32 | View Code Duplication | public function all() |
|
44 | |||
45 | View Code Duplication | public function fetch($query, $ids) |
|
60 | |||
61 | /** |
||
62 | * Creates a customer using the given +attributes+. If <tt>:id</tt> is not passed, |
||
63 | * the gateway will generate it. |
||
64 | * |
||
65 | * <code> |
||
66 | * $result = Customer::create(array( |
||
67 | * 'first_name' => 'John', |
||
68 | * 'last_name' => 'Smith', |
||
69 | * 'company' => 'Smith Co.', |
||
70 | * 'email' => '[email protected]', |
||
71 | * 'website' => 'www.smithco.com', |
||
72 | * 'fax' => '419-555-1234', |
||
73 | * 'phone' => '614-555-1234' |
||
74 | * )); |
||
75 | * if($result->success) { |
||
76 | * echo 'Created customer ' . $result->customer->id; |
||
77 | * } else { |
||
78 | * echo 'Could not create customer, see result->errors'; |
||
79 | * } |
||
80 | * </code> |
||
81 | * |
||
82 | * @access public |
||
83 | * @param array $attribs |
||
84 | * @return Braintree_Result_Successful|Braintree_Result_Error |
||
85 | */ |
||
86 | public function create($attribs = []) |
||
91 | |||
92 | /** |
||
93 | * attempts the create operation assuming all data will validate |
||
94 | * returns a Customer object instead of a Result |
||
95 | * |
||
96 | * @access public |
||
97 | * @param array $attribs |
||
98 | * @return Customer |
||
99 | * @throws Exception\ValidationError |
||
100 | */ |
||
101 | public function createNoValidate($attribs = []) |
||
106 | /** |
||
107 | * create a customer from a TransparentRedirect operation |
||
108 | * |
||
109 | * @deprecated since version 2.3.0 |
||
110 | * @access public |
||
111 | * @param array $attribs |
||
112 | * @return Customer |
||
113 | */ |
||
114 | View Code Duplication | public function createFromTransparentRedirect($queryString) |
|
125 | |||
126 | /** |
||
127 | * |
||
128 | * @deprecated since version 2.3.0 |
||
129 | * @access public |
||
130 | * @param none |
||
131 | * @return string |
||
132 | */ |
||
133 | public function createCustomerUrl() |
||
139 | |||
140 | |||
141 | /** |
||
142 | * creates a full array signature of a valid create request |
||
143 | * @return array gateway create request format |
||
144 | */ |
||
145 | public static function createSignature() |
||
162 | |||
163 | /** |
||
164 | * creates a full array signature of a valid update request |
||
165 | * @return array update request format |
||
166 | */ |
||
167 | public static function updateSignature() |
||
186 | |||
187 | |||
188 | /** |
||
189 | * find a customer by id |
||
190 | * |
||
191 | * @access public |
||
192 | * @param string id customer Id |
||
193 | * @return Customer|boolean The customer object or false if the request fails. |
||
194 | * @throws Exception\NotFound |
||
195 | */ |
||
196 | View Code Duplication | public function find($id) |
|
209 | |||
210 | /** |
||
211 | * credit a customer for the passed transaction |
||
212 | * |
||
213 | * @access public |
||
214 | * @param int $customerId |
||
215 | * @param array $transactionAttribs |
||
216 | * @return Result\Successful|Result\Error |
||
217 | */ |
||
218 | public function credit($customerId, $transactionAttribs) |
||
227 | |||
228 | /** |
||
229 | * credit a customer, assuming validations will pass |
||
230 | * |
||
231 | * returns a Transaction object on success |
||
232 | * |
||
233 | * @access public |
||
234 | * @param int $customerId |
||
235 | * @param array $transactionAttribs |
||
236 | * @return Transaction |
||
237 | * @throws Exception\ValidationError |
||
238 | */ |
||
239 | public function creditNoValidate($customerId, $transactionAttribs) |
||
244 | |||
245 | /** |
||
246 | * delete a customer by id |
||
247 | * |
||
248 | * @param string $customerId |
||
249 | */ |
||
250 | View Code Duplication | public function delete($customerId) |
|
257 | |||
258 | /** |
||
259 | * create a new sale for a customer |
||
260 | * |
||
261 | * @param string $customerId |
||
262 | * @param array $transactionAttribs |
||
263 | * @return Result\Successful|Result\Error |
||
264 | * @see Transaction::sale() |
||
265 | */ |
||
266 | public function sale($customerId, $transactionAttribs) |
||
275 | |||
276 | /** |
||
277 | * create a new sale for a customer, assuming validations will pass |
||
278 | * |
||
279 | * returns a Transaction object on success |
||
280 | * @access public |
||
281 | * @param string $customerId |
||
282 | * @param array $transactionAttribs |
||
283 | * @return Transaction |
||
284 | * @throws Exception\ValidationsFailed |
||
285 | * @see Transaction::sale() |
||
286 | */ |
||
287 | public function saleNoValidate($customerId, $transactionAttribs) |
||
292 | |||
293 | /** |
||
294 | * Returns a ResourceCollection of customers matching the search query. |
||
295 | * |
||
296 | * If <b>query</b> is a string, the search will be a basic search. |
||
297 | * If <b>query</b> is a hash, the search will be an advanced search. |
||
298 | * For more detailed information and examples, see {@link http://www.braintreepayments.com/gateway/customer-api#searching http://www.braintreepaymentsolutions.com/gateway/customer-api} |
||
299 | * |
||
300 | * @param mixed $query search query |
||
301 | * @return ResourceCollection |
||
302 | * @throws InvalidArgumentException |
||
303 | */ |
||
304 | public function search($query) |
||
326 | |||
327 | /** |
||
328 | * updates the customer record |
||
329 | * |
||
330 | * if calling this method in static context, customerId |
||
331 | * is the 2nd attribute. customerId is not sent in object context. |
||
332 | * |
||
333 | * @access public |
||
334 | * @param string $customerId (optional) |
||
335 | * @param array $attributes |
||
336 | * @return Result\Successful|Result\Error |
||
337 | */ |
||
338 | public function update($customerId, $attributes) |
||
348 | |||
349 | /** |
||
350 | * update a customer record, assuming validations will pass |
||
351 | * |
||
352 | * if calling this method in static context, customerId |
||
353 | * is the 2nd attribute. customerId is not sent in object context. |
||
354 | * returns a Customer object on success |
||
355 | * |
||
356 | * @access public |
||
357 | * @param string $customerId |
||
358 | * @param array $attributes |
||
359 | * @return Customer |
||
360 | * @throws Exception\ValidationsFailed |
||
361 | */ |
||
362 | public function updateNoValidate($customerId, $attributes) |
||
367 | /** |
||
368 | * |
||
369 | * @deprecated since version 2.3.0 |
||
370 | * @access public |
||
371 | * @return string |
||
372 | */ |
||
373 | public function updateCustomerUrl() |
||
379 | |||
380 | /** |
||
381 | * update a customer from a TransparentRedirect operation |
||
382 | * |
||
383 | * @deprecated since version 2.3.0 |
||
384 | * @access public |
||
385 | * @param string $queryString |
||
386 | * @return object |
||
387 | */ |
||
388 | View Code Duplication | public function updateFromTransparentRedirect($queryString) |
|
400 | |||
401 | /* instance methods */ |
||
402 | |||
403 | /** |
||
404 | * sets instance properties from an array of values |
||
405 | * |
||
406 | * @ignore |
||
407 | * @access protected |
||
408 | * @param array $customerAttribs array of customer data |
||
409 | * @return void |
||
410 | */ |
||
411 | protected function _initialize($customerAttribs) |
||
473 | |||
474 | /** |
||
475 | * returns a string representation of the customer |
||
476 | * @return string |
||
477 | */ |
||
478 | public function __toString() |
||
483 | |||
484 | /** |
||
485 | * returns false if comparing object is not a Customer, |
||
486 | * or is a Customer with a different id |
||
487 | * |
||
488 | * @param object $otherCust customer to compare against |
||
489 | * @return boolean |
||
490 | */ |
||
491 | public function isEqual($otherCust) |
||
495 | |||
496 | /** |
||
497 | * returns an array containt all of the customer's payment methods |
||
498 | * |
||
499 | * @return array |
||
500 | */ |
||
501 | public function paymentMethods() |
||
505 | |||
506 | /** |
||
507 | * returns the customer's default payment method |
||
508 | * |
||
509 | * @return CreditCard|PayPalAccount|ApplePayCard|AndroidPayCard |
||
510 | */ |
||
511 | public function defaultPaymentMethod() |
||
516 | |||
517 | public static function _defaultPaymentMethodFilter($paymentMethod) |
||
521 | |||
522 | /* private class properties */ |
||
523 | |||
524 | /** |
||
525 | * @access protected |
||
526 | * @var array registry of customer data |
||
527 | */ |
||
528 | protected $_attributes = [ |
||
529 | 'addresses' => '', |
||
530 | 'company' => '', |
||
531 | 'creditCards' => '', |
||
532 | 'email' => '', |
||
533 | 'fax' => '', |
||
534 | 'firstName' => '', |
||
535 | 'id' => '', |
||
536 | 'lastName' => '', |
||
537 | 'phone' => '', |
||
538 | 'createdAt' => '', |
||
539 | 'updatedAt' => '', |
||
540 | 'website' => '', |
||
541 | ]; |
||
542 | |||
543 | /** |
||
544 | * sends the create request to the gateway |
||
545 | * |
||
546 | * @ignore |
||
547 | * @param string $subPath |
||
548 | * @param array $params |
||
549 | * @return mixed |
||
550 | */ |
||
551 | View Code Duplication | public function _doCreate($subPath, $params) |
|
558 | |||
559 | /** |
||
560 | * verifies that a valid customer id is being used |
||
561 | * @ignore |
||
562 | * @param string customer id |
||
563 | * @throws InvalidArgumentException |
||
564 | */ |
||
565 | View Code Duplication | private function _validateId($id = null) { |
|
577 | |||
578 | |||
579 | /* private class methods */ |
||
580 | |||
581 | /** |
||
582 | * sends the update request to the gateway |
||
583 | * |
||
584 | * @ignore |
||
585 | * @param string $subPath |
||
586 | * @param array $params |
||
587 | * @return mixed |
||
588 | */ |
||
589 | View Code Duplication | private function _doUpdate($httpVerb, $subPath, $params) |
|
596 | |||
597 | /** |
||
598 | * generic method for validating incoming gateway responses |
||
599 | * |
||
600 | * creates a new Customer object and encapsulates |
||
601 | * it inside a Result\Successful object, or |
||
602 | * encapsulates a Errors object inside a Result\Error |
||
603 | * alternatively, throws an Unexpected exception if the response is invalid. |
||
604 | * |
||
605 | * @ignore |
||
606 | * @param array $response gateway response values |
||
607 | * @return Result\Successful|Result\Error |
||
608 | * @throws Exception\Unexpected |
||
609 | */ |
||
610 | View Code Duplication | private function _verifyGatewayResponse($response) |
|
625 | } |
||
626 | class_alias('Braintree\CustomerGateway', 'Braintree_CustomerGateway'); |
||
627 |
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.