2015-01-22 20 views
5

Tôi không có chuyên gia, nhưng tôi đang cố gắng hiểu nếu có một lỗ hổng trong logic của tôi ở đây. Tôi chắc rằng nó sẽ không mất nhiều thời gian để có nó chỉ ra nếu như vậy!Có cách nào để sử dụng cấu trúc làm đầu vào cho phương thức không?

Thay vì xác định một số tham số trên phương pháp chung, tại sao không chỉ đơn giản có cấu trúc làm tham số đầu vào đơn? Nếu mỗi tham số được xác định riêng và một tham số khác được thêm vào hoặc một tham số được loại bỏ, điều này yêu cầu bảo trì trên mọi đoạn mã gọi phương thức đó. Nếu một cấu trúc đã được sử dụng, thì không ai trong số các mã gọi sẽ phá vỡ, chắc chắn? Nếu một số mã trong tương lai yêu cầu thông số bổ sung nhưng mã kế thừa không, tôi có thể thêm thông số đó vào danh sách hiện tại và mã cũ vẫn không bị lu mờ. Ví dụ: Tôi đang xem một công ty có nhiều trang web/dự án (để làm rõ từ bài đăng gốc của tôi, nói 'sản phẩm' và có thể được hiểu sai là sản phẩm thực) cần giao tiếp với cổng thanh toán . Tùy thuộc vào vị trí của khách hàng, có thể gọi tới năm cổng thanh toán khác nhau để xử lý thanh toán của khách hàng.

Hiện tại, mỗi một trong số nhiều sản phẩm này đều có triển khai xử lý thanh toán của riêng mình, hoàn chỉnh với việc kiểm tra các tham số được trả lại từ cổng. Tôi thấy điều này gây nhầm lẫn và rõ ràng là sai và mặc dù không có thời gian hoặc nguồn lực để viết lại mọi thứ từ đầu, tôi nghĩ rằng việc sửa chữa tương đối nhanh sẽ tạo ra một dự án lớp "thanh toán" mới đã được xác định thiết lập các thông số.

Theo như tôi quan tâm, giao diện người dùng chỉ nên thực hiện điều gì đó đơn giản như gọi phương thức "thanh toán x bằng tiền y trong quốc gia z" và mong đợi cờ thành công/lỗi & thư tùy chọn làm rõ bất kỳ thất bại nào. Bằng cách này, mọi sản phẩm mà công ty đều có cùng xử lý thanh toán.

Bây giờ tham số khôn ngoan, hiện tại yêu cầu đầu vào chỉ là số tiền, tiền tệ & quốc gia (không, bạn không thể suy ra tiền tệ dựa trên vị trí của khách hàng trước khi ai đó gợi ý!). Và cho một sự trở lại nó sẽ chỉ là một thành công Boolean/thất bại và một thông điệp giải thích lý do thất bại. Tuy nhiên nó có thể là thông số bổ sung này sẽ được yêu cầu - cả trong và ngoài - trong tương lai và không muốn phá vỡ các cuộc gọi hiện có để phương pháp này, nó sẽ xuất hiện hợp lý để chỉ cần sử dụng một cấu trúc đơn giản, ví dụ:

struct inParams 
{ 
    public decimal amount; 
    public string currency; 
    public string country; 
}; 

struct outParams 
{ 
    public bool success; 
    public string message; 
}; 

    public outParams makePayment(inParams ip) 
    { 
     //...code goes here 
    } 

Đây có phải là "Thing Bad" để làm mã khôn ngoan hoặc là nó chấp nhận được thực hành? Nếu bất cứ ai nghĩ rằng tốt hơn là có các tham số được định nghĩa trong chữ ký phương thức riêng lẻ, bạn có thể giải thích tại sao điều đó sẽ tốt hơn đề xuất của tôi để sử dụng cấu trúc đơn giản không?


Để đối phó với "oɔɯǝɹ"

Tôi không thực sự thấy rằng những vấn đề mà bạn đã nêu ra là có nghiêm trọng mặc dù.

Ví dụ một cuộc gọi mẫu sẽ là một cái gì đó như:

 PaymentProcessor pp = new PaymentProcessor(); 
     ppInParams ppIn = new ppInParams(); 
     ppIn.amount = 100; 
     ppIn.currency = "USD"; 
     ppIn.country = "USA"; 

     ppOutParams ppOut = pp.makePayment(ppIn); 
     if (!ppOut.success) 
     { 
      //check error message, handle error, display message 
     } 
     else 
     { 
      //display confirmation message to user, update account log 
     } 

điểm của bạn: This leads to not very discoverable code là khó để biện minh khi đối mặt với sự đơn giản như trình bày ở trên, chắc chắn?

