2009-06-23 26 views
7

Tôi có xu hướng viết thành công các ứng dụng nhỏ gọn có thể đóng gói nhiều logic nghiệp vụ một cách đơn giản và không dự phòng. Tôi có xu hướng tạo ra các phương thức nhỏ, nhưng theo thời gian tôi đến các phương thức có quá nhiều tham số. Tôi biết mọi mã yêu cầu thiết kế của nó, nhưng tôi thấy một mô hình chống trong hành vi của tôi và tôi không chắc chắn đó sẽ là cách tốt nhất để chiến đấu chống lại nó.Làm thế nào để tránh nhiều thông số trong các phương pháp quan trọng của các ứng dụng của tôi?

Một tình huống điển hình sẽ là một cái gì đó như:

public loanStructure CalculateSomeComplicatedInterestRate(enum clientType, 
     int totalAmout, bool useTaxes, bool doYearlySummary, bool doNotReloadClient, etc) 
    { 
     loanStructure ret = new loanStructure(); 
     if (doNotReloadClient) ... ; 
     ... 
     if (doYearlySummary) GroupResults(ret); 
     return ret; 
    } 

và bên trong phương pháp này, một cây của các cuộc gọi chuyển tiếp các thiết lập boolean (doYearlySummary, doNotReloadClient, vv) để quy tắc kinh doanh khác nhau mà tác động lên tính.

IE, sự cố không nằm trên thực tế là tất cả các tham số có thể được đóng gói thành đối tượng (bigParameterStructure) ... Tôi không thoải mái với mẫu masterMethod, nhưng việc thực hiện quá tải như CalculateSomeComplicatedInterestRateMonthly và CalculateSomeComplicatedInterestRateYearly sẽ chỉ ẩn riêng CalculateSomeComplicatedInterestRate .... một số vấn đề !!

Trong thô thiết kế đối tượng định hướng sẽ giúp ... nhưng tôi vẫn kết thúc-up có loại phương pháp ở đâu đó trên đối tượng của tôi ...

Vâng guys ... bất kỳ sự giúp đỡ được chào đón.

Pablo.

+0

Logic nghiệp vụ là gì. Nếu nó phức tạp, nó phức tạp, không phải lỗi của bạn. Điều này có vẻ tốt với tôi, trừ khi bạn có ví dụ về điều này trong mã phi logic kinh doanh, quá? –

Trả lời

4

Nếu bạn thấy mình đang chuyển đổi giữa một vài tập hợp nhiều thông số, hãy xem xét việc tách mã theo phương thức phức tạp riêng và cung cấp một số phương pháp công khai lấy ít tham số hơn và gọi phương thức riêng tư cung cấp mặc định phù hợp cho một phần tham số. Điều này sẽ làm sạch giao diện công cộng. Sau đó, trừ khi bạn thực sự không thể làm điều đó vì lý do hiệu suất (không có khả năng), hãy chia phương thức riêng thành các cuộc gọi đến các phương thức riêng tư khác, để giữ cho mã dễ đọc hơn. Bạn có thể loại bỏ các nhận xét theo cách này, bằng cách chọn các tên phương thức tự mô tả. Mặt khác, nếu một số tham số không phải là về GÌ để làm, nhưng LÀM THẾ NÀO để làm điều đó (tức là bạn có thể có một enum chuyển đổi giữa lãi kép đơn giản và liên tục), xem xét sử dụng đa hình và có hai lớp thực hiện một giao diện chung, mỗi giao diện theo một cách khác nhau, HOẶC ủy thác chức năng này cho một thành phần của lớp chính có thể được thay đổi từ điều này sang thực thi đó (a'la "dependency injection"). Cách tiếp cận thứ hai sẽ làm cho mã của bạn linh hoạt hơn, vì người dùng trong lớp của bạn sẽ có thể cung cấp các thành phần của riêng họ và thay đổi hành vi của lớp chính mà không cần chạm vào mã của nó.

Bạn cũng nên suy nghĩ về những người sử dụng lớp học của bạn. Có lẽ danh sách tham số trong phương thức của bạn quá dài, vì phương thức đã phát triển theo thời gian để chứa các nhu cầu khác nhau của người dùng khác nhau ("người dùng" trong ngữ cảnh này có nghĩa là một đoạn mã khác, thậm chí được viết bởi bạn)? Nếu vậy, ít nhất tôi sẽ tạo ra một phương pháp cho một mục đích, một phương pháp khác cho một mục đích khác. Họ vẫn có thể chia sẻ mã chung. Lý tưởng nhất, bạn sẽ muốn có hai lớp học phục vụ hai mục đích, để có thể thay đổi chúng trong tương lai mà không vi phạm các chức năng khác.

2

Tôi thường tạo cấu trúc "bộ lọc" mà tôi có thể chuyển cho các phương pháp đó, vì vậy trong trường hợp của bạn.

(nhanh chóng và dơ bẩn, không chất lượng sản xuất.)

public struct InterestRateCalculationFilter 
{ 
    public enum ClientType {get;set;} 
    public int TotalAmount {get;set;} 
    public bool UseTaxes {get;set;} 
    public bool DoYearlySummary {get;set;} 
    public bool DoNotReloadClient {get;set;} 

    //Constructors go here, set defaults, etc. 
} 

Cách sử dụng:

InterestRateCalculationFilter filter = new InterestRateCalculationFilter(); 
filter.ClientType = Customer; 
filter.TotalAmount = 700; 
filter.UserTaxes = true; 
filter.DoYearlySummary = false; 

LoanStructure loanStructure = CalculateSomeComplicatedInterestRate(filter); 

Đây không phải là luôn luôn là một ý tưởng tốt, nhưng tôi đi đến nó thường xuyên, khi tham số danh sách có hơn mười mục và chúng không nhất thiết phải rơi vào cấu trúc đối tượng độc đáo.

+1

Tốt hơn là không để cho http://stackoverflow.com/users/23354/marc-gravell thấy những cấu trúc đó, hoặc anh ta sẽ trên tất cả các bạn như phát ban ;-) fwiw mặc dù, tôi thấy lớp học hơn là cấu trúc hữu ích hơn (vì struct được truyền qua bản sao) trong trường hợp này ** _ if _ ** callee sửa đổi các giá trị đầu vào, nếu không bạn phải chuyển qua ref. Ngoài ra nhìn vào khởi tạo đối tượng (csc> = 3 tôi nghĩ) để cắt giảm chất béo khi sử dụng. – si618

+0

Bạn mang lại một điểm tốt. Tôi đã luôn luôn sử dụng cấu trúc để đại diện cho các loại điều này, mặc dù tôi thấy rất nhiều người đang nói đến sử dụng các lớp học. Nhưng nó làm cho tôi tự hỏi nơi mà các cấu trúc phù hợp với không gian phát triển ứng dụng ngày nay. Tới Google !! –

+0

Tôi đồng ý về cú pháp khởi tạo đối tượng, nhưng anh ta không chỉ định một ngôn ngữ trong câu hỏi của anh ta, và tôi không muốn ném một quả bóng quá lớn vào đó bằng cú pháp. Lưu ý tuyên bố từ chối trách nhiệm :) –

