Khi nào một thư viện 'lõi' là một ý tưởng tồi?


8

Khi phát triển phần mềm, tôi thường có thư viện 'lõi' tập trung chứa mã tiện dụng có thể được chia sẻ và tham chiếu bởi các dự án khác nhau.

Ví dụ:

  • một tập hợp các hàm để thao tác các chuỗi
  • thường được sử dụng biểu thức chính quy
  • mã triển khai chung

Tuy nhiên, một số đồng nghiệp của tôi dường như đang quay lưng lại với phương pháp này. Họ có những lo ngại như chi phí bảo trì của mã kiểm tra lại được sử dụng bởi nhiều dự án một khi lỗi được sửa. Bây giờ tôi đang xem xét lại khi tôi nên làm điều này.

Các vấn đề khiến việc sử dụng thư viện 'lõi' trở thành một ý tưởng tồi là gì?


Có một thư viện lõi là một ý tưởng tốt khi mã thường được sử dụng lại, nhưng nó cần phải được kiểm tra một cách tôn giáo, bao gồm các bài kiểm tra đơn vị và công nghệ không gian khác.
Công việc

Đó là một ý tưởng tốt khi nó đã ổn định và không thay đổi.
Martin York

Mối quan tâm thi lại là rất hợp lệ. Bạn có muốn tìm hiểu bạn đã phá vỡ một dự án bảo trì 6 tháng trở lại?

Tôi không thể tưởng tượng việc viết lại tất cả các mã tiện ích của mình mỗi khi tôi cần nó.

Câu trả lời:


12

Các thư viện cốt lõi rất tệ khi chúng bắt đầu bị lỗi từ tính năng và rất tệ khi chúng không được bảo trì tốt.

Bạn có thể thấy bài viết này thú vị cho một quan điểm mở rộng (mà tôi hoàn toàn đồng ý với):

http://www.yosefk.com/blog/redundancy-vs-dependencies-which-is-worse.html


Don Knuth: "Đối với tôi, 'mã có thể chỉnh sửa lại' tốt hơn nhiều so với hộp đen hoặc bộ công cụ không thể chạm tới ... bạn sẽ không bao giờ thuyết phục tôi rằng mã có thể tái sử dụng không phải là mối đe dọa."


3

Sử dụng ý tưởng về một thư viện lõi là xấu khi nhiều dự án phụ thuộc vào nó, giống như nói rằng bạn không nên sử dụng jQuery cho web, libxml trong các ứng dụng * nix của bạn hoặc bất kỳ khung hoặc thư viện nào khác. Nhìn vào toàn bộ hệ sinh thái của sự phát triển hiện đại (DRY, OOP, v.v.) và mỗi ứng dụng được xây dựng từ một tập hợp các thư viện và khung.

Điều có thể xấu là nếu bạn không có bất kỳ loại thử nghiệm đơn vị nào, bạn không thử nghiệm hồi quy và bạn không sử dụng bất kỳ loại API / ABI nào với thư viện của mình. Nếu tất cả các ứng dụng của bạn có các bài kiểm tra phù hợp, thư viện của bạn sẽ có kiểm tra phù hợp và bạn chắc chắn rằng nếu bạn ngắt chức năng, hãy gọi cập nhật số phiên bản api một cách thích hợp.

Để có phạm vi bảo hiểm đầy đủ, điều người ta có thể muốn là khi các thay đổi được thực hiện đối với Thư viện, bạn có thể chạy một bộ thử nghiệm sẽ xác minh API chưa bị hỏng và việc thực thi tất cả mã không có lỗi. Sau đó, bạn có thể lấy bản cập nhật thư viện mới nhất vào ứng dụng của mình và chạy cùng một bộ kiểm tra. Nếu bạn cập nhật API, thì nó phải được ghi lại để bạn biết bạn cần làm gì trong ứng dụng của mình để cập nhật API. Dù bằng cách nào, khi bạn chạy thử nghiệm cho ứng dụng của mình, thì bạn có thể tự tin như bạn đang ở trong các thử nghiệm mà không có gì bị hỏng.

