Tại sao DRY quan trọng?


81

Khá đơn giản, tại sao tôi muốn viết mã hoạt động cho tất cả các trường hợp và dữ liệu có thể mở rộng khi tất cả những gì tôi cần làm là lặp lại quá trình tương tự một vài lần với một vài điều chỉnh nhỏ?

Tôi không cần phải chỉnh sửa lại bất cứ lúc nào sớm.

Có vẻ như rất ít công việc chỉ để đi ...

function doStuff1(){/*.a.*/}
function doStuff2(){/*.b.*/}
function doStuff3(){/*.c.*/}

Và nếu tôi cần thêm thứ gì đó ...

function doStuff4(){/*.d.*/}

Và nếu tôi cần loại bỏ nó, tôi loại bỏ nó.

Thật khó để tìm ra cách biến tất cả những thứ đó thành một mô hình đơn giản mà tôi có thể cung cấp dữ liệu và xử lý tất cả các trường hợp, và thực hiện một loạt các thay đổi mà tôi không cảm thấy như mình sẽ có làm.

Tại sao phải KHÔ khi có vẻ như cắt nhanh + dán sẽ làm việc ít hơn nhiều?


11
bởi vì khô vẫn nhanh hơn khi bạn làm đúng, còn nếu bạn mắc lỗi trong a. điều đó ảnh hưởng đến tất cả những người khác
Daniel Little

97
"Tôi không cần phải chỉnh sửa lại bất cứ lúc nào sớm" - bạn có thể hy vọng, nhưng rất có thể bạn đang mắc lỗi ở đây. Và nếu bạn sẽ làm việc với mã đó một lần nữa, nhưng không lâu nữa, nó sẽ chỉ khiến mọi thứ tồi tệ hơn; bạn sẽ quên nơi trùng lặp, và sự trùng lặp sẽ phát triển những khác biệt tinh vi nhưng nguy hiểm. "Viết như thể người sẽ duy trì mã của bạn là một kẻ điên nguy hiểm biết bạn sống ở đâu", để trích dẫn kinh điển.
9000

14
Tôi nghĩ bạn có thể tổng hợp khá nhiều với nó: Một điểm thay đổi duy nhất dễ duy trì hơn.
Falcon

17
Nếu bạn không thể tự trả lời câu hỏi này, bạn cần có thêm một số kinh nghiệm thực tế về phát triển và bảo trì.
David Heffernan

15
@Wayne Tôi cảm thấy một sự xáo trộn lớn trong nguồn, như thể hàng triệu lập trình viên đột nhiên kêu lên khủng bố.
Ẩn danh

Câu trả lời:


121

Nếu bạn lặp lại chính mình, bạn có thể tạo ra các vấn đề bảo trì. Nếu doStuff1-3 đều có mã có cấu trúc tương tự và bạn khắc phục sự cố trong một, bạn có thể dễ dàng quên khắc phục sự cố ở những nơi khác. Ngoài ra, nếu bạn phải thêm một trường hợp mới để xử lý, bạn chỉ cần chuyển các tham số khác nhau vào một chức năng thay vì dán sao chép khắp nơi.

Tuy nhiên, DRY thường được đưa đến một thái cực bởi các lập trình viên thông minh. Đôi khi để không lặp lại chính mình, bạn phải tạo ra sự trừu tượng đến mức khó hiểu để đồng đội của bạn không thể theo dõi họ. Đôi khi cấu trúc của hai thứ chỉ tương tự mơ hồ nhưng đủ khác nhau. Nếu doStuff1-4 đủ khác nhau để việc tái cấu trúc chúng không lặp lại chính bạn khiến bạn phải viết mã không tự nhiên hoặc trải qua các mã hóa ngược thông minh sẽ khiến nhóm của bạn lườm bạn, thì có thể bạn có thể lặp lại. Tôi đã cúi xuống để không lặp lại một vài lần theo cách không tự nhiên và hối tiếc sản phẩm cuối cùng.

Tôi luôn nhầm lẫn về phía DRY, trong trường hợp hiếm hoi lặp lại chính mình khi tôi nghĩ rằng những lợi ích trong khả năng đọc là giá trị rủi ro của việc ai đó quên sửa lỗi ở nhiều nơi.

Nếu tính đến lời khuyên đó, có vẻ như trong trường hợp của bạn

lặp lại quá trình tương tự một vài lần với một vài điều chỉnh nhỏ

Tôi chắc chắn sẽ làm việc chăm chỉ để không lặp lại chính mình trong trường hợp của bạn. Giả sử "tinh chỉnh" tối thiểu - chúng có thể được xử lý với các tham số khác nhau tác động đến hành vi hoặc có thể phụ thuộc vào tiêm để thực hiện các nhiệm vụ khác nhau.

Tại sao phải KHÔ khi có vẻ như cắt nhanh + dán sẽ làm việc ít hơn nhiều?

Những lời cuối cùng nổi tiếng. Bạn sẽ hối hận khi nghĩ rằng khi một kỹ sư cơ sở điều chỉnh / sửa chữa / cấu trúc lại một doStuff và thậm chí không nhận ra những cái khác tồn tại. Nảy sinh vui nhộn. Không có chủ yếu là ợ nóng xảy ra. Mỗi dòng mã có giá cao hơn. Bạn phải kiểm tra bao nhiêu đường dẫn mã với rất nhiều hàm lặp lại? Nếu một chức năng, bạn chỉ cần kiểm tra một đường dẫn chính với một vài sửa đổi hành vi. Nếu sao chép, bạn phải kiểm tra riêng từng doStuff. Điều lạ là bạn sẽ bỏ lỡ một và khách hàng có thể có một lỗi không mong muốn và bạn có thể có một số email không mong muốn trong hộp thư đến của mình.


