2010-07-30 23 views
7

Tôi có kịch bản sau đây nơi tôi có các loại thuật toán bán hàng khác nhau để tính toán giá bán. FixedSaleStrategy không cần tham số basePrice trong khi tất cả các triển khai chiến lược khác cần nó. Có cách nào tốt để tránh tham số dư thừa này?Có cách nào tốt để tránh tham số phương thức không sử dụng trong một số lớp con trong khi áp dụng mẫu chiến lược?

public abstract class SalesStrategy 
{ 
    public abstract double GetPrice(double basePrice, double saleAmount); 
} 
public class AmountOffSale : SalesStrategy 
{ 
    public override double GetPrice(double basePrice, double salesAmount) 
    { 
     return basePrice - salesAmount; 
    } 
} 
public class FixedPriceSale : SalesStrategy 
{ 
    public override double GetPrice(double basePrice, double salesAmount) 
    { 
     return salesAmount; 
    } 
} 
+2

Điều đầu tiên đầu tiên ... thay đổi tham số của bạn thành 'thập phân' thay vì' double'. ** Không bao giờ ** sử dụng số điểm nhị phân để xử lý tiền tệ vì chúng không thể đại diện chính xác các giá trị tiền tệ, ngay cả các giá trị đơn giản như '1.1', và làm tròn lỗi là rất quan trọng khi giao tiền của mọi người. Xem: http://en.wikipedia.org/wiki/Floating_point#Representable_numbers.2C_conversion_and_rounding –

Trả lời

6

Tại cốt lõi của mẫu chiến lược là ý tưởng rằng mã gọi điện không biết thực hiện được gọi.

Nếu bạn thay đổi thông số được sử dụng cho mỗi lần triển khai, bạn sẽ thấy rằng bạn không nhận được đầy đủ lợi ích của mẫu này: người gọi sẽ cần biết triển khai nào sẽ được sử dụng và cách gọi.

Điều tôi có xu hướng làm là chuyển một lớp chứa siêu thông tin (như PriceInfo), luôn được điền theo cùng một cách (Lý tưởng tập trung vào mã) và sự khác biệt duy nhất là việc triển khai chiến lược.

Một trong những lợi ích là tôi có thể thêm thuộc tính vào lớp PricingInfo không liên quan trong quá khứ (ví dụ: systemDiscount) và tác động đến toàn bộ hệ thống không quá lớn.

+0

Nhận xét tốt về PricingInfo. Bao bọc thông tin thực thể cơ bản (ví dụ: từ 'Sản phẩm' hoặc 'OrderItem') vào một PricingInfo, cũng cho phép nhiều loại thực thể hơn (chi phí vận chuyển?) Được đưa vào hệ thống sau này .. –

+0

Và giao diện PriceInfo hoặc tương tự 'trả lời' , cũng giúp tập trung thiết kế API của bạn tốt hơn; vào 'thuật toán cần biết', thay vì 'làm cách nào tôi có thể hack điều này từ các trường trong thực thể hiện tại của tôi'. Tôi thấy điều này giúp làm sạch & làm rõ mã của tôi, rất hiệu quả. –

0

Không tốt, theo ý kiến ​​của tôi. Tôi sẽ giữ nó như vậy. Có nhiều thủ thuật khác nhau mà bạn có thể sử dụng như params (có một tham số đơn double [] priceData) hoặc IDynamicObject. Nhưng sạch nhất là chỉ để có một số chiến lược bỏ qua các tham số thêm.

2

Nếu bạn đang sử dụng C# 4.0, bạn có thể đảo ngược các thông số và làm cho basePrice tùy chọn như vậy:

public abstract class SalesStrategy 
{ 
    public abstract double GetPrice(double saleAmount, double basePrice = 0d); 
} 

public class AmountOffSale : SalesStrategy 
{ 
    public override double GetPrice(double salesAmount, double basePrice) 
    { 
     return basePrice - salesAmount; 
    } 
} 

public class FixedPriceSale : SalesStrategy 
{ 
    public override double GetPrice(double salesAmount, double basePrice = 0d) 
    { 
     return salesAmount; 
    } 
} 

Ý nghĩa sau đây có thể được thực hiện ...

FixedPriceSale fixedPrice = new FixedPriceSale(); 
... 
fixedPrice.GetPrice(salesAmount); 

Note rằng thông số AmountOffSale của basePricekhông tùy chọn, có nghĩa là thông tin sau sẽ không biên dịch:

AmountOffSale amountOffSale = new AmountOffSale(); 
... 
// No overload for method 'GetPrice' takes 1 arguments 
amountOffSale.GetPrice(salesAmount); 
+0

Tôi vừa nhập vào câu trả lời tương tự này; sau đó nhận thấy câu trả lời của bạn khi tôi đang xác minh mã :-). – Jess

5

No. Không phải là tham số dự phòng; mã sử dụng một SalesStrategy không nên biết lớp bê tông nào đang sử dụng, vì vậy chữ ký phương thức phải giống hệt nhau trong tất cả các lớp dẫn xuất.

0

Một cách hay để loại bỏ các tham số không liên quan khỏi giao diện là truyền các tham số đó trong các hàm tạo từ các lớp con. Vì vậy, một thay thế cho thiết kế của bạn sẽ là:

public interface SalesStrategy 
    { 
     double CalculatePrice(double basePrice); 
    } 

public class FixedPriceSale : SalesStrategy 
    { 
     public double CalculatePrice(double basePrice) 
     { 
      return basePrice; 
     } 
    } 

public class AmountOffSale : SalesStrategy 
    { 
     public double SalesAmount { get; set; } 

     public AmountOffSale(double salesAmount) 
     { 
      this.SalesAmount = salesAmount; 
     } 

     public double CalculatePrice(double basePrice) 
     { 
      return basePrice - SalesAmount; 
     } 
    } 

Trong công trình đó bạn không gây ô nhiễm giao diện của bạn với dữ liệu cụ thể từ lớp con.

+2

Tôi nghĩ rằng vấn đề với cách tiếp cận này là tại một số điểm, một cái gì đó cần phải tạo một lớp con 'SalesStrategy' và ** điều đó sẽ cần phải biết basePrice. Vì vậy, hiệu quả điều này di chuyển các tham số thêm từ phương thức gọi đến phương thức factory. Vẫn sẽ có một nơi mà thông số này không được sử dụng. –

0

Một cách khác là sử dụng đối tượng thông số hoặc Dictionary<string, object>. Bằng cách này bạn có thể hợp nhất số tham số trên mỗi phương thức và để lại chỗ cho các tham số bổ sung nên có sự thay đổi về yêu cầu trong tương lai.

Một nhược điểm là Dictionary<string, object> có thể theo dõi các thông số khó hơn trong mã của bạn, nơi mà đối tượng thông số sẽ có tất cả các thuộc tính mà bạn có thể xem trong mã của mình.

+0

Từ điển không có cấu trúc để thiết kế tốt trong trường hợp với các thuộc tính đơn giản và được xác định rõ, chẳng hạn như định giá - và cũng không hiệu quả lắm. Họ có các ứng dụng như cấu hình hoặc tùy chọn cho các mô-đun con phức tạp, hoặc khả năng mở rộng rất mở - nhưng tốt nhất là được sử dụng lúc khởi tạo/cấu hình thời gian, thay vì tính toán. –

+0

Tôi đồng ý - đó là về việc trao đổi giữa việc tạo chữ ký phương thức phức tạp, mã hiệu quả, khả năng đọc, v.v. –

Các vấn đề liên quan