Khi sử dụng jquery, mootools, bất kỳ thư viện hoặc khung javascript nào, bạn không thể sử dụng một cách mù quáng phiên bản mới, đôi khi bạn không thể thậm chí với một bản phát hành 1.6.z nhỏ.


3

Họ có những lo ngại như chi phí bảo trì của mã kiểm tra lại được sử dụng bởi nhiều dự án một khi lỗi được sửa.

Nếu bạn có một bộ kiểm tra đơn vị toàn diện cho thư viện lõi; đó không phải là một vấn đề Không có mã sẽ được kiểm tra trừ khi tất cả các bài kiểm tra vượt qua. Nếu bạn giới thiệu một khiếm khuyết, bạn viết một bài kiểm tra thất bại để tái tạo lỗi và sửa nó; sau đó bạn sẽ luôn luôn kiểm tra lỗi đó. Mãi mãi.

Ngoài ra chức năng bạn mô tả là rất dễ dàng để viết bài kiểm tra đơn vị cho.

Là một vấn đề phụ, bạn có thể muốn có nhiều thư viện lõi, do đó bạn không cần phải bao gồm mã RegEx trừ khi bạn muốn.


2

Tôi sẽ cung cấp một chút khác nhau về điều này. Một thư viện cốt lõi, trong nhiều trường hợp, là một ý tưởng tuyệt vời!

Nếu bạn có hai dự án riêng biệt, chúng nên nằm trong hai kho mã riêng biệt. Bây giờ họ phụ thuộc vào chức năng chung. Hãy xem xét ví dụ các ứng dụng xử lý gói. Các chức năng phổ biến có thể bao gồm:

  • Cấp phát bộ nhớ
  • Địa chỉ Nghị quyết Nghị định thư
  • Cây AVL
  • Mã tuần tự hóa cho các giao thức nhị phân
  • Mảng động
  • Danh sách băm kiểu nhân Linux với đầu được liên kết đơn và các nút giữa được liên kết đôi
  • Bảng băm
  • Mã xử lý tiêu đề TCP / IP
  • Danh sách liên kết thường xuyên với đầu được liên kết đôi và các nút giữa được liên kết đôi
  • Thư viện đăng nhập
  • Linh tinh (tin tôi đi, bạn cần cái này cho những thứ nhỏ nhặt và tầm thường hoặc số lượng mô-đun khác nhau của bạn sẽ lên tới 100!)
  • Thư viện chụp gói
  • Thư viện giao diện gói I / O
  • Cấu trúc dữ liệu gói
  • Chặn hàng đợi để liên lạc giữa các luồng
  • Máy tạo số ngẫu nhiên
  • Cây đỏ đen
  • Một số loại thực hiện hẹn giờ

Bây giờ, các ứng dụng xử lý gói khác nhau có thể cần một tập hợp con khác nhau. Bạn nên triển khai một thư viện lõi với một kho lưu trữ mã nguồn, hay bạn nên có 18 kho lưu trữ khác nhau cho mỗi mô-đun này? Hãy nhớ rằng các mô-đun này có thể có các phụ thuộc lẫn nhau, vì vậy hầu hết các mô-đun này có thể phụ thuộc vào ví dụ mô-đun linh tinh.

Tôi sẽ tuyên bố rằng có một thư viện cốt lõi là cách tiếp cận tốt nhất. Nó làm giảm chi phí của nhiều kho mã nguồn. Nó làm giảm địa ngục phụ thuộc: một phiên bản cụ thể của bộ cấp phát bộ nhớ có thể cần một phiên bản cụ thể của mô-đun linh tinh. Và nếu bạn muốn cấp phát bộ nhớ phiên bản 1.7 tùy thuộc vào phiên bản cây 2.5 và AVL linh tinh 1.2 tùy thuộc vào linh tinh 2.6 thì sao? Bạn không thể liên kết linh tinh 2.5 và linh tinh 2.6 cùng lúc với chương trình của mình.

