如何改进这段代码?

时间:2011-09-03 04:56:49

标签: java design-patterns

public class OrderProcessor {

  public Double calculateTotalPriceWithDiscountCode(Order order,
        char discountCode) {

    Double itemTotal = order.getItemTotal();
    Double discountAmount = 0.0;

    switch (discountCode) {
    case 'A':
        discountAmount = 0.95 * itemTotal;
        break;
    case 'B':
        discountAmount = 0.15 * itemTotal;
        break;
    }
    return itemTotal - discountAmount;
}

订单处理器中的当前实现已关闭以进行扩展并打开以进行修改以添加新的折扣代码,如何改进设计以摆脱此限制

6 个答案:

答案 0 :(得分:3)

一般来说,switch的存在对于应该是什么类而言是一个相当不错的赠品。所以,第一次尝试就是这样:

public interface Discounter {
  public double applyDiscount(double itemTotal);
}

public class OrderProcessor {
  private Map<Char, Discounter> discounts = new HashMap<Char, Discounter>();

  public void addDiscounter(Char discountCode, Discounter discounter) {
    discounts.put(discountCode, discounter);
  }

  public Double calculateTotalPriceWithDiscountCode(Order order, char discountCode) {
    double itemTotal = order.getItemTotal();
    double discountAmount = 0.0;

    if (discounts.hasKey(discountCode))
      discountAmount = discounter.applyDiscount(itemTotal);

    return itemTotal - discountAmount;
  }
}

然后通过以下方式扩展:

processor.addDiscounter('A', new Discounter() {
  public double applyDiscount(double itemTotal) {
    return 0.95 * itemTotal;
  }
});

你也可以制作一个删除方法,然后你的折扣店可能会变得更加复杂,咨询外部数据等。你可能想要打开一点界面并传递整个订单以进行更多检查。

注意:如果这是你将要在制作中做的事情,我有两条未经请求的建议:

  1. 考虑使用JBoss Drools之类的东西来处理您的业务逻辑,而不是这样;它更强大,更灵活。

  2. 请勿使用double进行实际财务计算。 :)

答案 1 :(得分:2)

正如您所指出的,折扣代码需要更改,最好从代码中分离出实际的折扣代码。在xml或设置文件中维护折扣代码,并在使用之前将值延迟加载到字典/哈希集中。

例如,你的xml看起来像这样,

<Discounts>
  <Discount code="A" value="0.05"/>
  <Discount code="B" value="0.10"/>
  <Discount code="C" value="0.15"/>
</Discounts>

calculateTotalPriceWithDiscountCode中,使用从此xml读取的值填充字典。

if(discountsDictionary == null)
{    
    discountsDictionary = LoadValuesFromXml(xmlFilePath);
}

如果在程序执行期间您的折扣xml可能会发生变化,那么当您需要折扣值时(与在上面的代码片段中加载一次相比),请执行Load操作。

然后访问代码(密钥)以检索折扣(值),

if(discountsDictionary.ContainsKey(discountCode))
{
    discount = discountsDictionary[discountCode];
    discountedItemPrice = itemPrice * ( 1 - discount);
}
else
{
     // Invalid discount code...
}

答案 2 :(得分:1)

将基于订单计算总计的逻辑拉入其自己的类/接口:

class OrderProcessor {
    // Probably inject this or load it some other way than a static factory
    private Collection<TotalCalculator> calculators = TotalCalculatorFactory.getTotalCalculators();

    public Double calculateTotalPriceWithDiscountCode(Order order, char discountCode) {
        for (TotalCalculator calculator : calculators) {
            if (calculator.supports(discountCode)) {
                return calculator.calculateTotal(order);
            }
        }
        return order.getItemTotal();
    }
}

class TotalCalculator {
    private char discountCode;
    private double discountRatio;

    public TotalCalculator(char discountCode, double discountRatio) {
        this.discountCode = discountCode;
        this.discountRatio = discountRatio;
    }