2
Er, chỉ cần rõ ràng, tôi không đề xuất khô là xấu, câu hỏi này là nhiều người ủng hộ quỷ. Tôi thực sự đang tìm kiếm một phản hồi hợp lý Tôi có thể liên kết với những người nghĩ rằng cắt + dán + chỉnh sửa mã là tốt.
Ẩn danh

2
Điều đó đang được nói, tôi thích câu trả lời của bạn nhất vì nó bao gồm cả những thất bại trong DRY: không làm phiền, và quá nhiệt tình, cũng như giải thích các tác động. - Về một điểm về việc hy sinh khả năng đọc cho các lỗi, tôi cho rằng mã lặp đi lặp lại ít đọc hơn vì cùng lý do bạn chỉ ra, nơi dễ bị mất dấu vết của mọi thứ.
Ẩn danh

16
Tôi sẽ nhấn mạnh một cạm bẫy dễ dàng của DRY: tương tự được hợp nhất. Nếu bạn có hai trường hợp sử dụng không liên quan đến chức năng nhưng tình cờ có mã rất giống nhau, thì rất dễ hợp nhất hai trường hợpDRY tốt . Thật không may, khi một người cần phát triển, sau đó bạn thường thấy mình có nhiệm vụ khó chịu là phải phân chia chức năng một lần nữa, sau đó đi qua tất cả các trang web cuộc gọi và xem xét cẩn thận cái nào sẽ được gọi ở đây ... Ví dụ: Gõ cấu trúc LLVM (tất cả các loại tương tự được hợp nhất thành một) làm cho không thể ánh xạ IR trở lại mã ban đầu.
Matthieu M.

1
Chơi lô tô. Nếu hai hoặc nhiều đoạn mã sẽ được thay đổi và các thay đổi sẽ luôn giống nhau cho tất cả các đoạn, thì chúng sẽ được hợp nhất. Bất kỳ phần nào sẽ cần phải thay đổi trong một thời trang khác với những người khác không nên được hợp nhất. Nếu mã sẽ không bao giờ thay đổi, thì nó không thành vấn đề dù nó có được hợp nhất hay không. Câu hỏi về việc thay đổi nên được khóa hoặc tách ra là quan trọng hơn nhiều so với kích thước của mã được đề cập.
supercat

1
@MatthieuM đó là một ví dụ không công bằng. LLVM đang áp dụng gõ cấu trúc như một tối ưu hóa ; nghĩa là, những người LLVM đã quyết định trả giá của IR khó hiểu cho các lợi ích hiệu năng. DRY thường là một vấn đề bảo trì nhưng trong trường hợp này rõ ràng đó là một quyết định có chủ ý để giảm khả năng bảo trì.
Benjamin Hodgson

47

Bởi vì DRY sẽ làm việc ít hơn sau này.


DRY: (Đừng lặp lại chính mình)

Một hàm lấy một đối số.

def log(arg):
    print(arg)

C & P: (Sao chép và dán)

26 hàm gazillion thực hiện cùng một thứ, nhưng với chênh lệch 2 char.

def logA():
    print('a')

def logB():
    print('b')

...ad infinitum...

Làm thế nào về chúng tôi cập nhật in ấn của chúng tôi để xác định chính xác những gì đang in?

KHÔ:

def log(arg):
    print(arg + "Printed from process foo")

Làm xong.

C & P:

Bạn phải quay lại và thay đổi mọi chức năng .


Mà bạn nghĩ sẽ dễ dàng hơn để gỡ lỗi?


10
Ngoài ra, bạn cần phải viết nhiều bộ kiểm tra tương tự như bạn có các chức năng trùng lặp.
9000

Bạn đã minh họa khái niệm này đầy đủ, nhưng trong thực tế, không ai sẽ làm những gì bạn mô tả với các hàm gazillion, không phải theo cách đó.
Robert Harvey

@Robert Tôi sẽ không hy vọng! Tôi đã chọn một nhiệm vụ rất đơn giản để cố gắng minh họa rõ hơn về khái niệm này và tại sao nó có thể là một điều tốt.
Giăng

11
@Robert - Bạn đã đọc những bài viết tại thedailywtf.com ;) - có một số ra có những người sẽ làm việc đó
HorusKol

1
@ 9000 Không phải nếu bạn không có bất kỳ bộ thử nghiệm nào: p (mà thực tế thường có thể là trường hợp trong một số dự án ... thật không may ...)
Svish

16

Bởi vì , áp dụng cho ví dụ của bạn:

  • + khả năng đọc

    Mã ít hơn thường dịch để ít tiếng ồn hơn . (không phải lúc nào cũng ...)

  • + linh hoạt

    Nếu bạn phải thay đổi hành vi của doStuffX, bạn sẽ muốn tự sát hoặc bất cứ ai đã viết nó,

  • + khả năng mở rộng

    Nếu bạn đã trích xuất các phần riêng biệt thành cấu trúc dữ liệu mà bạn chọn và sau đó lặp lại bằng cách gọi chung doStuff, bạn cũng có thể thêm một dòng trong cấu trúc dữ liệu của mình vào nơi bạn muốn có mục mới hoặc xóa một mục và thay đổi hành vi sẽ chỉ có nghĩa là chỉnh sửa doStuff. Dễ bảo trì hơn .

  • + hiệu quả chi phí

    ít mã hơn ở đây có nghĩa là:

    • => ít phát triển => giảm chi phí
    • => ít xác suất xảy ra lỗi => ít thời gian hỗ trợ hơn = giảm chi phí
  • + (có thể) được tối ưu hóa được quản lý

    Tùy thuộc vào ngôn ngữ, trình biên dịch / trình thông dịch có thể có cơ hội lớn hơn để xác định rằng chung chung doStuffluôn luôn là những thứ gần như giống hệt nhau thường gọi nhau và có thể nội tuyến hóa nó hoặc cố gắng tối ưu hóa nó. Có lẽ sẽ không cho X biến thể của doStuffX.

  • + kiểm tra và chất lượng

    Kiểm tra dễ dàng hơn: doStuffcần kiểm tra, và đó là nó. Vâng, không chính xác, nhưng điều đó đã bao gồm nhiều hơn . Chỉ có các kỳ vọng IO của nó khác nhau và cần được kiểm tra trong các điều kiện khác nhau, nhưng vẫn dễ kiểm tra hơndễ bảo trì hơn tất cả các biến thể của doStuffX.