Vì vậy, hãy tiếp tục và thực hiện cấu trúc sau:

  • Kho thư viện lõi
  • Kho dự án số 1
  • Kho dự án số 2
  • ...
  • Kho lưu trữ dự án #N

Tôi đã thấy rằng chuyển sang loại cấu trúc này từ cấu trúc:

  • Kho dự án số 1
  • Kho dự án số 2
  • ...
  • Kho lưu trữ dự án #N

Đã dẫn đến giảm bảo trì và tăng chia sẻ mã thông qua các cơ chế không sao chép.

Tôi cũng đã thấy các dự án sử dụng cấu trúc sau:

  • Kho phân bổ bộ nhớ
  • Kho lưu trữ giao thức phân giải địa chỉ
  • Kho lưu trữ cây AVL
  • Mã tuần tự hóa cho kho lưu trữ giao thức nhị phân
  • Kho lưu trữ mảng động
  • Danh sách băm kiểu nhân Linux với đầu liên kết đơn và kho lưu trữ nút giữa được liên kết đôi
  • Kho băm bảng
  • Kho lưu trữ mã xử lý tiêu đề TCP / IP
  • Danh sách được liên kết thường xuyên với đầu được liên kết đôi và kho lưu trữ nút giữa được liên kết đôi
  • Ghi nhật ký kho thư viện
  • Kho lưu trữ linh tinh (tin tôi đi, bạn cần cái này cho những thứ nhỏ nhặt và tầm thường hoặc số lượng mô-đun khác nhau của bạn sẽ lên tới 100!)
  • Kho thư viện chụp gói
  • Kho lưu trữ thư viện giao diện I / O
  • Kho lưu trữ cấu trúc dữ liệu gói
  • Chặn hàng đợi cho kho lưu trữ liên lạc giữa các luồng
  • Kho lưu trữ số ngẫu nhiên
  • Kho lưu trữ cây đỏ-đen
  • Một số loại kho lưu trữ thực hiện hẹn giờ
  • Kho dự án số 1
  • Kho dự án số 2
  • ...
  • Kho lưu trữ dự án #N

... và địa ngục phụ thuộc và sự gia tăng số lượng kho lưu trữ là những vấn đề thực sự.

Bây giờ, bạn có nên sử dụng một thư viện mã nguồn mở hiện tại thay vì viết riêng của bạn không? Bạn cần xem xét:

  • Vấn đề giấy phép. Đôi khi, yêu cầu đơn thuần để cung cấp tín dụng cho tác giả trong tài liệu được cung cấp có thể quá nhiều, vì 20 thư viện thường sẽ có 20 tác giả khác nhau.
  • Hỗ trợ phiên bản hệ điều hành khác nhau
  • Phụ thuộc của thư viện cụ thể
  • Kích thước của thư viện cụ thể: nó có quá lớn so với chức năng được cung cấp không? Liệu nó có cung cấp quá nhiều tính năng?
  • Là liên kết tĩnh có thể? Là liên kết năng động mong muốn?
  • Là giao diện của thư viện những gì bạn muốn? Lưu ý rằng trong một số trường hợp, việc viết một trình bao bọc để cung cấp giao diện mong muốn có thể dễ dàng hơn việc tự viết lại toàn bộ thành phần.
  • ... và nhiều, nhiều thứ khác tôi chưa đề cập trong danh sách này