Users need to add extra boiler plate code just to instantiate your parameter struct. - vâng, vâng, nhưng không phải là một cơn đau đầu lớn để thêm một dòng mã cho mỗi thông số, phải không?

You will need to manage heaps of parameter structs. Two for every method. -How are you even going to name them? -In case you're thinming, I know, I'll just reuse them, then you've made your problem worse. Whats gonna happen when a single method using a struct need another parameter?

Tôi cũng không thấy vấn đề ở đây. Tôi có một lớp "paymentProcessor" duy nhất, trong đó tôi có "ppInParams" và "ppOutParams" và chúng được chứa trong chính lớp - với tôi, một nơi hoàn toàn hợp lý để đặt chúng khi chúng liên quan trực tiếp đến lớp đó.

Điểm cuối cùng của bạn: And in particular, your return type struct smells funny to me. Do you expect that every caller will inspect the boolean result? And what should i do with the message? Log it? show it? Have you not heard of structured exception handling? dường như kỳ quái với tôi và tôi không hiểu điểm bạn đang cố gắng thực hiện. Một triệu phần trăm CÓ Tôi mong đợi mọi người gọi để kiểm tra phản ứng boolean vì đó là những gì nói với họ nếu thanh toán là một thành công hay không! Đây là toàn bộ điểm đơn giản hóa cuộc gọi như thế này chắc chắn? Khi xử lý lỗi khác nhau tùy thuộc vào trang web nào đã gọi phương thức "thanh toán", thì một lần nữa, nó có ý nghĩa hoàn hảo với tôi để xử lý lỗi trong mã gọi.

+0

Điều gì về khách hàng đang sử dụng nhiều nguồn thanh toán cho một đơn hàng/sản phẩm? Đây là vấn đề thường gặp khi thanh toán đến từ nhiều tài khoản. – StarPilot

+0

@StarPilot - điều đó không xảy ra trong trường hợp này. Những khách hàng này sẽ thanh toán một lần cho số dư tài khoản/đăng ký của họ. – TheMook

+0

@ TheMook Bạn có thể không đồng ý với tôi (và bạn không phải làm thế!), Tuy nhiên, tôi có cảm giác rằng không có câu trả lời hợp lệ nào sẽ khiến bạn thay đổi ý định ở đây. Tại sao bạn thậm chí còn hỏi câu hỏi này? –

Trả lời

3

Sản phẩm không được bao gồm xử lý thanh toán. Việc xử lý thanh toán không mô tả thuộc tính hoặc hành động của Product nó mô tả hành động trênProduct. Bạn có thể cần một đối tượng Payment có thể được lỏng lẻo dựa trên inParams struct của bạn:

class Payment { 
    public decimal Amount { get; set; } 
    public string Currency { get; set; } 
} 

Các Country tài sản là thú vị. Nếu biết vị trí đặt hàng là quan trọng thì Đơn đặt hàng đến từ một địa điểm duy nhất để Country phải thuộc về Order. Tuy nhiên, nếu biết mỗi quốc gia thanh toán đến từ đâu là quan trọng thì Country phải thuộc về Payment. Điều đó tùy thuộc vào bạn. Tôi đã bao gồm Country trong Order.

Bạn cũng sẽ cần một đối tượng Order để nhóm PaymentProducts. Chú ý đối tượng Order chấp nhận một giao diện IPaymentProcessor làm đối số hàm tạo. Bạn có thể thực hiện các hành vi xử lý thanh toán khác nhau bằng cách thực hiện một giao diện khác nhau của giao diện IPaymentProcessor vào đối tượng Order. Bạn có thể áp dụng logic nghiệp vụ của mình để tìm ra triển khai cụ thể nào sẽ được gửi đến hàm tạo Order. Phần quan trọng của việc này là bạn sẽ không bao giờ phải thay đổi các lớp học Order hoặc Payment của mình để thay đổi hành vi xử lý thanh toán.

class Order { 
    public string Country { get; set; } 
    public ICollection<Payment> Payments { get; set; } 
    public ICollection<Product> Products { get; set; } 
    private IPaymentProcessor paymentProcessor; 

    public Order(IPaymentProcessor paymentProcessor) { 
     this.paymentProcessor = paymentProcessor; 
    } 

    void MakePayment(Payment payment) { 
     this.paymentProcessor.ProcessPayment(payment); 
    } 
} 

Nếu bạn không bao giờ chấp nhận nhiều khoản thanh toán trên đơn hàng, bạn có thể thay đổi ICollection<Payment> Payments thành Payment Payment.

IPaymentProcessor giao diện:

interface IPaymentProcessor { 
    void ProcessPayment(Payment payment); 
} 

