I'm in the process of re-writing a method I've come across in our code base. It's pretty lengthy and a lot of it seemed redundant. It's a Discount object class member. It operates on the discounts DiscountRules property.
DiscountRule can have behave as follows:
- Can apply to all brands and a single category
- Can apply to all categories and a single brand
- Can apply to a single category and a single brand
In addition to these few simple rules, there's also "include/exclude" functionality.
Example rules:
- All brands (include) in Televisions (exclude)
- Pioneer (include) in All categories (exclude)
- Pioneer (exclude) in All categories (include)
- Pioneer (include) in Televisions (include)
etc...
This would make the discount valid for any brands not in the 'Televisions' category (1st example). This is figured out by looking at the Brand + Category ID that a product in the basket falls under, if they match, it checks that the rule has both BrandInclude and CategoryInclude (include/exclude flags) set for the rule (either one of these being false would invalidate the whole).
Here is the original method:
public bool IsDiscountValidForProduct(int brandID, int categoryID) { foreach (DiscountRule rule in this.DiscountRules) { // Rule is all brands, no need to check brand id if (rule.AllBrands && rule.CategoryId == categoryID) { // Check is passed, now we just need to check the inclusion/exclusion rules if (rule.IncludesCategoriesAndBrands) { return true; } else if (!rule.IncludesCategoriesAndBrands) { return false; } else { // It's a mix of exclude + include if (rule.BrandInclude && !rule.CategoryInclude) { // no need for brand lookup, it's all brands, just make sure categoryID from rule not = categoryID param return rule.CategoryId != categoryID; } else if (rule.CategoryInclude && !rule.BrandInclude) { // Since this is all brands, and brands are excluded, no need to check category, return false return false; } } } // Rule is all categories, no need to check category id else if (rule.AllCategories && rule.BrandId == brandID) { if (rule.IncludesCategoriesAndBrands) { return true; } else if (!rule.IncludesCategoriesAndBrands) { return false; } else { // It's a mix of exclude + include - includes brand but not category if (rule.BrandInclude && !rule.CategoryInclude) { // Rule is category exclude, since we're checking under all categories, no need for lookup, return false return false; } else if (rule.CategoryInclude && !rule.BrandInclude) { // rule is all categories, no need to check category id, just look at the brand return rule.BrandId != brandID; } } } else if (rule.BrandId == brandID && rule.CategoryId == categoryID) { if (rule.IncludesCategoriesAndBrands) { return true; } // If either category or brand exclude or both, rule is invalid else if (!rule.IncludesCategoriesAndBrands || (!rule.CategoryInclude || !rule.BrandInclude)) { return false; } } } return true; } Here is the refactored version:
public bool IsDiscountValidForProduct(int brandID, int categoryID) { foreach (DiscountRule rule in this.DiscountRules) { if (rule.AllBrands && rule.CategoryId == categoryID || rule.AllCategories && rule.BrandId == brandID || rule.BrandId == brandID && rule.CategoryId == categoryID) { return rule.IncludesCategoriesAndBrands; } } return true; } How can I further improve this (short of adding comments)? What would you do differently and why?
IncludesCategoriesAndBrandsmean, and why is that being returned? Why istruereturned if no rules match? \$\endgroup\$IncludesCategoriesAndBrandsis a property ofDiscountRule. It's an&&ofCategoryIncludeandBrandInclude, the include/exclude flags. If a rule matches a product, it can only be valid if bothCategoryIncludeandBrandInclude. You raise an excellent point about returning true when no rules match, looks like a bug to me. I suspect the logic should be "if no rules, return true". Good spot. It also highlights another issue in not checking rules if a match occurs on a single ID, e.g. if only the brand matches, it should check whether this is include/exclude or not. \$\endgroup\$DiscountRule'sCategoryId, but not itsBrandId, but theDiscountRulehasCategoryIncludetrue andBrandIdfalse, shouldn't that give the result true rather than false? Also why would no rules return true? A rule seems like something inclusionary, rather than exclusionary. As in, adding a new rule could only add (brandId,categoryId) pairs which would be discounted (or leave them the same), never remove any \$\endgroup\$DiscountRuleis not required on a discount, if it has no restrictions it should just be true by default. \$\endgroup\$