Tôi thường sử dụng quy tắc rằng mọi thứ dưới 1000 dòng mã không yêu cầu thứ gì đó vượt quá chuyên môn của lập trình viên nên được tự mình thực hiện. Lưu ý: 1000 dòng bao gồm các bài kiểm tra đơn vị. Vì vậy, tôi chắc chắn sẽ không ủng hộ việc tự viết 1000 dòng mã nếu nó yêu cầu 10 000 dòng bổ sung cho các bài kiểm tra đơn vị. Đối với các chương trình xử lý gói của tôi, điều này có nghĩa là các thành phần bên ngoài duy nhất tôi đã sử dụng là:

  • Mọi thứ được cung cấp bởi một bản phân phối Linux tiêu chuẩn, bởi vì nó có quá nhiều dòng mã mà nó không có ý nghĩa để thực hiện lại Linux. Các phần của việc triển khai lại Linux cũng sẽ vượt quá trình độ chuyên môn của tôi.
  • Bison / flex vì phân tích cú pháp LALR vượt quá trình độ chuyên môn của tôi và hơn 1000 dòng mã. Tôi chắc chắn có thể tự mình viết một trình phân tích cú pháp gốc đệ quy, nhưng Bison / flex rất tiện dụng tôi thấy chúng là hữu ích.
  • Netmap, vì nó hơn 1000 dòng và vượt quá trình độ chuyên môn của tôi
  • Bỏ qua triển khai bộ đếm thời gian dựa trên danh sách từ DPDK, vì nó vượt quá trình độ chuyên môn của tôi mặc dù nó ít hơn 1000 dòng mã (mặc dù tôi có các triển khai hẹn giờ thay thế không sử dụng danh sách bỏ qua)

Một số điều tôi đã tự thực hiện vì chúng đơn giản bao gồm cả những thứ như:

  • MurMurHash
  • SipHash
  • Mersenne Twister

... bởi vì việc triển khai tùy chỉnh này có thể cho phép nội tuyến nặng, dẫn đến hiệu suất được cải thiện.

Tôi không làm mật mã; nếu tôi đã làm, tôi sẽ thêm một số loại thư viện tiền điện tử vào danh sách, vì tự mình viết thuật toán mật mã có thể dễ bị tấn công bộ đệm thời gian ngay cả khi bạn có thể kiểm tra đơn vị kỹ lưỡng cho thấy chúng tương thích với các thuật toán chính thức.


1

Một thư viện lõi có thể xấu khi nhiều dự án phụ thuộc vào nó, bạn không chỉ phải kiểm tra bất kỳ thay đổi nào đối với lõi mà còn phải kiểm tra hồi quy cho mỗi dự án phụ thuộc. Thứ hai, các API cốt lõi của bạn không bao giờ có thể thay đổi vì bạn sẽ phải cấu trúc lại mọi dự án phụ thuộc. Càng nhiều dự án sử dụng thư viện của bạn, bẫy càng sâu.

Một vấn đề khác là xu hướng bắt đầu ném mọi thứ "chung" vào thư viện cốt lõi của bạn, làm đầy nó và khiến cho việc kéo vào các mảnh nhỏ trở nên khó khăn hơn. Tôi sẽ chỉ nói rằng có một lần tôi nghe nói về một nơi trở nên sợ hãi khi chạm vào bất kỳ thư viện cốt lõi nào của họ, chi phí kiểm tra hồi quy QA là rất lớn.

Thay vào đó, có lẽ bạn có thể tạo một tài nguyên đoạn mã để cho phép các nhóm dự án tìm kiếm và lấy mã họ cần và tự cắt đứt mọi vấn đề về bảo trì hoặc hồi quy? Dù sao đó cũng là những gì tôi làm ở nhà.


4
Khó hơn rất nhiều để sửa một lỗi trong đoạn mã đã được sao chép và dán vào một số nơi mặc dù không phải vậy?
Alex Angas

Một câu trích dẫn của Donald Knuth: "Tôi cũng phải thú nhận sự thiên vị mạnh mẽ đối với thời trang đối với mã có thể tái sử dụng. Đối với tôi, mã có thể chỉnh sửa lại, rất tốt hơn nhiều so với hộp đen hoặc bộ công cụ không thể chạm tới. Tôi có thể tiếp tục và tiếp tục về điều này. Nếu bạn hoàn toàn tin rằng mã có thể tái sử dụng là tuyệt vời, có lẽ tôi sẽ không thể làm ảnh hưởng đến bạn, nhưng bạn sẽ không bao giờ thuyết phục tôi rằng mã có thể tái sử dụng không phải là mối đe dọa. "
Patrick Hughes