Nhìn chung , tài khoản này có mã dễ bảo trì hơn và hiệu quả phát triển được cải thiện cho nhóm của bạn và đó là một trong nhiều thực tiễn tốt để giúp bạn sản xuất phần mềm mạnh mẽ và đáng tin cậy hơn.


13

Vì mọi người khác đã làm rất tốt trong việc giải thích các vấn đề về khả năng bảo trì với mã trùng lặp, tôi sẽ chỉ nói điều này:

Phần lớn lập trình đòi hỏi bạn phải suy nghĩ về tương lai, không chỉ là hiện tại ngay lập tức. Bạn đúng rằng sao chép và dán bây giờ dễ dàng hơn, nhưng tuyên bố, tôi không cần phải chỉnh sửa lại bất cứ lúc nào sớm " cho thấy bạn không suy nghĩ chính xác. Vâng, bạn có thể mua cho mình một chút thời gian với sao chép / dán nhanh và bẩn, nhưng khi làm như vậy bạn đang thể hiện rằng bạn không thể nhìn xa hơn vấn đề tức thời của mình và nghĩ về ngày mai. Bạn có tích cực không bao giờ cần phải xem lại mã này không? Bạn có biết chắc chắn có Không có lỗi trong đó? Bạn có thể đảm bảo 100% bạn sẽ không cần phải xem lại khi bộ tính năng tiếp theo của bạn cần được triển khai không? Đó là những vấn đề cho ngày mai và cần được xem xét khi bạn thiết kế hôm nay.

Tất nhiên, có những lúc cần sao chép / dán. Là một nhà phát triển giao diện người dùng, tôi đã thấy rằng đôi khi tôi phải vi phạm nguyên tắc DRY. Nó thật tệ, tôi co rúm mỗi khi nó xảy ra, và may mắn thay, nó rất hiếm. Nhưng nó xảy ra.

Sự khác biệt là khi vi phạm DRY, bạn nên có một lý do rất thuyết phục để làm điều đó, và tuyên bố, Thật khó để tìm ra cách biến tất cả những điều đó thành một mô hình đơn giản không thực sự là một trong số chúng. Trừ khi bạn đang trong thời gian khủng hoảng khủng khiếp và khiến sếp của bạn la hét để có được thứ gì đó trong vài giờ tới hoặc bạn sẽ mất việc, tôi không nghĩ đây là một lý do hợp lệ.

Đừng hiểu sai về điều này: Tôi không cố gắng thiến hoặc trừng phạt bạn, mà là thử và đưa bạn đến xem tâm lý của bạn sai ở đâu. Lập trình viên đầu tư vào sự lười biếng trong tương lai; DRY là một cách để đạt được điều đó. Công việc bạn làm hôm nay giải quyết một vấn đề thiết kế khó khăn sẽ được đền đáp vào ngày mai.


Tôi không chắc chắn rằng tôi đồng ý với một ông chủ nói với bạn "hoàn thành công việc hoặc bạn bị sa thải" là một lý do tuyệt vời để vi phạm DRY. Nợ kỹ thuật, khi nào bạn có thời gian để làm đúng, nó có thực sự tiết kiệm thời gian không, v.v?
Ẩn danh

1
@Incognito, tôi đã cố tỏ ra hơi mỉa mai :)
bedwyr

7

Tôi không cần phải chỉnh sửa lại bất cứ lúc nào sớm.

Nếu đây thực sự là trường hợp thực sự, thì bạn có thể thoát khỏi nó, nhưng thường xuyên hơn là bạn sẽ không làm việc với mã cần được duy trì. Điều đó có nghĩa là mở rộng chức năng, sửa lỗi và cải tiến khác. Nếu bạn có các biến thể nhỏ của cùng một mã ở 10 địa điểm khác nhau và một ngày nào đó bạn quay lại mã đó và cần thực hiện thay đổi, bây giờ bạn có nhiệm vụ dễ bị lỗi khi thực hiện cùng một thay đổi ở 10 địa điểm khác nhau (Xin lỗi, ở đó được 11 địa điểm, bạn đã quên một và bây giờ bạn có một lỗi).

Nếu bạn có thể khái quát vấn đề nào bạn đang cố gắng giải quyết, bạn có thể làm cho mã của mình dễ dàng mở rộng và khắc phục hơn nếu lỗi xuất hiện.


Câu trả lời tuyệt vời, nhưng ngay cả khi tôi có thể thoát khỏi nó, vậy còn nhựa nghèo để duy trì nó sau tôi thì sao? ;).
Ẩn danh

Xem: "thường xuyên hơn là bạn sẽ không làm việc với mã cần được duy trì" :)
Alex

Ngay cả sau đó chỉ là trường hợp nếu những gì bạn sao chép và dán là sự hoàn hảo không có lỗi 100%. Và nó không phải, và ngừng suy nghĩ nó có thể.
Dan Ray

Tôi sẽ thừa nhận tôi đã sao chép và dán các thứ trong quá khứ, nhưng không bao giờ cho bất cứ điều gì tôi sẽ giữ xung quanh hơn một ngày. Đôi khi bạn chỉ cần một kịch bản ném đi nhanh chóng và bẩn thỉu.
Alex

6