Ghi struct là kiểu giá trị và sẽ được sao chép khi chúng được thông qua như là một tham số đến một phương pháp. Các lớp là các kiểu tham chiếu và chỉ tham chiếu (giá trị 32 bit hoặc 64 bit dựa trên kích thước từ của máy tính) được truyền theo giá trị cho một phương thức. Có vẻ như một lớp học tốt hơn trong tình huống, đặc biệt là nếu bạn dự đoán đối tượng sẽ phát triển.

CẬP NHẬT SAU KHI BÌNH LUẬN:

Điều đó chắc chắn sẽ thay đổi cách tôi đọc câu hỏi của bạn! Tôi nghĩ phần lớn câu trả lời vẫn được áp dụng nên tôi sẽ rời khỏi nó. Sử dụng logic ở trên, tôi sẽ thực hiện một dịch vụ OrderProcessor chấp nhận các đối tượng Order và gọi phương thức ProcessPayment của chúng tôi (và bất kỳ logic nào khác là bắt buộc).Bạn vẫn tiêm giao diện IPaymentProcessor dựa trên các quy tắc của hệ thống (nơi đơn đặt hàng bắt nguồn). Ngoài ra, nếu các phần của quá trình đặt hàng tương tự nhau, hãy xem xét việc tạo IPaymentProcessor thành lớp cơ sở PaymentProcessor nơi bạn có thể ghi đè các hành vi cụ thể trong lớp cơ sở khi cần. Có rất nhiều cách để thực hiện điều này nhưng ý tưởng chính mà tôi đang cố gắng truyền đạt là đây là một trường hợp tuyệt vời cho đa hình và đây chỉ là một ví dụ về cách bạn có thể xây dựng một kiến ​​trúc cơ bản bằng nguyên tắc đó.

+0

Cảm ơn bạn đã phản hồi - đọc qua điều này ngay bây giờ. Để làm rõ, tôi có nghĩa là "sản phẩm" như một thuật ngữ chung cho các dự án web, xin lỗi! Công ty có nhiều dự án/trang web khác nhau là sản phẩm của họ và mỗi công ty đều cần quyền truy cập vào hệ thống thanh toán để cho phép mọi người thanh toán cho các sản phẩm và dịch vụ PHYSICAL! – TheMook

+0

Tôi thực sự đánh giá cao những suy nghĩ và nỗ lực đã đi vào phản ứng này, nhưng (do thiếu sự rõ ràng trong bài viết gốc của tôi) Tôi không nghĩ rằng nó thực sự giải quyết vấn đề tôi đang xem xét. Đã có các giao diện (và các lớp trừu tượng) được sử dụng trong các hệ thống khác nhau, nhưng tôi đang cố gắng xem một thư viện lớp "thanh toán" hợp nhất đơn giản có thể dễ dàng đưa vào tất cả các dự án. phải làm là lấy một vài tham số cơ bản và nhổ ra một boolean "thành công/thất bại" và thông báo nếu nó thất bại. mã gọi điện có thể xử lý bất kỳ vấn đề nào được đưa ra bởi quá trình thanh toán. – TheMook

3

Không thể có quy tắc nghiêm ngặt về việc có bao gồm nhiều thông số đầu vào vào một số lớp/cấu trúc hoặc chuyển riêng từng thông số hay cấu trúc đó hay không. Tất cả mọi thứ phụ thuộc vào tình hình cụ thể của bạn.

Hãy xem xét ví dụ sau (khá ngớ ngẩn, chỉ để hiển thị một ý tưởng):

public void makePayment(decimal amount, string currency, ILog logger) 
{ 
    //...code goes here 
} 

Nhìn trên các thông số đầu vào, rõ ràng là amountcurrency có thể có mối quan hệ ngữ nghĩa giữa mỗi khác. Vì vậy, nó là hợp lý để quấn chúng vào lớp riêng biệt Payment. Nhưng ILog không liên quan đến Payment không có nghĩa là. Nó chỉ là một logger.

Bạn có thể cấu trúc lại phương pháp của bạn là như thế này:

public void makePayment(Payment payment, ILog logger) 
{ 
    //...code goes here 
} 

Ý tưởng chính mà bạn không nên nhân tạo kết hợp các thông số đầu vào. Bạn nên kết hợp chúng được hướng dẫn bởi ngữ nghĩa của chúng. Sau đó, có nhiều cơ hội mà bạn sẽ có thể tái sử dụng các lớp học của bạn ở những nơi khác. Ngoài ra, đừng quên về khả năng đọc - dễ hiểu hơn về ý nghĩa của phương pháp của bạn nếu bạn có sự tách biệt rõ ràng về các phụ thuộc ngữ nghĩa khác nhau.

Đối với các tham số đầu ra, không có nhiều lựa chọn - bạn chỉ có thể trả lại một tham số (không xem xét các đối số ref/out ở đây). Vì vậy, chắc chắn, nó là OK để bọc nhiều tham số vào một lớp riêng biệt, nhưng điều quan trọng là đặt tên nó theo ý nghĩa của nó: trong ví dụ của bạn nó có thể được đặt tên, nói, PaymentResult.