@AlexAngas: Điều đó đúng, nhưng có thể có trường hợp thư viện bị lỗi, nhưng chỉ hoạt động chính xác một số thư viện khác có lỗi tinh vi bù đắp lỗi trong lần đầu tiên. Mặc dù cả hai bộ lỗi nên được sửa khi thực tế, có một bản sao mã nguồn của thư viện thứ hai là một phần của dự án với cái đầu tiên có nghĩa là sửa lỗi được áp dụng cho mã đó sẽ là một thay đổi dễ nhận biết đối với dự án, điều này có thể tạm thời quay trở lại nếu nó phá vỡ mọi thứ (do đó cho phép nó được xác định là nguyên nhân của sự đổ vỡ).
supercat

@AlexAngas: Tất nhiên, xác định sửa lỗi cho thói quen thứ hai là nguyên nhân của sự cố không có nghĩa là biện pháp khắc phục không phải là sửa lỗi thứ hai, mà nó chỉ ra thực tế là một số mã bị nhầm lẫn dựa trên hành vi sai lầm của thói quen đó ; khám phá đó sẽ là chìa khóa để giải quyết hiệu quả các vấn đề thực sự. Ngược lại, nếu tất cả mọi người đều biết rằng mã được sử dụng để hoạt động tự phát ngừng hoạt động, sẽ rất khó để theo dõi những gì cần làm về nó.
supercat

1

Một điểm chưa được đề cập là bất kỳ mã nào sẽ có sự phụ thuộc vào một cái gì đó , ngay cả khi nó thực sự là thứ duy nhất chạy trong ROM của một vi điều khiển nhúng; nếu nhà sản xuất bộ điều khiển thay đổi một số hành vi mà mã dựa vào, mã sẽ phải được sửa đổi để hoạt động trên các chip được sản xuất sau khi thay đổi, hoặc các nhà sản xuất thiết bị sử dụng mã này sẽ phải mua chip bằng cách nào đó không kết hợp thay đổi - có thể trả giá cao cho họ.

Sử dụng một thư viện để thực hiện các chức năng phần cứng khác nhau có thể có nghĩa là mã hiện phụ thuộc vào thư viện trong khi trước đây, nhưng nó cũng có thể loại bỏ sự phụ thuộc giữa mã và phần cứng. Ví dụ, một nhà sản xuất chip có thể hứa sẽ cung cấp một thư viện cho tất cả các chip hiện tại và tương lai sẽ luôn thực hiện các chức năng I / O nhất định theo một cách nhất định. Mã sử ​​dụng thư viện đó để thực hiện các chức năng I / O đó sẽ trở nên phụ thuộc vào nhà sản xuất để cung cấp các phiên bản phù hợp của thư viện đó, nhưng sẽ không còn phụ thuộc vào nhà sản xuất sử dụng cùng thực hiện các chức năng đó.

Thật không may, thường rất khó để biết đâu là cách tiếp cận chính xác cho mã chứng minh trong tương lai. Tôi đã thấy các trường hợp một nhà cung cấp chip thay đổi cách thư viện làm việc (để phù hợp với các chip mới), ngay cả khi nó được sử dụng để truy cập vào một con chip đã thay đổi. Tôi cũng đã thấy các trường hợp nhà sản xuất chip thay đổi cách thức hoạt động của phần cứng, nhưng các thư viện được cung cấp đã được điều chỉnh phù hợp, do đó, mã sử dụng thói quen thư viện sẽ tiếp tục hoạt động mà không thay đổi, trong khi mã truy cập phần cứng trực tiếp phải được điều chỉnh.

Tình huống tương tự tồn tại với các ứng dụng Windows. Microsoft đôi khi thích thay đổi cách các ứng dụng được yêu cầu để thực hiện; mã sử dụng một số thư viện nhất định cho những thứ đó có thể được nâng cấp đơn giản bằng cách cập nhật thư viện, trong khi mã không sử dụng các thư viện được cập nhật cho chúng phải được cập nhật thủ công.


1