1

Cách tiếp cận này có nhược điểm nhất định, nhưng có một cách để đơn giản hóa các danh sách đối số là tạo ra một "lý lẽ" class mà có các tính chất để giữ tất cả các thông số có thể được thông qua vào.

Ví dụ (C#) :

public class InterestCalculationArguments // or maybe a struct... 
{ 
    public EClientType ClientType {get;set;} 
    public int TotalAmount {get;set;} 
    // etc. 
} 

một điều tốt đẹp với phương pháp này là bạn có thể phân lớp lớp đối số để mở rộng các đối số có thể được thông qua tại, hoặc bạn cũng có thể thêm một "NameValueCollection" (ví dụ như bản đồ chuỗi string) để cho phép người gọi chuyển qua các đối số bổ sung mà phương thức có thể xử lý. Bạn có thể sử dụng cách tiếp cận tương tự này cho đối tượng trả về, một lớp để chứa các giá trị trả về khác nhau. Cách tiếp cận này cũng có thể cách ly người gọi từ những thay đổi nhỏ cho đối số/đối tượng trả về.

+0

ý tưởng tuyệt vời! Tôi sẽ sử dụng nó ngay bây giờ – chester89

2

Một thay thế cho "đối số struct" cách tiếp cận, thường hoạt động tốt hơn trong những trường hợp phức tạp, là để di chuyển logic của một phương pháp lớn để một lớp riêng biệt:

InterestRateCalculator rateCalc = new InterestRateCalculator(); 
rateCalc.ClientType = ... 
rateCalc.TotalAmount = ... 
rateCalc.CalculateSomeComplicatedInterestRate(); 

Nó có thể được kết hợp với một loại nhà máy:

InterestRateCalculator myCalc 
    = sharedCalc.MakeMeAnInterestRateCalculator(); 

Và kể từ khi bạn đề cập đến chức năng quá tải để đơn giản hóa danh sách đối số, bạn đôi khi có thể thay thế đối số boolean với lớp kế thừa.

phương pháp
4

Chia phức tạp vào các lớp học riêng biệt và tạo tính dụ cho các thông số thích hợp (trong ví dụ của bạn: clientType, useTaxes, doYearlySummarydoNotReloadClient).

Tôi rất có thể khuyên bạn nên Robert C. Martins "Clean Code: A Handbook of Agile Software Craftsmanship" cung cấp một giới thiệu rất hay về việc tạo mã sạch.

1

Đôi khi thật đơn giản để di chuyển các danh sách tham số đó vào một lớp, nếu nó có ý nghĩa. Ví dụ: bạn có thể có InterestRateDefinition có thể được sử dụng ở nhiều nơi khác nhau.

Tuy nhiên, nó thường khuyến khích việc tạo các lớp đơn giản tồn tại để phục vụ như danh sách tham số và lớp là vô nghĩa. Tôi đã làm điều này một chút công bằng bản thân mình trong quá khứ và họ có xu hướng lộn xộn mã thay vì cung cấp sự rõ ràng. Những ngày này, nếu danh sách đối số không cho chính nó vào một lớp, thì tôi sẽ biến chính hàm đó thành một lớp.

Vì vậy, bạn có thể có một lớp InterestRateCalculator thay vào đó, có một số yếu tố đầu vào hoặc thuộc tính công khai.Này giải quyết vấn đề và duy trì một cảm giác "Mục đích mã" ngoài ra bạn có thể cung cấp giá trị mặc định và cách thay thế để gọi thói quen của bạn thông qua các lớp học (ví dụ CalculateNextCoupon, CalculateAnnual vv)

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