Như tôi đã nêu trong câu trả lời cho một câu hỏi khác, cách tiếp cận của tôi là như sau:

  1. Lần đầu tiên tôi giải quyết một vấn đề nào đó, tôi mới hoàn thành nó.
  2. Lần thứ hai (tức là khi tôi giải quyết một vấn đề tương tự) tôi nghĩ: hm, có lẽ tôi đang lặp lại chính mình, nhưng tôi sẽ đi sao chép nhanh và dán ngay bây giờ.
  3. Lần thứ ba tôi nghĩ: hm, tôi đang lặp lại chính mình -> làm cho nó chung chung!

Tức là lên đến 2, một nguyên tắc khác (YAGNI) thắng DRY. Nhưng bắt đầu từ 3 (hoặc 4 nếu tôi thực sự lười biếng!) Có vẻ như tôi sẽ cần nó và vì vậy tôi đi theo DRY.

Cập nhật

Một số ý tưởng từ kinh nghiệm gần đây của tôi. Tôi đã phải điều chỉnh / tích hợp hai thành phần A và B được phát triển bởi một nhóm khác vào sản phẩm của chúng tôi. Đầu tiên: hai thành phần A và B B rất giống nhau, vì vậy tôi đã bị làm phiền bởi thực tế là chúng có kiến ​​trúc hơi khác nhau. Thứ hai: Tôi phải điều chỉnh chúng vì vậy tôi sẽ rất vui khi sử dụng các lớp con và chỉ ghi đè lên những gì tôi thực sự cần.

Vì vậy, tôi bắt đầu tái cấu trúc hai thành phần này (mỗi thành phần bao gồm khoảng 8 lớp C ++): Tôi muốn có một kiến ​​trúc chung cho cả A và B, sau đó thêm các tính năng chúng ta cần bằng cách xác định các lớp con. Theo cách này, hai thành phần mới A 'và B' của chúng tôi sẽ được lấy từ các thành phần hiện có.

Sau hai tuần cố gắng để có được một cấu trúc phổ biến và được xác định rõ ràng từ mã hiện có và phải giải thích trong các cuộc họp hàng ngày của chúng tôi rằng tôi đã đạt được ít tiến bộ vì mã ban đầu quá lộn xộn, tôi đã nói với sếp của mình. Chúng tôi quan sát thấy rằng chúng tôi sẽ không cần bất cứ thứ gì ngoài hai thành phần mới A 'và B' (sẽ không có bốn hoặc sáu trong số chúng, chỉ có hai thành phần đó).

Ok, vậy thôi: Tôi đã thực hiện một bản sao lớn và đổi tên các lớp từ A và B và bắt đầu điều chỉnh bản sao của mã. Tôi đã làm cho nó hoạt động trong hai tuần nữa (hiện vẫn đang sửa một số lỗi).

Ưu điểm: Chúng tôi có chức năng gần như đã hoàn thành và khi chúng tôi đã sửa tất cả các lỗi chúng tôi đã hoàn tất. Chúng tôi đã lưu tất cả các phép tái cấu trúc và thử nghiệm của A và B.

Nhược điểm: Hai tuần trước, nhóm khác đã thay đổi thành phần C khác, được sử dụng bởi A và B. Họ đã điều chỉnh A và B nhưng A 'và B' cũng bị hỏng và chúng tôi phải tự thay đổi chúng. Điều này đã giới thiệu một lỗi mới mà chúng tôi phải sửa. Công việc làm thêm này có lẽ sẽ không cần thiết nếu A 'và B' đã chia sẻ hầu hết mã của họ với A và B.

Vì vậy: sao chép mã luôn nguy hiểm. Tôi nghĩ luôn luôn là vấn đề tìm kiếm sự đánh đổi và thường thì không dễ dàng gì.


5

Chỉ cần làm rõ, vì tôi không tìm thấy điều này trong bất kỳ câu trả lời nào khác:

DRY là một nguyên tắc phát triển phần mềm nhằm giảm sự lặp lại thông tin của tất cả các loại .

Mỗi phần kiến ​​thức phải có một đại diện duy nhất, rõ ràng, có thẩm quyền trong một hệ thống.

Nguyên tắc DRY như được đề cập bởi Andy Hunt và Dave Thomas không giới hạn trong việc ngăn chặn việc sao chép mã. Nó cũng ủng hộ việc tạo mã và bất kỳ quy trình tự động hóa nào. Trớ trêu thay, kết quả tạo mã thậm chí có thể là mã trùng lặp ...

Lý do tại sao đã được giải thích cặn kẽ trong các câu trả lời khác, nhưng bình luận của Falcon tổng hợp nó đủ tốt IMHO:

Một điểm thay đổi duy nhất là dễ dàng hơn để duy trì.


Ồ wow, tôi nghĩ thẻ có một số dữ liệu trong đó. Tôi sẽ đặt một số thông tin trong đó.
Ẩn danh

3

Có một điều như quá nhiều KHÔ. Khi điều này xảy ra, hai khái niệm xuất hiện tại một số điểm tương tự nhau để đảm bảo mã bao thanh toán (1) sau đó có thể trở nên đủ khác biệt để chúng xứng đáng được triển khai riêng biệt.

Nói cách khác, DRY và khớp nối lỏng lẻo đôi khi xung đột. Nếu bạn mong muốn doStuff1 và bạn bè phân kỳ với mỗi lần phát hành phần mềm mới, bạn có thể sao chép mã của họ.

Theo kinh nghiệm của tôi, có thể khó đánh giá phần mềm của bạn sẽ đi đâu trong tương lai và vì lý do này, DRY thường là một lựa chọn an toàn.

Mã đã được "sấy khô" quá mức thường có luồng điều khiển phức tạp và quá nhiều tham số. Ban đầu, một chức năng đơn giản sau đó đã được mở rộng để hỗ trợ chức năng mới được điều khiển bởi một tham số phụ. Sau hai hoặc ba lần lặp, chức năng không còn duy trì được nữa. Khắc phục một lỗi xảy ra trong một cài đặt và bạn giới thiệu các lỗi mới trong các cài đặt khác.