Tôi muốn giải quyết vấn đề này một chút khác biệt, mặc dù tôi thích Denis de Bernardycâu trả lời và bài viết được liên kết về việc giảm thiểu sự phụ thuộc so với giảm thiểu dư thừa (chúng phản ánh rất nhiều suy nghĩ của tôi về vấn đề này khi tôi tin rằng việc sử dụng lại mã là một hành động cân bằng).

Vấn đề lớn nhất tôi gặp phải với một corethư viện là:

Khi nào nó hoàn thành? Khi nào nó sẽ đạt đến điểm ổn định, nơi nó sẽ làm tất cả những gì nó cần làm và thực sự được "thực hiện"?

Và tôi nghĩ rất có thể câu trả lời có thể là " không bao giờ ". Mọi người có thể luôn bị cám dỗ để thêm vào nó vì nó mô hình một ý tưởng mơ hồ như vậy, đặc biệt nếu thư viện này chỉ phát triển trong quá trình phát triển phần mềm thay vì có các mục tiêu được dự đoán trước. Và có lẽ việc thêm vào thư viện không phải là điều tồi tệ nhất trên thế giới vì nó sẽ không phá vỡ sự phụ thuộc hiện có vào thư viện, nhưng với những mục tiêu mơ hồ như vậy, thư viện có thể ngày càng trở nên chiết trung và xấu xí, cung cấp chức năng khác biệt mà ai đó quan tâm sử dụng thư viện chỉ có thể tìm thấy một phần nhỏ của nó áp dụng cho nhu cầu của họ.

Các phụ thuộc trong cơ sở mã của bạn lý tưởng sẽ chuyển sang các gói rất ổn định. Một coregói có thể dễ dàng thấy nó rất không ổn định trong khi các phần lớn của cơ sở mã của bạn có các phụ thuộc chảy vào nó.

Vì vậy, tôi nghĩ rằng đáng để chia thư viện thành các thư viện thống nhất hơn dành cho việc làm một cái gì đó cụ thể hơn là "thư viện cốt lõi của bất cứ thứ gì mọi người có thể cần" để nó có thể phát triển theo hướng thống nhất hơn với sự phối hợp tốt hơn giữa các đồng đội của bạn về chính xác những gì nó nên và quan trọng hơn là không nên làm và có khả năng đạt đến điểm ổn định khi được kiểm tra tốt và bạn không cảm thấy như có thêm bất cứ điều gì cần phải thêm vào nó để tương đối " hoàn thành "và ổn định (như trong, không thay đổi).


0

Viết thư viện cho những thứ cơ bản như chuỗi và danh sách liên kết là khá ngớ ngẩn trong thiên niên kỷ này. Sử dụng ngôn ngữ lập trình bao gồm pin có chức năng cốt lõi đã có trong đó.

Nếu bạn thích viết thư viện hỗ trợ thời gian chạy cốt lõi chỉ để giải trí, thì hãy thiết kế một ngôn ngữ lập trình mới. Nếu bạn làm điều đó trong một ứng dụng, thì về cơ bản, bạn đang phát triển một ngôn ngữ từ phía đó.

Bên cạnh đó, có ai đó đã viết N thư viện lõi khác nhau bằng ngôn ngữ bạn đang sử dụng chưa? Nghiên cứu các khung hiện có và chọn một khung phù hợp nhất có thể sử dụng thời gian tốt hơn so với thực hiện từ đầu.


Trong lĩnh vực của tôi, xử lý gói hiệu suất cao, chắc chắn sử dụng ngôn ngữ lập trình bao gồm pin không phải là một lựa chọn. C là sự lựa chọn rõ ràng. Và không, N thư viện lõi khác nhau có sẵn cho ví dụ bảng băm tồi tệ hơn so với triển khai nhân Linux. Việc triển khai nhân Linux, là GPL'd, yêu cầu bạn tự thực hiện một triển khai tương tự mà không cần nhìn vào mã nguồn nhân Linux, nhưng khi biết bảng băm nâng cao có tính năng sử dụng nhân Linux. Điều này có thể khác nhau trên các lĩnh vực, tuy nhiên.
juhist
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.