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 ProductVariation 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 ProductVariation, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 6 | class ProductVariation extends DataObject implements BuyableModel, EditableEcommerceObject |
||
|
|
|||
| 7 | { |
||
| 8 | /** |
||
| 9 | * Standard SS variable. |
||
| 10 | */ |
||
| 11 | private static $api_access = array( |
||
| 12 | 'view' => array( |
||
| 13 | 'Title', |
||
| 14 | 'Description', |
||
| 15 | 'FullName', |
||
| 16 | 'AllowPurchase', |
||
| 17 | 'InternalItemID', |
||
| 18 | 'NumberSold', |
||
| 19 | 'Price', |
||
| 20 | 'Weight', |
||
| 21 | 'Model', |
||
| 22 | 'Quantifier', |
||
| 23 | 'Version', |
||
| 24 | ), |
||
| 25 | ); |
||
| 26 | |||
| 27 | /** |
||
| 28 | * Standard SS variable. |
||
| 29 | */ |
||
| 30 | private static $db = array( |
||
| 31 | 'InternalItemID' => 'Varchar(30)', |
||
| 32 | 'Price' => 'Currency', |
||
| 33 | 'Weight' => 'Float', |
||
| 34 | 'Model' => 'Varchar(30)', |
||
| 35 | 'Quantifier' => 'Varchar(30)', |
||
| 36 | 'AllowPurchase' => 'Boolean', |
||
| 37 | 'Sort' => 'Int', |
||
| 38 | 'NumberSold' => 'Int', |
||
| 39 | 'Description' => 'Varchar(255)', |
||
| 40 | 'FullName' => 'Varchar(255)', |
||
| 41 | 'FullSiteTreeSort' => 'Varchar(110)', |
||
| 42 | ); |
||
| 43 | |||
| 44 | /** |
||
| 45 | * Standard SS variable. |
||
| 46 | */ |
||
| 47 | private static $has_one = array( |
||
| 48 | 'Product' => 'Product', |
||
| 49 | 'Image' => 'Product_Image', |
||
| 50 | ); |
||
| 51 | |||
| 52 | /** |
||
| 53 | * Standard SS variable. |
||
| 54 | */ |
||
| 55 | private static $many_many = array( |
||
| 56 | 'AttributeValues' => 'ProductAttributeValue', |
||
| 57 | ); |
||
| 58 | |||
| 59 | /** |
||
| 60 | * Standard SS variable. |
||
| 61 | */ |
||
| 62 | private static $casting = array( |
||
| 63 | 'Parent' => 'Product', |
||
| 64 | 'Title' => 'HTMLText', |
||
| 65 | 'Link' => 'Text', |
||
| 66 | 'AllowPurchaseNice' => 'Varchar', |
||
| 67 | 'CalculatedPrice' => 'Currency', |
||
| 68 | 'CalculatedPriceAsMoney' => 'Money', |
||
| 69 | ); |
||
| 70 | |||
| 71 | /** |
||
| 72 | * Standard SS variable. |
||
| 73 | */ |
||
| 74 | private static $defaults = array( |
||
| 75 | 'AllowPurchase' => 1, |
||
| 76 | ); |
||
| 77 | |||
| 78 | /** |
||
| 79 | * Standard SS variable. |
||
| 80 | */ |
||
| 81 | private static $versioning = array( |
||
| 82 | 'Stage', |
||
| 83 | ); |
||
| 84 | |||
| 85 | /** |
||
| 86 | * Standard SS variable. |
||
| 87 | */ |
||
| 88 | private static $extensions = array( |
||
| 89 | "Versioned('Stage')", |
||
| 90 | ); |
||
| 91 | |||
| 92 | /** |
||
| 93 | * Standard SS variable. |
||
| 94 | */ |
||
| 95 | private static $indexes = array( |
||
| 96 | 'Sort' => true, |
||
| 97 | 'FullName' => true, |
||
| 98 | 'FullSiteTreeSort' => true, |
||
| 99 | ); |
||
| 100 | |||
| 101 | /** |
||
| 102 | * Standard SS variable. |
||
| 103 | */ |
||
| 104 | private static $field_labels = array( |
||
| 105 | 'Description' => 'Title (optional)', |
||
| 106 | ); |
||
| 107 | |||
| 108 | /** |
||
| 109 | * Standard SS variable. |
||
| 110 | */ |
||
| 111 | private static $summary_fields = array( |
||
| 112 | 'CMSThumbnail' => 'Image', |
||
| 113 | 'Title' => 'Title', |
||
| 114 | 'Price' => 'Price', |
||
| 115 | 'AllowPurchaseNice' => 'For Sale', |
||
| 116 | ); |
||
| 117 | |||
| 118 | /** |
||
| 119 | * Standard SS variable. |
||
| 120 | */ |
||
| 121 | private static $searchable_fields = array( |
||
| 122 | 'FullName' => array( |
||
| 123 | 'title' => 'Keyword', |
||
| 124 | 'field' => 'TextField', |
||
| 125 | 'filter' => 'PartialMatchFilter', |
||
| 126 | ), |
||
| 127 | 'Price' => array( |
||
| 128 | 'title' => 'Price', |
||
| 129 | 'field' => 'NumericField', |
||
| 130 | ), |
||
| 131 | 'InternalItemID' => array( |
||
| 132 | 'title' => 'Internal Item ID', |
||
| 133 | 'filter' => 'PartialMatchFilter', |
||
| 134 | ), |
||
| 135 | 'AllowPurchase', |
||
| 136 | ); |
||
| 137 | |||
| 138 | /** |
||
| 139 | * Standard SS variable. |
||
| 140 | */ |
||
| 141 | private static $default_sort = '"AllowPurchase" DESC, "FullSiteTreeSort" ASC, "Sort" ASC, "InternalItemID" ASC, "Price" ASC'; |
||
| 142 | |||
| 143 | /** |
||
| 144 | * Standard SS variable. |
||
| 145 | */ |
||
| 146 | private static $singular_name = 'Product Variation'; |
||
| 147 | public function i18n_singular_name() |
||
| 151 | |||
| 152 | /** |
||
| 153 | * Standard SS variable. |
||
| 154 | */ |
||
| 155 | private static $plural_name = 'Product Variations'; |
||
| 156 | public function i18n_plural_name() |
||
| 166 | |||
| 167 | /** |
||
| 168 | * How is the title build up? |
||
| 169 | * |
||
| 170 | * @var array |
||
| 171 | **/ |
||
| 172 | private static $title_style_option = array( |
||
| 173 | 'default' => array( |
||
| 174 | 'ShowType' => true, |
||
| 175 | 'BetweenTypeAndValue' => ': ', |
||
| 176 | 'BetweenVariations' => ', ', |
||
| 177 | ), |
||
| 178 | ); |
||
| 179 | |||
| 180 | /** |
||
| 181 | * change the way the title of the variation is displayed |
||
| 182 | * @param string $code key |
||
| 183 | * @param string $showType do we show the type (e.g. colour, size)? |
||
| 184 | * @param string $betweenTypeAndValue e.g. a semi-colon (:) |
||
| 185 | * @param string $betweenVariations e.g. a comma (,) |
||
| 186 | */ |
||
| 187 | public static function add_title_style_option($code, $showType, $betweenTypeAndValue, $betweenVariations) |
||
| 196 | |||
| 197 | /** |
||
| 198 | * remove style option by key |
||
| 199 | * @param string $code key |
||
| 200 | */ |
||
| 201 | public static function remove_title_style_option($code) |
||
| 205 | |||
| 206 | private static $current_style_option_code = 'default'; |
||
| 207 | |||
| 208 | public static function get_current_style_option_array() |
||
| 212 | |||
| 213 | /** |
||
| 214 | * Standard SS method. |
||
| 215 | * |
||
| 216 | * @return FieldSet |
||
| 217 | */ |
||
| 218 | public function getCMSFields() |
||
| 338 | |||
| 339 | /** |
||
| 340 | * link to edit the record. |
||
| 341 | * |
||
| 342 | * @param string | Null $action - e.g. edit |
||
| 343 | * |
||
| 344 | * @return string |
||
| 345 | */ |
||
| 346 | public function CMSEditLink($action = null) |
||
| 354 | |||
| 355 | /** |
||
| 356 | * Use the sort order of the variation attributes to order the attribute values. |
||
| 357 | * This ensures that when VariationAttributes is used for a table header |
||
| 358 | * and AttributeValues are used for the table rows then the columns will be |
||
| 359 | * in the same order. |
||
| 360 | * |
||
| 361 | * @return DataObjectSet |
||
| 362 | */ |
||
| 363 | public function AttributeValuesSorted() |
||
| 374 | |||
| 375 | /** |
||
| 376 | * standard SS method. |
||
| 377 | */ |
||
| 378 | public function populateDefaults() |
||
| 383 | |||
| 384 | /** |
||
| 385 | * Puts together a title for the Product Variation. |
||
| 386 | * |
||
| 387 | * @return string |
||
| 388 | */ |
||
| 389 | public function Title() |
||
| 415 | |||
| 416 | /** |
||
| 417 | * shorthand. |
||
| 418 | * |
||
| 419 | * @return string |
||
| 420 | */ |
||
| 421 | public function FullDescription() |
||
| 425 | |||
| 426 | /** |
||
| 427 | * shorthand. |
||
| 428 | * |
||
| 429 | * @return string |
||
| 430 | */ |
||
| 431 | public function ImgAltTag() |
||
| 435 | |||
| 436 | /** |
||
| 437 | * returns YES or NO for the CMS Fields. |
||
| 438 | * |
||
| 439 | * @return string |
||
| 440 | */ |
||
| 441 | public function AllowPurchaseNice() |
||
| 445 | |||
| 446 | protected $currentStageOfRequest = ''; |
||
| 447 | |||
| 448 | /** |
||
| 449 | * when we save this object, should we save the parent |
||
| 450 | * as well? |
||
| 451 | * |
||
| 452 | * @var bool |
||
| 453 | */ |
||
| 454 | protected $saveParentProduct = false; |
||
| 455 | |||
| 456 | /** |
||
| 457 | * By setting this to TRUE |
||
| 458 | * the parent (product) will be save when this object will be saved. |
||
| 459 | * |
||
| 460 | * @param bool $b |
||
| 461 | */ |
||
| 462 | public function setSaveParentProduct($b) |
||
| 466 | |||
| 467 | /** |
||
| 468 | * standard SS method |
||
| 469 | * sets the FullName + FullSiteTreeSort of the variation. |
||
| 470 | */ |
||
| 471 | public function onBeforeWrite() |
||
| 478 | |||
| 479 | /** |
||
| 480 | * sets the FullName and FullSiteTreeField to the latest values |
||
| 481 | * This can be useful as you can compare it to the ones saved in the database. |
||
| 482 | * Returns true if the value is different from the one in the database. |
||
| 483 | * |
||
| 484 | * @return bool |
||
| 485 | */ |
||
| 486 | public function prepareFullFields() |
||
| 512 | |||
| 513 | /** |
||
| 514 | * Standard SS Method. |
||
| 515 | */ |
||
| 516 | public function onAfterWrite() |
||
| 536 | |||
| 537 | /** |
||
| 538 | * Standard SS Method |
||
| 539 | * Remove links to Attribute Values. |
||
| 540 | */ |
||
| 541 | public function onBeforeDelete() |
||
| 546 | |||
| 547 | /** |
||
| 548 | * this is used by TableListField to access attribute values. |
||
| 549 | * |
||
| 550 | * @return DataObject |
||
| 551 | */ |
||
| 552 | public function AttributeProxy() |
||
| 563 | |||
| 564 | //GROUPS AND SIBLINGS |
||
| 565 | |||
| 566 | /** |
||
| 567 | * We use this function to make it more universal. |
||
| 568 | * For a buyable, a parent could refer to a ProductGroup OR a Product. |
||
| 569 | * |
||
| 570 | * @return DataObject | Null |
||
| 571 | **/ |
||
| 572 | public function Parent() |
||
| 580 | |||
| 581 | /** |
||
| 582 | * Returns the direct parent (group) for the product. |
||
| 583 | **/ |
||
| 584 | public function MainParentGroup() |
||
| 588 | |||
| 589 | /** |
||
| 590 | * Returns Buybales in the same group. |
||
| 591 | **/ |
||
| 592 | public function Siblings() |
||
| 596 | |||
| 597 | //IMAGES |
||
| 598 | /** |
||
| 599 | * returns a "BestAvailable" image if the current one is not available |
||
| 600 | * In some cases this is appropriate and in some cases this is not. |
||
| 601 | * For example, consider the following setup |
||
| 602 | * - product A with three variations |
||
| 603 | * - Product A has an image, but the variations have no images |
||
| 604 | * With this scenario, you want to show ONLY the product image |
||
| 605 | * on the product page, but if one of the variations is added to the |
||
| 606 | * cart, then you want to show the product image. |
||
| 607 | * This can be achieved bu using the BestAvailable image. |
||
| 608 | * |
||
| 609 | * @return Image | Null |
||
| 610 | */ |
||
| 611 | public function BestAvailableImage() |
||
| 621 | |||
| 622 | /** |
||
| 623 | * Little hack to show thumbnail in summary fields in modeladmin in CMS. |
||
| 624 | * |
||
| 625 | * @return string (HTML = formatted image) |
||
| 626 | */ |
||
| 627 | public function CMSThumbnail() |
||
| 637 | |||
| 638 | /** |
||
| 639 | * Returns a link to a default image. |
||
| 640 | * If a default image is set in the site config then this link is returned |
||
| 641 | * Otherwise, a standard link is returned. |
||
| 642 | * |
||
| 643 | * @return string |
||
| 644 | */ |
||
| 645 | public function DefaultImageLink() |
||
| 649 | |||
| 650 | /** |
||
| 651 | * returns the default image of the product. |
||
| 652 | * |
||
| 653 | * @return Image | Null |
||
| 654 | */ |
||
| 655 | public function DefaultImage() |
||
| 659 | |||
| 660 | /** |
||
| 661 | * returns a product image for use in templates |
||
| 662 | * e.g. $DummyImage.Width();. |
||
| 663 | * |
||
| 664 | * @return Product_Image |
||
| 665 | */ |
||
| 666 | public function DummyImage() |
||
| 670 | |||
| 671 | // VERSIONING |
||
| 672 | |||
| 673 | /** |
||
| 674 | * Action to return specific version of a product. |
||
| 675 | * This is really useful for sold products where you want to retrieve the actual version that you sold. |
||
| 676 | * |
||
| 677 | * @TODO: this is not correct yet, as the versions of product and productvariation are muddled up! |
||
| 678 | * |
||
| 679 | * @param HTTPRequest $request |
||
| 680 | */ |
||
| 681 | public function viewversion($request) |
||
| 701 | |||
| 702 | /** |
||
| 703 | * Action to return specific version of a product variation. |
||
| 704 | * This can be any product to enable the retrieval of deleted products. |
||
| 705 | * This is really useful for sold products where you want to retrieve the actual version that you sold. |
||
| 706 | * |
||
| 707 | * @param int $id |
||
| 708 | * @param int $version |
||
| 709 | * |
||
| 710 | * @return DataObject | Null |
||
| 711 | */ |
||
| 712 | public function getVersionOfBuyable($id = 0, $version = 0) |
||
| 723 | |||
| 724 | //ORDER ITEM |
||
| 725 | |||
| 726 | /** |
||
| 727 | * returns the order item associated with the buyable. |
||
| 728 | * ALWAYS returns one, even if there is none in the cart. |
||
| 729 | * Does not write to database. |
||
| 730 | * |
||
| 731 | * @return OrderItem (no kidding) |
||
| 732 | **/ |
||
| 733 | public function OrderItem() |
||
| 753 | |||
| 754 | /** |
||
| 755 | * @var string |
||
| 756 | */ |
||
| 757 | protected $defaultClassNameForOrderItem = 'ProductVariation_OrderItem'; |
||
| 758 | |||
| 759 | /** |
||
| 760 | * you can overwrite this function in your buyable items (such as Product). |
||
| 761 | * |
||
| 762 | * @return string |
||
| 763 | **/ |
||
| 764 | public function classNameForOrderItem() |
||
| 774 | |||
| 775 | /** |
||
| 776 | * You can set an alternative class name for order item using this method. |
||
| 777 | * |
||
| 778 | * @param string $ClassName |
||
| 779 | **/ |
||
| 780 | public function setAlternativeClassNameForOrderItem($className) |
||
| 784 | |||
| 785 | /** |
||
| 786 | * When purchasing this buyable, how many decimals can it have? |
||
| 787 | * |
||
| 788 | * @return int |
||
| 789 | */ |
||
| 790 | public function QuantityDecimals() |
||
| 794 | |||
| 795 | /** |
||
| 796 | * Number of variations sold. |
||
| 797 | * |
||
| 798 | * @TODO: check if we need to use other class names |
||
| 799 | * |
||
| 800 | * |
||
| 801 | * @return int |
||
| 802 | */ |
||
| 803 | public function HasBeenSold() |
||
| 821 | |||
| 822 | //LINKS |
||
| 823 | |||
| 824 | /** |
||
| 825 | * Takes you to the Product and filters |
||
| 826 | * for the provided variation. |
||
| 827 | * |
||
| 828 | * @param string $action - OPTIONAL |
||
| 829 | * |
||
| 830 | * @return string |
||
| 831 | */ |
||
| 832 | public function Link($action = null) |
||
| 840 | |||
| 841 | |||
| 842 | /** |
||
| 843 | * |
||
| 844 | * @todo TEST!!!! |
||
| 845 | * @return string |
||
| 846 | */ |
||
| 847 | public function VersionedLink() |
||
| 858 | |||
| 859 | /** |
||
| 860 | * passing on shopping cart links ...is this necessary?? ...why not just pass the cart? |
||
| 861 | * |
||
| 862 | * @return string |
||
| 863 | */ |
||
| 864 | public function AddLink() |
||
| 868 | |||
| 869 | /** |
||
| 870 | * link use to add (one) to cart. |
||
| 871 | * |
||
| 872 | *@return string |
||
| 873 | */ |
||
| 874 | public function IncrementLink() |
||
| 879 | |||
| 880 | /** |
||
| 881 | * Link used to remove one from cart |
||
| 882 | * we can do this, because by default remove link removes one. |
||
| 883 | * |
||
| 884 | * @return string |
||
| 885 | */ |
||
| 886 | public function DecrementLink() |
||
| 890 | |||
| 891 | /** |
||
| 892 | * remove one buyable's orderitem from cart. |
||
| 893 | * |
||
| 894 | * @return string (Link) |
||
| 895 | */ |
||
| 896 | public function RemoveLink() |
||
| 900 | |||
| 901 | /** |
||
| 902 | * remove all of this buyable's orderitem from cart. |
||
| 903 | * |
||
| 904 | * @return string (Link) |
||
| 905 | */ |
||
| 906 | public function RemoveAllLink() |
||
| 910 | |||
| 911 | /** |
||
| 912 | * remove all of this buyable's orderitem from cart and go through to this buyble to add alternative selection. |
||
| 913 | * |
||
| 914 | * @return string (Link) |
||
| 915 | */ |
||
| 916 | public function RemoveAllAndEditLink() |
||
| 920 | |||
| 921 | /** |
||
| 922 | * set new specific new quantity for buyable's orderitem. |
||
| 923 | * |
||
| 924 | * @param float |
||
| 925 | * |
||
| 926 | * @return string (Link) |
||
| 927 | */ |
||
| 928 | public function SetSpecificQuantityItemLink($quantity) |
||
| 932 | |||
| 933 | /** |
||
| 934 | * @return string |
||
| 935 | */ |
||
| 936 | public function AddToCartAndGoToCheckoutLink() |
||
| 943 | |||
| 944 | /** |
||
| 945 | * Here you can add additional information to your product |
||
| 946 | * links such as the AddLink and the RemoveLink. |
||
| 947 | * One useful parameter you can add is the BackURL link. |
||
| 948 | * |
||
| 949 | * Usage would be by means of |
||
| 950 | * 1. decorating product |
||
| 951 | * 2. adding a updateLinkParameters method |
||
| 952 | * 3. adding items to the array. |
||
| 953 | * |
||
| 954 | * You can also extend Product and override this method... |
||
| 955 | * |
||
| 956 | * @return array |
||
| 957 | **/ |
||
| 958 | protected function linkParameters() |
||
| 970 | |||
| 971 | //TEMPLATE STUFF |
||
| 972 | |||
| 973 | /** |
||
| 974 | * @return bool |
||
| 975 | */ |
||
| 976 | public function IsInCart() |
||
| 980 | |||
| 981 | /** |
||
| 982 | * @return EcomQuantityField |
||
| 983 | */ |
||
| 984 | public function EcomQuantityField() |
||
| 990 | |||
| 991 | /** |
||
| 992 | * returns the instance of EcommerceConfigAjax for use in templates. |
||
| 993 | * In templates, it is used like this: |
||
| 994 | * $EcommerceConfigAjax.TableID. |
||
| 995 | * |
||
| 996 | * @return EcommerceConfigAjax |
||
| 997 | **/ |
||
| 998 | public function AJAXDefinitions() |
||
| 1002 | |||
| 1003 | /** |
||
| 1004 | * @return EcommerceDBConfig |
||
| 1005 | **/ |
||
| 1006 | public function EcomConfig() |
||
| 1010 | |||
| 1011 | /** |
||
| 1012 | * Is it a variation? |
||
| 1013 | * |
||
| 1014 | * @return bool |
||
| 1015 | */ |
||
| 1016 | public function IsProductVariation() |
||
| 1020 | |||
| 1021 | /** |
||
| 1022 | * returns the actual price worked out after discounts, currency conversions, etc... |
||
| 1023 | * |
||
| 1024 | * @casted |
||
| 1025 | * |
||
| 1026 | * @return float |
||
| 1027 | */ |
||
| 1028 | public function CalculatedPrice() |
||
| 1042 | |||
| 1043 | /** |
||
| 1044 | * How do we display the price? |
||
| 1045 | * |
||
| 1046 | * @return Money |
||
| 1047 | */ |
||
| 1048 | public function CalculatedPriceAsMoney() |
||
| 1056 | |||
| 1057 | //CRUD SETTINGS |
||
| 1058 | |||
| 1059 | /** |
||
| 1060 | * Is the product for sale? |
||
| 1061 | * |
||
| 1062 | * @return bool |
||
| 1063 | */ |
||
| 1064 | public function canPurchase(Member $member = null, $checkPrice = true) |
||
| 1091 | |||
| 1092 | /** |
||
| 1093 | * standard SS Method |
||
| 1094 | * we explicitely set this to give access in the API. |
||
| 1095 | * |
||
| 1096 | * @return bool |
||
| 1097 | */ |
||
| 1098 | public function canView($member = null) |
||
| 1106 | |||
| 1107 | /** |
||
| 1108 | * Shop Admins can edit. |
||
| 1109 | * |
||
| 1110 | * @return bool |
||
| 1111 | */ |
||
| 1112 | public function canEdit($member = null) |
||
| 1123 | |||
| 1124 | /** |
||
| 1125 | * Standard SS method. |
||
| 1126 | * |
||
| 1127 | * @return bool |
||
| 1128 | */ |
||
| 1129 | View Code Duplication | public function canDelete($member = null) |
|
| 1138 | |||
| 1139 | /** |
||
| 1140 | * Standard SS method |
||
| 1141 | * //check if it is in a current cart? |
||
| 1142 | * |
||
| 1143 | * @return bool |
||
| 1144 | */ |
||
| 1145 | View Code Duplication | public function canDeleteFromLive($member = null) |
|
| 1154 | |||
| 1155 | /** |
||
| 1156 | * Standard SS method. |
||
| 1157 | * |
||
| 1158 | * @return bool |
||
| 1159 | */ |
||
| 1160 | View Code Duplication | public function canCreate($member = null) |
|
| 1169 | |||
| 1170 | /** |
||
| 1171 | * finds similar ("siblings") variations where one |
||
| 1172 | * attribute value is NOT the same. |
||
| 1173 | * |
||
| 1174 | * @return DataList |
||
| 1175 | */ |
||
| 1176 | public function MostLikeMe() |
||
| 1207 | } |
||
| 1208 |
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.