Có thể hiểu rằng chất lượng mã thường đi xuống khi mã phát triển, nhưng tôi đã thấy các trường hợp trong đó hàm đa tham số với spaghetti if-then-other trong cơ thể là kết quả của nỗ lực tái cấu trúc có ý nghĩa tốt nhưng được thực hiện kém.

(1) Tôi đang sử dụng từ "mã", nhưng điều này cũng đúng với thiết kế.


Sẽ rất hữu ích khi đưa ra một ví dụ về "quá khô" vì nó là phần cuối của quang phổ ít thấy hơn.
Ẩn danh

@Incognito: Tôi đã chỉnh sửa câu trả lời của mình. Không có ví dụ cụ thể, nhưng hy vọng những gì tôi muốn nói là đủ rõ ràng.
Joh

2

Tôi phải đề cập đến các vấn đề với DRY trong thế giới cơ sở dữ liệu quan hệ. Cơ sở dữ liệu được thiết kế để thực hiện nhanh chóng và tốt bằng cách sử dụng logic dựa trên tập hợp và thông qua các truy vấn có thể mở rộng. Các nguyên tắc DRY thường khiến nhà phát triển viết các truy vấn không thể Sargable hoặc sử dụng logic Row-by-agonizing-Row để tận dụng mã hiện có trong nhiều tình huống. DRY và tối ưu hóa hiệu suất thường mâu thuẫn và trong thế giới cơ sở dữ liệu, hiệu suất thường quan trọng hơn nhiều so với bảo trì. Điều này không có nghĩa là bạn hoàn toàn không nên sử dụng các nguyên tắc DRY, chỉ là bạn nên biết cách nó sẽ ảnh hưởng đến khả năng sử dụng chung của cơ sở dữ liệu. Nhà phát triển ứng dụng điều DRY trước và hiệu suất thứ hai, nhà phát triển cơ sở dữ liệu nghĩ rằng toàn vẹn dữ liệu trước, hiệu suất thứ hai, bảo mật dữ liệu thứ ba (hiệu suất và bảo mật có thể hoán đổi vị trí trong một số hệ thống).

Nói chung, tôi nhận thấy rằng càng nhiều lớp trừu tượng bạn đưa vào truy vấn cơ sở dữ liệu thì chúng càng trở nên chậm hơn. Tôi không nói rằng tôi không muốn những người tự thiết kế các chương trình cơ sở dữ liệu không làm tốt hơn việc cho phép các nhà phát triển sử dụng DRY mà không ảnh hưởng đến việc cơ sở dữ liệu hoạt động tốt như thế nào, nhưng tôi không thiết kế phần mềm cơ sở dữ liệu ở mức đó , vì vậy có lẽ mâu thuẫn giữa trừu tượng và hiệu năng trong cơ sở dữ liệu khó khắc phục hơn tôi cho là. Tuy nhiên, chúng tôi phải làm việc với các hệ thống vì chúng hiện đang được xây dựng. Chúng tôi có thể yêu cầu thực hiện tốt hơn các nguyên tắc DRY trong các bản phát hành trong tương lai sẽ không làm giảm hiệu suất (và nó đã trở nên tốt hơn qua nhiều năm nhưng vẫn có vấn đề), nhưng trong khi đó, chúng tôi phải xem xét liệu DRY có phải là bước đi đúng đắn cho cơ sở dữ liệu này không tại thời điểm này.

Nhưng thường thì các tính năng mà bạn muốn sử dụng để đảm bảo nguyên tắc DRY được đáp ứng là những tính năng gây ra vấn đề rất lớn cho cơ sở dữ liệu. Tôi không nói không bao giờ sử dụng DRY nhưng đừng quá nhiệt tình với nó.

Ví dụ về những gì tôi đang nói về. Bạn cần thực hiện nhập dữ liệu của một triệu bản ghi mỗi tháng một lần. Bản ghi có thể được thêm thủ công thông qua giao diện người dùng gọi một Proc được lưu trữ. Proc này, bởi vì nó được thiết kế cho nhập khẩu bản ghi duy nhất, chỉ thêm một bản ghi tại một thời điểm. Sử dụng DRY để tránh có mã chèn ở hai vị trí, bạn viết một con trỏ để gọi Proc liên tục thay vì viết các nhập khẩu dựa trên tập hợp mà bạn cần. Thời gian để nhập từ 30 phút sẽ sử dụng logic dựa trên thiết lập đến 18 giờ. Bây giờ cách đúng đắn để tuân thủ DRY trong trường hợp này sẽ là sửa lỗi Proc để xử lý nhập khẩu bản ghi mulitple. Thật không may, thường không thể hoặc rất khó để gửi một mảng đến một Proc (tùy thuộc vào phần cuối của db) và bằng cách thay đổi Proc, cuối cùng bạn sẽ phá vỡ ứng dụng.

Các hàm vô hướng và các hàm có giá trị bảng cũng được sử dụng để thực hiện các nguyên tắc DRY và một lần nữa chúng có thể ảnh hưởng nghiêm trọng đến hiệu suất, đặc biệt nếu bạn cần sử dụng chúng theo cách ngăn các chỉ mục trở nên hữu ích.

