Thiết kế các phương thức liên quan đến cơ sở dữ liệu, cái nào tốt hơn để trả về: true / false hoặc hàng bị ảnh hưởng?


10

Tôi có một số phương thức thực hiện một số thay đổi dữ liệu trong cơ sở dữ liệu (chèn, cập nhật và xóa). Các ORM Tôi đang sử dụng giá trị int trở lại hàng bị ảnh hưởng đối với những loại phương pháp. Tôi nên trả lại cái gì cho "phương pháp của tôi", để chỉ ra trạng thái thành công / thất bại của hoạt động?

Hãy xem xét mã đang trả về một int:

A.1

public int myLowerLevelMethod(int id) {
    ...
    int affectedRows = myOrm.deleteById(id)
    ...

    return affectedRows;
}

Sau đó sử dụng:

A.2

public void myOtherMethod() {
    ...
    int affectedRows = myLowerLevelMethod(id)

    if(affectedRows > 0) {
        // Success
    } else {
        // Fail
    }
}

So sánh với việc sử dụng boolean:

B.1

public boolean myLowerLevelMethod(int id) {
    ...
    int affectedRows = myOrm.deleteById(id)
    ...

    return affectedRows > 0;
}

Sau đó sử dụng:

B.2

public void myOtherMethod() {
    ...
    boolean isSuccess = myLowerLevelMethod(id)

    if(isSuccess) {
        // Success
    } else {
        // Fail
    }
}

Cái nào (A hoặc B) là tốt hơn? Hoặc ưu / nhược điểm của từng?


Trong "A.2" của bạn. Nếu hàng 0 bị ảnh hưởng, tại sao đó là thất bại nếu hàng 0 cần phải bị ảnh hưởng? Nói cách khác, nếu không có lỗi cơ sở dữ liệu, tại sao nó lại thất bại?
Jaydee

5
Có sự khác biệt về ngữ nghĩa giữa "không thành công" và "hàng không bị ảnh hưởng" không? Ví dụ: khi xóa tất cả các đơn đặt hàng của khách hàng, có một sự khác biệt giữa "khách hàng không tồn tại" và "khách hàng không có đơn hàng".
Cephalepad

Bạn có xem xét xóa với hàng không bất ngờ? Trong trường hợp đó ném.
usr

@Arian Tôi nghĩ đó là câu hỏi thực sự cho tôi. Tôi nghĩ rằng tôi đã chọn B, vì với A, mã của tôi hiện chứa kiểm tra 0 ở một số nơi và cho -1 ở những nơi khác
Hoàng Trần

Câu trả lời:


18

Một tùy chọn khác là trả về một đối tượng kết quả thay vì các loại cơ bản. Ví dụ:

OperationResult deleteResult = myOrm.deleteById(id);

if (deleteResult.isSuccess()) {
    // ....
}

Với điều này, nếu vì lý do nào đó bạn cần trả về số lượng hàng bị ảnh hưởng, bạn chỉ cần thêm một phương thức trong OperationResult:

if (deleteResult.isSuccess()) {
    System.out.println("rows deleted: " + deleteResult.rowsAffected() );
}

Thiết kế này cho phép hệ thống của bạn phát triển và bao gồm chức năng mới (biết về các hàng bị ảnh hưởng) mà không cần sửa đổi mã hiện có.


2
+1 "Thiết kế này cho phép hệ thống của bạn phát triển và bao gồm chức năng mới (biết về các hàng bị ảnh hưởng) mà không cần sửa đổi mã hiện có" Đây là cách suy nghĩ đúng về loại câu hỏi này.
Được thông báo vào

Tôi đã làm điều tương tự trong dự án cũ của tôi. Tôi có đối tượng yêu cầu và kết quả bao bọc. Cả hai đều sử dụng Thành phần để bao gồm nhiều chi tiết hơn. và cả hai đều có dữ liệu cơ bản trong đó. Trong trường hợp này, đối tượng Kết quả có mã trạng thái và trường thông báo.
Được thông báo vào

Đặc biệt đối với một hệ thống sử dụng ORM, tôi sẽ gọi đây là cách thực hành tốt nhất. ORM trong câu hỏi cũng có thể bao gồm (các) loại đối tượng kết quả như vậy!
Brian S

