5

我正在尝试研究如何改进我编写的一些代码,该代码使用一些规则来计算汽车保险的一些不同定价。这是困扰我的部分:

public Insurance GetInsurance(CarData carData)
{
    var insurance = new Insurance();

    insurance.priceGeneral = this.CalculatePrice(new Car { BrandDealer = false, MonthPayment = false, CarData = carData });
    insurance.priceGeneralMonth = this.CalculatePrice(new Car { BrandDealer = false, MonthPayment = true, CarData = carData });
    insurance.priceBrandDealer = this.CalculatePrice(new Car { BrandDealer = true, MonthPayment = false, CarData = carData });
    insurance.priceBrandDealerMonth = this.CalculatePrice(new Car { BrandDealer = true, MonthPayment = true, CarData = carData });

    return insurance;
}

请注意,计算每月付款与正常价格(每年付款)之间存在显着差异,并且取决于 BrandDealer 是真是假,它的计算方式也不同。我试图消除这个变量,但客户要求这些规则。

我知道某些属性不是“汽车”的实际属性,但我也会尽快对其进行重构。

我为这个计算做了 4 次而烦恼,以后会有更多的规则。即将到来的新规则将添加另一个布尔值和另外 2 个计算。

是否有一些不错的设计模式我没有在这里发现我应该使用?

4

3 回答 3

3

首先,让我们解决您今天遇到的问题,这是一种清理重复计算代码的方法。

首先,我们需要将 Stratgey 模式应用到您的价格计算中,定义计算接口并将不同的计算逻辑代码移动到它们的新位置:

// calculation common interface
public interface IPriceCalculation 
{
   public InsurancePrice CalculatePrice(CarData data);   
}

// result from the calculation
public class InsurancePrice
{
   public string Description { get; set; }
   public decimal Price { get; set; }      
}


// concrete implementations 
public class BrandDealerMonthlyPaymentCalculation : IPriceCalculation
{
   public InsurancePrice CalculatePrice(CarData data)
   {
      // logic to perform calculation of BrandDealer = true, MonthPayment = true

      // just for example...
      return new InsurancePrice() 
      { 
         Description = "Policy price with a Brand dealer and monthly payments",
         Price = 250.25;
      };
   }
}

public class BrandDealerYearlyPaymentCalculation : IPriceCalculation
{
   public InsurancePrice CalculatePrice(CarData data)
   {
      // logic to perform calculation of BrandDealer = true, MonthPayment = false
   }
}

public class NonBrandDealerYearlyCalculation : IPriceCalculation
{
   public InsurancePrice CalculatePrice(CarData data)
   {
      // logic to perform calculation of BrandDealer = false, MonthPayment = false
   }
}

public class NonBrandDealerMonthlyCalculation : IPriceCalculation
{
   public InsurancePrice CalculatePrice(CarData data)
   {
      // logic to perform calculation of BrandDealer = false, MonthPayment = true
   }
}

定义计算后,您可以安装它们。在定义 GetInsurance 方法的类(我们称之为 InsuranceFactory)中,我们将在您的 ctor 中执行此操作。这可以通过另一个类通过属性、通过配置、通过 DI 等将它们推入来完成,但是为了说明,ctor 是最简单的:

public class InsuranceFactory
{
   private List<IPriceCalculation> _priceCalculators = new List<IPriceCalculation>();

   public InsuranceFactory()
   {
      _priceCalculators.Add(new BrandDealerYearlyPaymentCalculation());
      _priceCalculators.Add(new BrandDealerMonthlyPaymentCalculation());
      _priceCalculators.Add(new NonBrandDealerYearlyCalculation());
      _priceCalculators.Add(new NonBrandDealerMonthlyCalculation());
      // easy to add more calculations right here...
   }

}

接下来我们重新访问上面 InsuranceFactory 类中的 GetInsurance 方法:

public Insurance GetInsurance(CarData carData) 
{     
   var insurance = new Insurance();      

   // iterate the different pricing models and them to the insurance policy results

   foreach (IPriceCalculation calculator in _priceCalculators)
   {
      insurance.PriceOptions.Add(calculator.CalculatePrice(carData));
   }

   return insurance; 

} 

请注意,您的 GetInsurance 方法不再需要在每次创建新计算时进行更改。同样,通过将结果存储在保险对象 ( insurance.PriceOptions) 中的列表中,您的保险类别也不需要更改。您的 UI 代码可以通过迭代该列表来呈现所有选项。这个例子稍微简化了一点,但应该可以帮助你。

现在谈谈我可以预料到的可能的第二个问题。如果你的计算子类开始有额外的排列,你将会有一个类爆炸。例如,现在您有 2 个因素(Brand 和 PaySchedule),每个因素有 2 个选择,给您 2 x 2 = 4 个类。但是,如果我们将 CreditScore 添加到其中,有 3 个选项(Goor、Fair、Poor)会怎样。然后你得到:

GoodCreditBrandDealerYearlyPaymentCalculation
GoodCreditBrandDealerMonthlyPaymentCalculation
GoodCreditNonBrandDealerYearlyCalculation
GoodCreditNonBrandDealerMonthlyCalculation
FairCreditBrandDealerYearlyPaymentCalculation
FairCreditBrandDealerMonthlyPaymentCalculation
FairCreditNonBrandDealerYearlyCalculation
FairCreditNonBrandDealerMonthlyCalculation
PoorCreditBrandDealerYearlyPaymentCalculation
PoorCreditBrandDealerMonthlyPaymentCalculation
PoorCreditNonBrandDealerYearlyCalculation
PoorCreditNonBrandDealerMonthlyCalculation

这只会从这里变得更糟。如果出现,这确实值得自己提问和回答,但这是您应该注意的事情。如果它开始变成这样,请重构 Calculation 类。但好的是 GetInsurance 中的代码仍然不需要更改。

于 2012-05-21T20:53:23.137 回答
0

真正突出的一件事是每次计算时都会创建 4 个新的 Car 对象。这完全有必要吗?您可以使用不同的 BrandDealer/MonthlyPayment 组合预先创建(并存储)其中的 4 个,然后仅使用它们来计算传入适当的 CarData 吗?

此外,使用某种计算工厂可能会更好地实现这一点,该计算工厂抽象/隐藏所需的不同 Car 对象和其他计算细节。考虑为每种计算类型添加内部方法,这样对于新的计算,您只需添加另一种方法,而不是修改一个大方法。通常,这可以通过添加计算对象而不是方法来实现,但这对于这种特殊情况可能是多余的。

于 2012-05-20T11:25:58.693 回答
0

您需要做的是将计算分解为更小的组件。您需要找到通用代码并将其保存在地图中。假设 calc 1 = A * B + C. 和 calc 2 = A * C - b

您只需要执行一次 A , B ,C 并将其保存在 hashmap 中。现在计算 2 = hash.getOrAdd(A key) * hash.getOrAdd(C key) - hash.getOrAdd(C key)。

如果您找不到任何通用代码,那么您很不走运,需要执行所有计算。

于 2012-05-20T10:14:32.990 回答