Lượt xem cũng tốt cho việc thực hiện DRY. Tuy nhiên, nếu bạn triển khai DRY thông qua việc sử dụng các chế độ xem gọi các chế độ xem gọi các chế độ xem khác, bạn sẽ nhanh chóng đi đến điểm mà các truy vấn sẽ hết thời gian tải. Trong thực tế, bạn có thể cần phải tạo ra bộ dữ liệu của hàng triệu bản ghi khi bạn chỉ cần ba cái ở cuối. Vì vậy, chế độ xem một cấp của một tập hợp phức tạp để triển khai DRY có thể rất tuyệt vời (tôi có một bản thân mà chúng tôi sử dụng để đảm bảo tất cả báo cáo tài chính sử dụng cùng một bảng cơ sở và tính toán của một số điều nhất định), hơn hai cấp và bạn cần xem xét nếu bạn đang tạo ra một mớ hỗn độn hiệu suất.


1

Tôi không thấy những điểm chính trong câu trả lời của mình ở trên, vì vậy hãy đến đây. Đừng xem DRY nhiều như một quy tắc chống lạiđang làm gì đó. Nó có thể được thực hiện như vậy nhưng nó thực sự có thể phục vụ một mục đích khá khác biệt và tích cực. Đó là một tín hiệu để dừng lại, suy nghĩ và tìm một câu trả lời tốt hơn. Nó thách thức tôi tìm kiếm cơ hội để thiết kế một giải pháp tốt hơn. Đó là mặt tốt của mùi hôi trong mã của tôi khiến tôi phải suy nghĩ lại về thiết kế của mình và khiến tôi làm nó tốt hơn rất nhiều. DRY không chỉ là vi phạm cú pháp bitty. Nó thách thức tôi để mô đun hóa. Nó thách thức tôi thành lập. Nó báo hiệu sự lặp lại nhắc nhở tôi suy nghĩ về việc sử dụng các mẫu và tạo mã thay vì vũ phu và thiếu hiểu biết. Nó giúp tôi nhận ra rằng tôi nên tìm một chút thời gian để tự động hóa tự động hóa của mình. Nó dẫn bạn đến một lối sống kỳ dị! Nó giúp bạn dành nhiều thời gian hơn để làm những thứ mới mẻ mát mẻ hơn là những chi tiết nhàm chán cũ kỹ. Và nó mang lại cho bạn cách cư xử tốt, hơi thở tốt và lối sống lành mạnh! Chà, có lẽ tôi sáp một chút lạc lối ....


DRY có những tác động rất khác nhau đối với tôi, tuy nhiên, nếu đây là những tác động của nó đối với bạn, tôi thích triết lý của một thứ gì đó là "tín hiệu dừng lại, suy nghĩ và tìm ra câu trả lời tốt hơn" và thách thức.
n611x007

1

Tôi có một dự án cũ, nơi một số nhà phát triển trước đây không quan tâm đến DRY. Vì vậy, toàn bộ cơ sở mã đã bị lộn xộn với các phương thức của trình trợ giúp như GetSystemTimeAsString (), LogToFile () và rất nhiều thứ khác. Một số phương pháp được tùy chỉnh một chút cho các nhu cầu đặc biệt, nhưng hầu hết chỉ là sao chép và dán.

Thật không may, một số phương thức có các lỗi tinh vi như mảng char không đủ dài trong một số trường hợp, sử dụng các công cụ không an toàn như strcpy (), v.v.

Vì vậy, nó là một PITA thực sự để tìm tất cả các đoạn mã, hài hòa chúng và sửa lỗi. Và chúng tôi vẫn hài hòa và sửa chữa công cụ.

Bạn không bao giờ biết, nếu bạn mắc lỗi trong phương pháp đầu tiên và sau đó phải sửa nó nhiều lần, vì bạn vừa sao chép nó. Và nếu bạn muốn sử dụng một số phương thức sau này, làm thế nào để bạn biết, trong số 5 phương thức trong codebase là phương thức nào cho trường hợp của bạn bây giờ? Vì vậy, bạn chỉ cần sao chép một, tùy chỉnh nó và ở đây nó bắt đầu lại ...


1

Có, đừng bận tâm về DRY nếu bạn đang viết Mã Ném .

Nhưng DRY là quan trọng tất nhiên, nếu bạn có kế hoạch để giữ mã.


1

Cụm từ "đừng lặp lại chính mình" là một chút quá đơn giản. Điều quan trọng là "tránh để một phần thông tin có khả năng thay đổi được gói gọn ở hai nơi độc lập ."

Nếu một chương trình được yêu cầu xử lý các widget, mỗi chương trình có ba woozles và có nhiều vòng lặp của biểu mẫu

for (i=0; i<3; i++)
  thisWidget.processWoozle(i);

sau đó, kỳ vọng rằng các vật dụng dự kiến ​​sẽ chứa ba woozles sẽ được gói gọn trong mỗi vòng lặp đó và việc cập nhật mã để chứa bất kỳ số lượng woozles nào trên mỗi widget có thể khó khăn. Ngược lại, nếu người ta muốn nói

#define WOOZLES_PER_WIDGET 3

và mỗi vòng lặp được viết lại

for (i=0; i<WOOZLES_PER_WIDGET; i++) ...

thiết kế như vậy có thể giúp dễ dàng thay đổi số lượng woozles trên mỗi widget.

Tuy nhiên, điều quan trọng cần lưu ý là mặc dù mong muốn hợp nhất thông tin như số lượng woozles trên mỗi widget vào một điểm duy nhất, nhưng không phải lúc nào cũng thực tế. Đôi khi có thể cần logic mã cứng sẽ chỉ hoạt động nếu mọi thứ có kích thước cụ thể. Ví dụ: nếu mỗi woozle có một giá trị và người ta muốn tìm giá trị trung bình được liên kết với một tiện ích cụ thể, thì có thể sắp xếp các giá trị và lấy giá trị ở giữa, và cách tiếp cận như vậy sẽ hoạt động với bất kỳ số lượng woozles nào, nhưng logic được viết bằng tay đặc biệt để tìm trung vị của ba mục có thể nhanh hơn đáng kể.