Chỉ cần nhìn vào ví dụ OP, tất cả những gì tôi có thể nghĩ là xóaById sẽ trả về 2 vào một lúc nào đó :) vì vậy chắc chắn là một loại tùy chỉnh, vâng.
dẫn

Tôi thích cách tiếp cận này. Tôi nghĩ rằng nó không chặn, bao gồm cả hai cách tiếp cận của tôi và có thể sửa đổi / mở rộng (trong tương lai, nếu cần). Nhược điểm duy nhất tôi có thể nghĩ đến đó là mã nhiều hơn một chút, đặc biệt là khi nó ở giữa dự án của tôi. Tôi sẽ đánh dấu đây là câu trả lời. Cảm ơn bạn.
Hoàng Trần

12

Trả lại số lượng hàng bị ảnh hưởng là tốt hơn bởi vì nó cung cấp thêm thông tin về cách tiến hành hoạt động.

Không có lập trình viên sẽ đổ lỗi cho bạn bởi vì anh ấy / cô ấy phải viết điều này để kiểm tra xem họ có một số thay đổi trong quá trình hoạt động không:

if(affectedRows > 0) {
    // success
} else {
    // fail
}

nhưng họ sẽ đổ lỗi cho bạn khi họ sẽ phải biết số lượng hàng bị ảnh hưởng và họ nhận ra rằng không có phương pháp nào để có được số đó.

BTW: nếu "thất bại", bạn có nghĩa là lỗi truy vấn cú pháp (trong trường hợp đó, số lượng hàng bị ảnh hưởng rõ ràng là 0) thì việc ném ngoại lệ sẽ phù hợp hơn.


4
-1 vì lý do bạn đưa ra. Cung cấp thêm thông tin không phải lúc nào cũng là một ý tưởng tốt. Thông thường, thiết kế tốt hơn sẽ dẫn đến việc giao tiếp với người gọi đúng những gì nó cần, và không có gì hơn thế.
Arseni Mourzenko

1
@MainMa không có gì cản trở việc tạo ra các phương thức quá tải. Ngoài ra, trong ngôn ngữ bản địa (ví dụ họ C), số lượng hàng có thể được sử dụng trực tiếp trong logic (0 là sai, bất cứ điều gì khác là đúng).
PTwr

@PTwr: để đối phó với thực tế là phương thức đang trả lại quá nhiều thông tin, bạn có đề xuất tạo quá tải không? Điều này có vẻ không đúng. Đối với "không là sai, khác không là đúng", đây không phải là điểm nhận xét của tôi và là một thực tiễn xấu trong một số ngôn ngữ.
Arseni Mourzenko

1
Có lẽ tôi là một trong những lập trình viên hư hỏng vì tôi không nghĩ rằng sử dụng bất kỳ thủ thuật nào có thể được coi là thực hành tốt. Trong thực tế, bất cứ khi nào tôi phải đối phó với mã của người khác, tôi rất biết ơn bất cứ ai đã viết nó và không sử dụng bất kỳ thủ thuật nào.
proskor

4
Bạn nên luôn luôn trả lại số lượng hàng bị ảnh hưởng ở đây !!! Vì bạn không thể biết liệu số hàng = 0 là thất bại hay nếu số hàng = 3 là thành công? Nếu ai đó muốn chèn 3 hàng và chỉ có 2 hàng được chèn, bạn sẽ trả về đúng, nhưng điều đó không đúng! Và nếu ai đó muốn update t set category = 'default' where category IS NULLthậm chí 0 hàng bị ảnh hưởng sẽ là một thành công, bởi vì bây giờ không có mục nào không có danh mục, ngay cả khi không có hàng nào bị ảnh hưởng!
Falco

10

Tôi sẽ không đề nghị bất kỳ trong số họ. Thay vào đó, trả lại không có gì (khoảng trống) cho thành công và ném ngoại lệ vào thất bại.

Đây là lý do chính xác giống như tôi chọn để khai báo một số bộ nhớ riêng của lớp. Nó cũng làm cho chức năng dễ sử dụng hơn. Nhiều bối cảnh hoạt động không phải lúc nào cũng có nghĩa là tốt hơn, nhưng nó chắc chắn có nghĩa là phức tạp hơn. Bạn càng ít hứa hẹn, bạn càng trừu tượng hóa, khách hàng càng dễ hiểu và bạn càng có nhiều tự do hơn trong việc lựa chọn cách thực hiện nó.