    public boolean supports(char discountCode) {
        return this.discountCode == discountCode;
    }

    public Double calculateTotal(Order order) {
        return order.getItemTotal() - order.getItemTotal() * discountRatio;
    }
}

class TotalCalculatorFactory {
    public static Collection<TotalCalculator> getTotalCalculators() {
        return Arrays.asList(
                new TotalCalculator('A', 0.95),
                new TotalCalculator('B', 0.15)
        );
    }
}

在一个真实的系统中,我会将TotalCalculator设置为一个界面,这样可以获得额外的优势,即能够以其他方式计算订单总数,而不仅仅是折扣百分比。

此解决方案还非常灵活,允许您使用除手动将其编码到工厂之外的其他机制来创建TotalCalculator(或接口实现)。例如,您可以使用IoC容器创建和注入它们,或使用ServiceLoader来查找和加载它们。

答案 3 :(得分:0)

在此calculateTotalPriceWithDiscountCode函数中,将折扣代码作为参数传递不是一个好主意。好吧,第三个人,审查你的代码,不理解会不明白它是什么意思除了你,一种代码味道。

其中一个建议是您需要创建另一个名为Discount的类,并将Discount对象作为参数传递,并从其公共方法中获取内部值。

public class Discount {
//Use a hash map to store your Code associate with your discountAmount

    public Discount(char discountCode){
        this.discountCode = discountCode
    }

    public int getDiscountAmount(){
        ....
       ....
    }
}

目前,您实际需要修改的内容仅为Discount课程,您的calculateTotalPriceWithDiscountCode无需关注。

答案 4 :(得分:0)

我的想法是分开维持折扣。

public class OrderProcessor {

  public Double calculateTotalPriceWithDiscountCode(Order order,
    char discountCode) {

   Double itemTotal = order.getItemTotal();
 return itemTotal - (itemTotal* Discounts.GetDiscounts(discountCode));
  }
}

////////////////

class Discounts
{
 public static double GetDiscounts(char discountCode)
    {
       switch (discountCode) {
case 'A':
    return 0.95d;

case 'B':
    return 0.15d;

default:
      return 0.00d;
  }
 }
}

答案 5 :(得分:-2)

您可以使用单独的xml文件来存储代码及其计算机制。

这将消除添加新折扣代码的无法限制。

XML文件:discounts.xml

<discount-codes>
    <discount-code>
       <code>A</code>
       <val>0.15</val>
    </discount-code>
    <discount-code>
        <code>B</code>        
        <val>0.95</val>
    </discount-code>
</discount-codes>

注意:操作代码(我打算如何处理这些值?)目前尚未实现。您可以在最后实现相同的目标。

使用XML解析器类:

类:DiscountModel.java(此类是存储折扣代码的模型)

public class DiscountModel {
    char code;
    Double val;

    // Implement getters and setters
}

类:DiscountParser.java(这将解析discounts.xml文件并将代码存储在列表中)

public class DiscountParser {
    List<DiscountModel> discountsList;

    // Getters and Setters for discountsList

    // Parser Code
    public void parseDiscounts() {
         // Code here
    }

    // Add new discount
    public void addDiscount() {
        // Code 
    }

    // Remove discount
    public void removeDiscount () {
       // Code
    }
}

类:OrderProcessor.java(这将在计算后显示贴现值)

/**
 *  Call this class when calculations need to be done.
 */
public class OrderProcessor {
    // Declare instance of DocumentParser
    DocumentParser dc1;

    // Getter and setter for dc1

    public Double calculateTotalPriceWithDiscountCode(Order order, char discountCode) {
        // Find the corresponding discountcode and 
        // value from the list of values in the 
        // Class DocumentParser          

        // Use the corresponding values to calculate 
        // the discount and return the value
    }
}  

每当要添加新代码时,您都可以将其插入到xml文件中。如果需要删除折扣代码,则同样适用。

希望以上有所帮助。