Mặc dù có hằng số WOOZLES_PER_WIDGET có thể làm cho mã dễ đọc hơn, nhưng cần phải nhận xét để làm rõ rằng giá trị của nó không thể thay đổi mà không cần điều chỉnh khác với logic chương trình. Trong trường hợp đó, logic được mã hóa cứng cho ba mục và hằng số WOOZLES_PER_WIDGET sẽ sao chép thông tin "mỗi tiện ích có ba woozles", nhưng lợi ích của việc sao chép đó (tốc độ thực thi lớn hơn) có thể vượt quá chi phí.


0

Trong khi tôi thực sự đồng ý với các bình luận áp phích khác về khả năng bảo trì, vv tất cả đều hợp lệ.

Tôi muốn thêm một tiếng nói bất đồng nhỏ vào cuộc tranh luận.

  • Nó chỉ quan trọng đối với các lập trình viên. Những người trả lương của bạn không thể quan tâm miễn là phần mềm vượt qua UAT.
  • Về tầm quan trọng, nó xếp hạng thấp hơn các mục như nhận được các yêu cầu chính xác, lắng nghe các nhà tài trợ dự án và giao hàng đúng hạn.

vì trang web này dành cho "Lập trình viên" Tôi nghĩ thật an toàn khi nói câu hỏi được hướng vào "quan điểm lập trình viên". Tất nhiên, những tuyên bố của bạn về người trả lương, UAT và cấp bậc quan trọng là hợp lệ, nhưng không liên quan đến câu hỏi cụ thể này.
ozz

1
Tôi hoàn toàn không đồng ý. Quản lý tốt sẽ hiểu các nguyên tắc và lý do tại sao chúng được thực hiện nếu được giải thích chi tiết. Đây phải là một cuộc trò chuyện chuyên sâu, không phải là 5 phút.
Michael Durrant

2
Điểm đạn thứ hai của bạn là hoàn toàn chính xác.
Jim G.

1
@Ozz, niềm tự hào về nghề của bạn rất quan trọng thậm chí cần thiết cho một lập trình viên giỏi, nhưng, có lẽ "quan điểm lập trình viên" phải bao gồm một chút quan tâm về "sự hài lòng của khách hàng".
James Anderson

0

<tl;dr>

Tôi không thể đọc tất cả các câu trả lời lặp đi lặp lại vì vậy tôi có thể đã bỏ lỡ điều gì đó (và sẽ tự lặp lại nó <= xem tôi đã làm gì ở đây?).

Dưới đây là danh sách những điều tuyệt vời về việc ngăn chặn sự sao chép mã!

  1. Dễ kiểm tra hơn: bạn chỉ cần kiểm tra một 'bản sao' của mã.
  2. Dễ sửa hơn: bạn chỉ cần tìm lỗi trong một 'bản sao' của mã và sửa nó một lần.
  3. Dễ dàng cập nhật hơn (giống như trên): những thay đổi cần thiết, thường có thể được xử lý bằng cách sửa đổi mã ở rất ít nơi vì bạn đã dành thời gian để sử dụng lại mã đúng cách và không sao chép cùng một dòng vào hàng trăm hoặc hàng nghìn vị trí khác nhau trong nguồn.
  4. Dễ dàng sử dụng lại: khi (không được sao chép ở một vài nơi) và được giữ trong các phương thức được đặt tên chung chung, bạn có thể dễ dàng tìm thấy chúng và sử dụng chúng thay vì viết riêng.
  5. Dễ đọc hơn: mã trùng lặp rất khó đọc vì nó dài dòng không cần thiết; nó chứa rất nhiều dòng không phải là một phần của logic và chức năng dự định cụ thể (ví dụ các lệnh chung được sử dụng để thiết lập giai đoạn cho hành động diễn ra hoặc các nhiệm vụ lặp lại đơn giản chung chung cần thiết ở nhiều nơi). Mã sạch làm cho logic và chức năng bật ra vì không có sự lặp lại rải rác trong không gian mã.
  6. Dễ dàng gỡ lỗi hơn vì (1) và (5).
  7. Tiết kiệm thời gian và tiền bạc của bạn và làm nhiều điều thú vị hơn trong tương lai; đặc biệt tạo mã mạnh hơn tốt hơn. Đây là điểm mấu chốt và đó là tổng kết của khá nhiều thứ ở trên. Nếu nhiều người sử dụng cùng một chức năng doFoo1(a, b), có nhiều khả năng nhiều lỗi và các trường hợp khó chịu của nó sẽ được phát hiện và giải quyết. Nếu mọi người sao chép mã và tạo doFoo2(specialA)... doFuu2^n(a, b, c)thì họ đã sao chép các vấn đề trong đó doFoo1và cụ thể tạo ra nhiều công việc hơn.

</tl;dr>

Phiên bản dài:

Vấn đề với sao chép mã là nó "phát triển theo cấp số nhân" (nói cách khác, nó mở rộng nhanh chóng) gây ra khi bạn sao chép mã mà bạn vô tình cấp cho người khác quyền (đối với một người, bạn không còn ở vị trí để phán xét họ) và bạn khuyến khích họ làm như vậy. Bạn cũng làm cho nó khó hơn không vì khó phát hiện và sử dụng lại mã hữu ích khi có rất nhiều sự lặp lại dư thừa khó hiểu trong nguồn. Đặc biệt là nếu mã chưa được trích xuất thành một hàm có tên thích hợp. Vì vậy, nếu bạn gặp phải một vấn đề đơn giản phổ biến để giải quyết vấn đề, bạn có thể sẽ tự viết một đoạn mã để giải quyết nó. Và bạn có thể sẽ không kiểm tra một vài trường hợp cạnh, thêm mã lỗi chưa được kiểm tra.