Câu hỏi là làm thế nào để chỉ ra thành công / lỗi. Trong trường hợp này, nó đủ để báo hiệu sự thất bại bằng cách ném một ngoại lệ và không trả lại thành công. Tại sao tôi phải cung cấp nhiều hơn nhu cầu của người dùng?

Thất bại / tình huống đặc biệt có thể xảy ra và sau đó bạn sẽ phải đối phó với chúng. Cho dù bạn sử dụng thử / bắt để làm điều đó hay kiểm tra mã trả về, đó là vấn đề về phong cách / sở thích cá nhân. Các ý tưởng đằng sau thử / bắt là: tách luồng bình thường khỏi luồng đặc biệt và để ngoại lệ nổi lên trên lớp nơi chúng có thể được xử lý một cách thích hợp nhất. Vì vậy, như nhiều người đã chỉ ra, nó phụ thuộc vào việc thất bại có thực sự đặc biệt hay không.


4
-1 Tại sao bạn không trả lại bất cứ thứ gì mà bạn có thể trả lại một cái gì đó, không có tác dụng phụ tiêu cực, cung cấp bối cảnh hoạt động bổ sung?
FreeAsInBeer

7
Vì lý do chính xác tương tự, tôi chọn khai báo một số bộ nhớ riêng của lớp. Nó cũng làm cho chức năng dễ sử dụng hơn. Nhiều bối cảnh hoạt động không phải lúc nào cũng có nghĩa là tốt hơn, nhưng nó chắc chắn có nghĩa là phức tạp hơn. Bạn càng ít hứa hẹn, bạn càng trừu tượng hóa, khách hàng càng dễ hiểu và bạn càng có nhiều tự do hơn trong việc lựa chọn cách thực hiện nó.
proskor

1
Người dùng thực sự cần lấy dữ liệu gì? Câu hỏi là làm thế nào để chỉ ra thành công / lỗi. Trong trường hợp này, nó đủ để báo hiệu sự thất bại bằng cách ném một ngoại lệ và không trả lại thành công. Tại sao tôi phải cung cấp nhiều hơn nhu cầu của người dùng?
proskor

4
@proskor: Trường hợp ngoại lệ dành cho các trường hợp ngoại lệ. "Thất bại" trong kịch bản này có thể là kết quả mong đợi. Bằng mọi cách, hãy đưa điều này về phía trước như một giải pháp thay thế tiềm năng, nhưng không có đủ thông tin ở đây để đưa ra khuyến nghị.
Nick Barnes

1
-1 Ngoại lệ không phải là một phần của luồng chương trình thông thường. Không rõ "thất bại" nghĩa là gì trong bối cảnh lỗi của bạn, nhưng một ngoại lệ trong ngữ cảnh của một cuộc gọi cơ sở dữ liệu phải là do một ngoại lệ xảy ra trong cơ sở dữ liệu. Ảnh hưởng đến hàng 0 không phải là một ngoại lệ. Một truy vấn sai lệch không thể được phân tích cú pháp, tham chiếu một bảng không tồn tại, v.v. sẽ là một ngoại lệ vì công cụ cơ sở dữ liệu sẽ bóp nghẹt nó và ném.

2

"Điều này có tốt hơn thế không?" không phải là một câu hỏi hữu ích khi hai lựa chọn thay thế không làm điều tương tự.

Nếu bạn cần biết số hàng bị ảnh hưởng, thì bạn phải sử dụng phiên bản A. Nếu bạn không phải, thì bạn có thể sử dụng phiên bản B - nhưng bất kỳ lợi thế nào bạn có thể đạt được về nỗ lực viết mã ít hơn đã hết bạn đã gặp khó khăn để đăng cả hai phiên bản lên một diễn đàn trực tuyến!