+0

Tôi đồng ý với phần lớn điều này, cảm ơn. Tuy nhiên bạn dường như tạo ra một tình huống mà tôi chưa đề xuất - tôi không quan tâm đến việc thêm khả năng ghi vào lớp thanh toán, nó sẽ CHỈ được sử dụng để thanh toán, không bao giờ có thứ gì khác để các tham số luôn có liên quan (logic và ngữ nghĩa)). Tôi không có ý định vi phạm SRP, chỉ cần đặt tất cả mã thanh toán ở một nơi với các tham số vào/ra đơn giản. – TheMook

0

Thay đổi đột phá sẽ vỡ;

Hãy xem xét điều gì xảy ra khi bạn cần cập nhật phương thức thanh toán để thêm một thông số bắt buộc khác. Ví dụ: vì tỷ giá hối đoái có thể thay đổi và bạn đang thực hiện thanh toán quốc tế, bạn phát hiện ra rằng bạn cần phải thêm thông số ngày để xác định tỷ giá hối đoái chính xác.

Tỷ giá hối đoái khi mở cửa thị trường được lấy làm giá trị mong muốn. Đây là một yêu cầu bắt buộc, để lại điều đó sẽ giới thiệu rất nhiều rắc rối và chảy máu. Vì vậy, cấu trúc của bạn trở thành:

struct inParams 
{ 
    public decimal amount; 
    public string currency; 
    public string country; 
    public DateTime paymentDate; 
}; 

'Tuyệt vời' bạn nghĩ, vì bạn không phá vỡ thay đổi giao diện. Nhưng vì đây là giá trị bắt buộc, bỏ qua giá trị này, sẽ ném một số ArgumentNullException.

Chỉ có vấn đề là, tất cả người dùng giao diện của bạn vẫn cần phải cập nhật mã của họ!

+0

Điều này chỉ đúng nếu tham số bắt buộc được thêm vào. Nếu không, giá trị null sẽ được xử lý trong phương thức thanh toán cho mã gọi không bị ảnh hưởng bởi bất kỳ tham số bổ sung không bắt buộc nào. Ngược lại, nếu tôi thêm các tham số tùy chọn vào chữ ký của phương thức trong tương lai thì tôi phải khai báo chúng tùy chọn và thêm mã để xử lý chúng, vì vậy tôi không thể sử dụng tốt hơn. Cách tôi đang xem xét, tôi có một cấu trúc "ppInParams" đơn giản giữ chữ ký phương thức sạch sẽ và không dẫn đến các cuộc gọi phương thức như 'makePayment (100.00," USD "," USA ", 2.3," Donkey "," Barack Obama ", 3,44" ' – TheMook

0

Bên cạnh số other answer, tôi nghĩ đây là thực tiễn không tốt.

  • Điều này dẫn đến mã không dễ phát hiện.Biên tập viên, IDE và IntilliSense được điều chỉnh để thông thường đi qua.
  • Người dùng cần thêm mã tấm phụ để chỉ tạo cấu trúc tham số của bạn.
  • Bạn sẽ cần phải quản lý đống cấu trúc thông số. Hai cho mọi phương pháp.
    • Bạn thậm chí sẽ đặt tên cho chúng bằng cách nào?
    • Trong trường hợp bạn đang làm mỏng, I know, I'll just reuse them, thì bạn đã làm cho vấn đề của mình tồi tệ hơn. Whats sẽ xảy ra khi một phương pháp duy nhất bằng cách sử dụng một struct cần một tham số?
  • Và đặc biệt, cấu trúc kiểu trả về của bạn có mùi buồn cười với tôi. Bạn có nghĩ rằng mọi người gọi sẽ kiểm tra kết quả boolean? Và tôi nên làm gì với tin nhắn? Đăng nhập? hiển thị nó? Bạn đã không nghe nói về xử lý ngoại lệ có cấu trúc chưa?

TL, DR; Bạn có thể nhận được một chút lợi thế tại địa phương (mà tôi không nhìn thấy), nhưng nó đòi hỏi một chức năng gọi thêm rất nhiều công việc như thế này. Vì có nhiều người gọi phương thức hơn là phương pháp, bạn nên làm cho nó dễ dàng hơn để gọi phương thức của bạn.

+0

Tôi đã thêm một phần trong bài viết gốc để giải quyết các điểm của bạn TL: DR: Tôi không đồng ý với tất cả các bạn nói và tôi thậm chí không nghĩ rằng một số điểm bạn thực hiện là hợp lệ. Tôi có thể, tất nhiên, là sai. – TheMook

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