Một điều nữa là đối với người mới, điều này nghe có vẻ như là một vấn đề sẽ chỉ ảnh hưởng đến các công ty lớn nhưng tôi thấy nó gây ảnh hưởng đến các công ty khởi nghiệp nhỏ chỉ là một vấn đề tồi tệ (như trong 10.000 dòng mã phía máy chủ trùng lặp). Nó là một trạng thái của tâm. Bạn không chỉ nên thành thạo DRY mà còn cố gắng khuyến khích người khác làm điều tương tự; bởi vì nếu không, bạn sẽ tự cam chịu mã trùng lặp. Khi các phương tiện của DRY trong tầm tay và được thi hành, việc áp dụng chúng sẽ dễ dàng hơn nhiều. Khi có nhiều mã trùng lặp, việc áp dụng các giải pháp sao chép sẽ dễ dàng hơn nhiều.

Những điều tôi thấy có hại trong sao chép mã:

  1. Là chức năng này có thể sử dụng? Giả sử bạn tìm thấy một chức năng thực hiện (hoặc dường như để làm những gì bạn cần), làm thế nào để bạn biết liệu nó có hoạt động chính xác hay không nếu đó chỉ là mã bị trùng lặp và bị bỏ rơi.
  2. Mã dự phòng. Đôi khi mọi người sao chép mã, sử dụng nó và quên nó (họ luôn có thể sao chép lại mã đó trong tương lai). Tại một số thời điểm, một số người sẽ loại bỏ các cuộc gọi đến chức năng trùng lặp ở một số nơi trong nỗ lực tái cấu trúc nhưng chức năng không được sử dụng vẫn còn ngay cả khi nó không được sử dụng tích cực.
  3. Khó tìm thấy những gì bạn đang tìm kiếm. Mã trùng lặp chiếm không gian và làm cho việc tìm kiếm những thứ hữu ích và cần thiết (sử dụng các công cụ như grep) trở thành một nhiệm vụ khó khăn hơn so với khi bạn nhận được hàng chục hoặc hàng ngàn kết quả mà bạn chỉ nên nhận một số ít.
  4. (Đã được đề cập trước đó): Khó bảo trì nhưng cũng khó sử dụng cho mục đích duy trì và hồi quy. Nếu mã kiểm tra được sao chép và không được trích xuất chính xác vào các hàm, những người khác sẽ sao chép nó. Có ai bận tâm với việc viết một API dễ sử dụng, dễ đọc để làm cho QoL tốt hơn không? Theo kinh nghiệm của tôi, không, thường có một cái gì đó mọi người cho là vấn đề cấp bách hơn cho đến khi nó vượt khỏi tầm tay.
  5. Sao chép mã khó đọc hơn vì nó làm cho mã dài dòng mà không cần phải ở đó, ở những nơi mà tính dài dòng không thêm thông tin về chức năng dự định: ví dụ như các lệnh gọi phương thức chung được sử dụng [lặp đi lặp lại] đặt nền tảng cho nhiều loại chức năng dự định, làm cho chức năng thực tế này khó bật ra hơn.
  6. Điều này đã được đề cập rất nhiều. Nếu mã sai, một số người nghèo hoặc anh chàng sẽ cần tìm kiếm và thay đổi mỗi lần sử dụng mã đó. Chẳng hạn, nếu ai đó đã sử dụng lệnh gọi SQL không an toàn tới mysql_query ở một vài nơi trong Lớp có tổ chức khi cần, bạn có thể dễ dàng sửa nó và sử dụng PHP PDO thay vào đó nhưng nếu họ sử dụng nó trong hơn một nghìn nơi sao chép cuộc gọi qua và hơn thế nữa, việc sửa nó thực tế sẽ cần phải thuê ngoài hoặc đôi khi nguy hiểm hơn, mã sẽ cần phải được viết lại từ đầu.
  7. Mã trùng lặp là một thói quen xấu. Nếu bạn thực hành một cái gì đó, nó dần trở thành bản chất thứ hai và nó ảnh hưởng đến mọi người xung quanh bạn. Junior devs thấy bạn làm điều đó và cũng làm nó. Bạn nên thực hành những gì bạn giảng và tạo thói quen làm đúng. Bạn tìm hiểu thêm. Viết mã không trùng lặp khó hơn và nhiều thách thức hơn. Đó là một thói quen bổ ích.

Lưu ý cuối cùng về phòng chống trùng lặp mã quá nhiệt & tóm tắt mọi thứ:

Điều này cũng đã được nói trước đây nhưng đôi khi tránh trùng lặp khiến bạn "cúi người về phía sau" và làm những việc quá phức tạp (hoặc không quan trọng) để người khác hiểu. Viết mã không thể đọc được (hoặc như chúng tôi gọi đùa là công việc của chúng tôi bảo vệ mã của Google) là một vấn đề ngay cả khi không ngăn chặn việc sao chép mã. Tuy nhiên, tôi thấy rằng nếu cơ sở hạ tầng phù hợp và các thực tiễn tốt nhất được thấm nhuần ngay từ đầu thì việc ngăn chặn sao chép mã sẽ dễ dàng hơn nhiều và mọi người thường có thể tránh làm những việc không trực quan để có thể ngăn chặn hàng đống công việc không cần thiết và không thể đọc được trong tương lai để ngăn chặn việc sao chép mã bạn làm điều ngay từ khi bắt đầu.

Điều gì đang làm đúng? Đó là một câu hỏi khó trả lời nhưng có một điều là xác định phương pháp nào là cần thiết cho dự án và xem những gì đã được thực hiện bởi những người khác (bên ngoài và) trong công ty và sử dụng lại khi có thể; ghi lại tất cả mọi thứ bạn làm để thêm vào cơ sở mã và cố gắng làm cho nó trở nên chung chung hơn so với nó nhưng đó là nó. Đừng lạm dụng các mẫu thiết kế chỉ để làm cho mã linh hoạt ở nơi không cần thiết.

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.