Quan điểm của tôi là: giải pháp nào tốt hơn phụ thuộc hoàn toàn vào yêu cầu của bạn cụ thể đối với ứng dụng này là gì và bạn biết những trường hợp đó tốt hơn nhiều so với chúng tôi. Không có sự lựa chọn nào trong toàn ngành, an toàn để sử dụng, thực hành tốt nhất, sẽ không bị sa thải vì nó là lựa chọn tốt hơn nói chung ; bạn phải tự suy nghĩ về nó Và đối với một quyết định dễ dàng sửa đổi như quyết định này, bạn cũng không cần phải dành nhiều thời gian để suy nghĩ.


Tôi đồng ý rằng điều này phụ thuộc nhiều vào yêu cầu ứng dụng. Tuy nhiên, có vẻ như loại tình huống này không phải là rất độc đáo và tôi không tìm kiếm viên đạn bạc mà chỉ là trải nghiệm của người khác đối phó với điều tương tự / tương tự (có thể câu hỏi hơi sai lệch, tôi thích các đề xuất khác ngoài A / B)
Hoàng Trần

1

Hai trong số các nguyên tắc quan trọng nhất trong thiết kế phần mềm có thể bảo trì là KISSYAGNI .

  • KISS : Giữ nó đơn giản, ngu ngốc
  • YAGNI : Bạn không cần nó

Nó gần như là không bao giờ là một ý tưởng tốt để đặt trong logic bạn không ngay lập tức cần ngay bây giờ . Trong số nhiều người khác, Jeff Atwood (một người đồng sáng lập StackExchange) đã viết về điều này , và theo kinh nghiệm của tôi, anh ấy và những người đề xuất các khái niệm này là hoàn toàn đúng.

Bất kỳ sự phức tạp nào bạn thêm vào một chương trình đều phải trả giá, được trả trong một khoảng thời gian dài. Chương trình trở nên khó đọc hơn, phức tạp hơn để thay đổi và dễ dàng hơn cho các lỗi xuất hiện. Đừng rơi vào cái bẫy thêm những thứ "chỉ trong trường hợp". Đó là một cảm giác an toàn sai lầm.

Bạn hiếm khi nhận được bất kỳ mã ngay lần đầu tiên. Thay đổi là không thể tránh khỏi; thêm logic đầu cơ để chuẩn bị phòng thủ cho các tình huống không xác định trong tương lai sẽ không thực sự bảo vệ bạn khỏi phải cấu trúc lại mã của bạn khi tương lai hóa ra khác với bạn mong đợi. Bảo trì logic không cần thiết / dự phòng là vấn đề bảo trì nhiều hơn là tái cấu trúc sau này để thêm chức năng bị thiếu.

Do đó, vì dường như tất cả các chương trình của bạn cần bây giờ là để biết liệu hoạt động thành công hay thất bại, giải pháp B được đề xuất của bạn (trả về một boolean duy nhất) là cách tiếp cận đúng. Bạn luôn có thể cấu trúc lại nó sau nếu yêu cầu thay đổi. Giải pháp này là đơn giản nhất và có độ phức tạp thấp nhất (KISS) và thực hiện đúng những gì bạn cần và không có gì hơn (YAGNI).


-1

Toàn bộ hàng hoặc trạng thái lỗi

Xem xét trả lại toàn bộ hàng, ít nhất là tùy chọn thời gian chạy. Trong phần chèn DB, bạn có thể cần kiểm tra dữ liệu được chèn - vì nó thường sẽ khác với những gì bạn đã gửi cho DB; ví dụ phổ biến bao gồm ID hàng được tạo tự động (ứng dụng có thể sẽ cần ngay lập tức), giá trị mặc định được xác định bởi DB và kết quả của trình kích hoạt nếu bạn sử dụng chúng.

Mặt khác, nếu bạn không cần dữ liệu trả về, sau đó bạn cũng không cần đếm hàng bị ảnh hưởng, vì nó không phải là hữu ích cho các kết quả của 0. Nếu có sai lầm, sau đó bạn cần phải trả lại những gì loại của đã xảy ra lỗi, theo cách phù hợp với các nguyên tắc xử lý lỗi dự án của bạn (ngoại lệ, mã lỗi số, bất cứ điều gì); nhưng có những truy vấn hợp lệ sẽ ảnh hưởng chính xác đến 0 hàng (nghĩa là "xóa tất cả các đơn hàng đã hết hạn" nếu thực sự không có bất kỳ).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.