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 GSTTaxModifier 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 GSTTaxModifier, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
13 | class GSTTaxModifier extends OrderModifier |
||
|
|||
14 | { |
||
15 | |||
16 | /** |
||
17 | * |
||
18 | * @var Boolean |
||
19 | */ |
||
20 | private static $show_in_cart_table = true; |
||
21 | |||
22 | /** |
||
23 | * message explaining how GST is based on a sale |
||
24 | * to a particular country ... |
||
25 | * @var string |
||
26 | */ |
||
27 | private static $based_on_country_note = ""; |
||
28 | // ######################################## *** model defining static variables (e.g. $db, $has_one) |
||
29 | |||
30 | /** |
||
31 | * standard SS variable |
||
32 | * |
||
33 | * @var Array |
||
34 | */ |
||
35 | private static $db = array( |
||
36 | 'DefaultCountry' => 'Varchar(3)', |
||
37 | 'Country' => 'Varchar(3)', |
||
38 | 'DefaultRate' => 'Double', |
||
39 | 'CurrentRate' => 'Double', |
||
40 | 'TaxType' => "Enum('Exclusive, Inclusive','Inclusive')", |
||
41 | 'DebugString' => 'HTMLText', |
||
42 | 'RawTableValue' => 'Currency' |
||
43 | ); |
||
44 | |||
45 | private static $many_many = array( |
||
46 | "GSTTaxModifierOptions" => "GSTTaxModifierOptions" |
||
47 | ); |
||
48 | |||
49 | /** |
||
50 | * standard SS variable |
||
51 | * @var String |
||
52 | */ |
||
53 | private static $singular_name = "Tax Charge"; |
||
54 | public function i18n_singular_name() |
||
58 | |||
59 | |||
60 | /** |
||
61 | * standard SS variable |
||
62 | * @var String |
||
63 | */ |
||
64 | private static $plural_name = "Tax Charges"; |
||
65 | public function i18n_plural_name() |
||
69 | |||
70 | |||
71 | // ######################################## *** cms variables + functions (e.g. getCMSFields, $searchableFields) |
||
72 | |||
73 | |||
74 | |||
75 | /** |
||
76 | * standard SS method |
||
77 | * @return FieldList for CMS |
||
78 | */ |
||
79 | public function getCMSFields() |
||
101 | |||
102 | // ######################################## *** other (non) static variables (e.g. private static $special_name_for_something, protected $order) |
||
103 | /** |
||
104 | * default country for tax calculations |
||
105 | * IMPORTANT: we need this variable - because in case of INCLUSIVE prices, |
||
106 | * we need to know on what country the prices are based as to be able |
||
107 | * to remove the tax for other countries. |
||
108 | * @var String |
||
109 | */ |
||
110 | private static $default_country_code = ""; |
||
111 | protected static function get_default_country_code_combined() |
||
119 | |||
120 | /** |
||
121 | * wording in cart for prices that are tax exclusive (tax added on top of prices) |
||
122 | * @var String |
||
123 | */ |
||
124 | private static $exclusive_explanation = ""; |
||
125 | |||
126 | /** |
||
127 | * wording in cart for prices that are tax inclusive (tax is part of the prices) |
||
128 | * @var String |
||
129 | */ |
||
130 | private static $inclusive_explanation = ""; |
||
131 | |||
132 | /** |
||
133 | * wording in cart for prices that are include a tax refund. |
||
134 | * A refund situation applies when the prices are tax inclusive |
||
135 | * but NO tax applies to the country to which the goods are sold. |
||
136 | * E.g. for a UK shop no VAT is charged to buyers outside the EU. |
||
137 | * @var String |
||
138 | */ |
||
139 | private static $refund_title = "Tax Exemption"; |
||
140 | |||
141 | /** |
||
142 | * wording in cart for prices that are tax exempt (no tax applies) |
||
143 | * @var String |
||
144 | */ |
||
145 | private static $no_tax_description = "Tax-exempt"; |
||
146 | |||
147 | /** |
||
148 | * name of the method in the buyable OrderItem that works out the |
||
149 | * portion without tax. You can use this method by creating your own |
||
150 | * OrderItem class and adding a method there. This is by far the most |
||
151 | * flexible way to work out the tax on products with complex tax rules. |
||
152 | * @var String |
||
153 | */ |
||
154 | private static $order_item_function_for_tax_exclusive_portion = "portionWithoutTax";//PortionWithoutTax |
||
155 | |||
156 | /** |
||
157 | * Use this variable IF: |
||
158 | * |
||
159 | * a. you have localised prices for countries |
||
160 | * other than the default country |
||
161 | * |
||
162 | * b. prices on the website are TAX INCLUSIVE |
||
163 | * |
||
164 | * If not, the tax for an international for a |
||
165 | * site with tax inclusive prices will firstly |
||
166 | * deduct the default tax and then add the tax |
||
167 | * of the country at hand. |
||
168 | * |
||
169 | * @var Boolean |
||
170 | */ |
||
171 | private static $alternative_country_prices_already_include_their_own_tax = false;//PortionWithoutTax |
||
172 | |||
173 | /** |
||
174 | * contains all the applicable DEFAULT tax objects |
||
175 | * @var Object |
||
176 | */ |
||
177 | private static $default_tax_objects = null; |
||
178 | |||
179 | |||
180 | /** |
||
181 | * tells us the default tax objects tax rate |
||
182 | * @var Float | Null |
||
183 | */ |
||
184 | private static $default_tax_objects_rate = null; |
||
185 | |||
186 | |||
187 | /** |
||
188 | * contains all the applicable tax objects for the current order |
||
189 | * @var Object |
||
190 | */ |
||
191 | private static $current_tax_objects = null; |
||
192 | |||
193 | /** |
||
194 | * tells us the current tax objects tax rate |
||
195 | * @var NULL | Float |
||
196 | */ |
||
197 | private static $current_tax_objects_rate = null; |
||
198 | |||
199 | /** |
||
200 | * any calculation messages are added to the Debug Message |
||
201 | * @var String |
||
202 | */ |
||
203 | protected $debugMessage = ''; |
||
204 | |||
205 | |||
206 | // ######################################## *** CRUD functions (e.g. canEdit) |
||
207 | // ######################################## *** init and update functions |
||
208 | /** |
||
209 | * updates database fields |
||
210 | * @param Bool $force - run it, even if it has run already |
||
211 | * @return void |
||
212 | */ |
||
213 | public function runUpdate($force = true) |
||
225 | |||
226 | |||
227 | // ######################################## *** form functions (e. g. Showform and getform) |
||
228 | // ######################################## *** template functions (e.g. ShowInTable, TableTitle, etc...) ... USES DB VALUES |
||
229 | |||
230 | /** |
||
231 | * Can the user remove this modifier? |
||
232 | * standard OrderModifier Method |
||
233 | * @return Bool |
||
234 | */ |
||
235 | public function CanBeRemoved() |
||
239 | |||
240 | /** |
||
241 | * Show the GSTTaxModifier in the Cart? |
||
242 | * standard OrderModifier Method |
||
243 | * @return Bool |
||
244 | */ |
||
245 | public function ShowInTable() |
||
249 | |||
250 | |||
251 | // ######################################## *** inner calculations.... USES CALCULATED VALUES |
||
252 | |||
253 | /** |
||
254 | * works out what taxes apply in the default setup. |
||
255 | * we need this, because prices may include tax |
||
256 | * based on the default tax rate. |
||
257 | * |
||
258 | * @return ArrayList | FALSE of applicable taxes in the default country. |
||
259 | */ |
||
260 | protected function defaultTaxObjects() |
||
288 | |||
289 | |||
290 | /** |
||
291 | * returns an ArrayList of all applicable tax options |
||
292 | * @return ArrayList | Null |
||
293 | */ |
||
294 | protected function currentTaxObjects() |
||
321 | |||
322 | /** |
||
323 | * returns the sum of rates for the given taxObjects |
||
324 | * @param Object - ArrayList of tax options |
||
325 | * @return Float |
||
326 | */ |
||
327 | protected function workOutSumRate($taxObjects) |
||
341 | |||
342 | /** |
||
343 | * tells us if the tax for the current order is exclusive |
||
344 | * default: false |
||
345 | * @return Bool |
||
346 | */ |
||
347 | protected function isExclusive() |
||
351 | |||
352 | /** |
||
353 | * tells us if the tax for the current order is inclusive |
||
354 | * default: true |
||
355 | * @return Bool |
||
356 | */ |
||
357 | protected function isInclusive() |
||
383 | |||
384 | /** |
||
385 | * turns a standard rate into a calculation rate. |
||
386 | * That is, 0.125 for exclusive is 1/9 for inclusive rates |
||
387 | * default: true |
||
388 | * @param float $rate - input rate (e.g. 0.125 equals a 12.5% tax rate) |
||
389 | * @return float |
||
390 | */ |
||
391 | protected function turnRateIntoCalculationRate($rate) |
||
395 | |||
396 | /** |
||
397 | * works out the tax to pay for the order items, |
||
398 | * based on a rate and a country |
||
399 | * @param float $rate |
||
400 | * @param string $country |
||
401 | * @return float - amount of tax to pay |
||
402 | */ |
||
403 | protected function workoutOrderItemsTax($rate, $country) |
||
458 | |||
459 | /** |
||
460 | * this method is a bit of a hack. |
||
461 | * if a product variation does not have any specific tax rules |
||
462 | * but the product does, then it uses the rules from the product. |
||
463 | * @param DataObject $buyable |
||
464 | */ |
||
465 | public function dealWithProductVariationException($buyable) |
||
477 | |||
478 | /** |
||
479 | * works out the tax to pay for the order modifiers, |
||
480 | * based on a rate |
||
481 | * @param float $rate |
||
482 | * @return float - amount of tax to pay |
||
483 | */ |
||
484 | protected function workoutModifiersTax($rate, $country) |
||
550 | |||
551 | /** |
||
552 | * Are there Any taxes that do not apply to all products |
||
553 | * @return Boolean |
||
554 | */ |
||
555 | protected function hasExceptionTaxes() |
||
562 | |||
563 | // ######################################## *** calculate database fields: protected function Live[field name] ... USES CALCULATED VALUES |
||
564 | |||
565 | |||
566 | /** |
||
567 | * Used to save DefaultCountry to database |
||
568 | * |
||
569 | * determines value for DB field: Country |
||
570 | * @return String |
||
571 | */ |
||
572 | protected function LiveDefaultCountry() |
||
576 | |||
577 | /** |
||
578 | * Used to save Country to database |
||
579 | * |
||
580 | * determines value for DB field: Country |
||
581 | * @return String |
||
582 | */ |
||
583 | protected function LiveCountry() |
||
587 | |||
588 | /** |
||
589 | * determines value for the default rate |
||
590 | * @return Float |
||
591 | */ |
||
592 | protected function LiveDefaultRate() |
||
597 | |||
598 | /** |
||
599 | * Used to save CurrentRate to database |
||
600 | * |
||
601 | * determines value for DB field: Country |
||
602 | * @return Float |
||
603 | */ |
||
604 | protected function LiveCurrentRate() |
||
609 | |||
610 | /** |
||
611 | * Used to save TaxType to database |
||
612 | * |
||
613 | * determines value for DB field: TaxType |
||
614 | * @return String (Exclusive|Inclusive) |
||
615 | */ |
||
616 | protected function LiveTaxType() |
||
623 | |||
624 | /** |
||
625 | * temporary store of data for additional speed. |
||
626 | * @var Array |
||
627 | */ |
||
628 | |||
629 | private static $temp_raw_table_value = array(); |
||
630 | |||
631 | /** |
||
632 | * Used to save RawTableValue to database |
||
633 | * |
||
634 | * In case of a an exclusive rate, show what is actually added. |
||
635 | * In case of inclusive rate, show what is actually included. |
||
636 | * @return float |
||
637 | */ |
||
638 | protected function LiveRawTableValue() |
||
649 | |||
650 | /** |
||
651 | * Used to save DebugString to database |
||
652 | * @return float |
||
653 | */ |
||
654 | protected function LiveDebugString() |
||
658 | |||
659 | /** |
||
660 | * Used to save TableValue to database |
||
661 | * |
||
662 | * @return float |
||
663 | */ |
||
664 | protected function LiveTableValue() |
||
668 | |||
669 | /** |
||
670 | * Used to save Name to database |
||
671 | * @return String |
||
672 | */ |
||
673 | protected function LiveName() |
||
710 | |||
711 | /** |
||
712 | * Used to save CalculatedTotal to database |
||
713 | |||
714 | * works out the actual amount that needs to be deducted / added. |
||
715 | * The exclusive case is easy: just add the applicable tax |
||
716 | * |
||
717 | * The inclusive case: work out what was included and then work out what is applicable |
||
718 | * (current), then work out the difference. |
||
719 | * |
||
720 | * @return Float |
||
721 | */ |
||
722 | protected function LiveCalculatedTotal() |
||
767 | |||
768 | private static $field_or_method_to_use_for_sub_title = ""; |
||
769 | |||
770 | public function getTableSubTitle() |
||
783 | |||
784 | // ######################################## *** Type Functions (IsChargeable, IsDeductable, IsNoChange, IsRemoved) |
||
785 | |||
786 | // ######################################## *** standard database related functions (e.g. onBeforeWrite, onAfterWrite, etc...) |
||
787 | |||
788 | // ######################################## *** AJAX related functions |
||
789 | |||
790 | // ######################################## *** debug functions |
||
791 | } |
||
792 |
You can fix this by adding a namespace to your class:
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.