2011-04-11 40 views
19

Gần đây tôi đã cảm thấy thất vọng hơn với một vấn đề mà tôi thấy đang nổi lên trong các dự án mã cơ sở của mình.Theo dõi các lớp học tiện ích

Tôi đang làm việc trên một dự án java quy mô lớn có> 1M dòng mã. Các giao diện và cấu trúc lớp được thiết kế rất tốt và các kỹ sư viết mã rất thành thạo. Vấn đề là trong một nỗ lực để làm cho mã sạch hơn mọi người viết các lớp tiện ích bất cứ khi nào họ cần sử dụng lại một số chức năng, kết quả theo thời gian và khi dự án phát triển ngày càng nhiều phương thức tiện ích. Tuy nhiên, khi kỹ sư tiếp theo bắt gặp nhu cầu về cùng một chức năng, anh ta không có cách nào để biết rằng ai đó đã triển khai một lớp tiện ích (hoặc phương thức) ở đâu đó trong mã và thực hiện một bản sao của chức năng trong một lớp khác. Kết quả là rất nhiều sự sao chép mã và quá nhiều lớp tiện ích với chức năng chồng chéo.

Có bất kỳ công cụ hay nguyên tắc thiết kế nào mà chúng tôi là một nhóm có thể thực hiện nhằm ngăn chặn sự trùng lặp và mức độ hiển thị thấp của các lớp tiện ích không?

Ví dụ: kỹ sư A có 3 nơi ông cần phải chuyển đổi XML để String nên ông viết một lớp tiện ích gọi là XMLUtil và đặt một toString(Document) phương pháp tĩnh trong nó. Kỹ sư B có nhiều nơi anh ta sắp xếp các tài liệu thành nhiều định dạng khác nhau bao gồm String, vì vậy anh ta viết một lớp tiện ích gọi là SerializationUtil và có phương thức tĩnh gọi là serialize(Document) trả về một chuỗi. Lưu ý rằng đây không chỉ là việc sao chép mã vì nó có thể thực hiện 2 ví dụ trên là khác nhau (ví dụ: sử dụng API biến áp và các ứng dụng khác Xerces2-J) để có thể xem như là một "thực hành tốt nhất" vấn đề cũng ...

cập nhật:. tôi đoán tôi mô tả rõ hơn về môi trường hiện tại chúng tôi phát triển trong chúng tôi sử dụng Hudson cho CI, Clover cho mã số bảo hiểm và Checkstyle để phân tích mã tĩnh. Chúng tôi sử dụng phát triển nhanh bao gồm các cuộc đàm phán hàng ngày và (có lẽ không đủ) đánh giá mã. Chúng tôi xác định tất cả các lớp tiện ích của chúng tôi trong một .util do kích thước của nó bây giờ có 13 gói con và khoảng 60 lớp trong lớp gốc (.util). Chúng tôi cũng sử dụng thư viện của bên thứ ba như hầu hết các lọ thuốc phiện apache và một số lọ tạo nên ổi. Tôi rất tích cực khi chúng tôi có thể giảm số lượng tiện ích xuống một nửa nếu chúng tôi đặt ai đó vào nhiệm vụ tái cấu trúc toàn bộ gói, tôi đã tự hỏi liệu có bất kỳ công cụ nào có thể làm cho hoạt động đó ít tốn kém hơn không, và nếu có là bất kỳ phương pháp nào có thể trì hoãn càng nhiều càng tốt vấn đề từ định kỳ.

+0

Xem http://stackoverflow.com/questions/4188919/how-to-search-for-java-api-methods-by-type-signature – meriton

+0

Tôi tưởng tượng bạn cũng có thể điều chỉnh giải pháp đó để liệt kê tất cả các phương pháp có trùng lặp loại chữ ký. – meriton

Trả lời

5

Vấn đề của bạn rất phổ biến. Và một vấn đề thực sự nữa, bởi vì không có giải pháp tốt.

Chúng tôi đang ở trong tình trạng tương tự ở đây, tôi cũng nói tồi tệ hơn, với 13 triệu dòng mã, doanh thu và hơn 800 nhà phát triển làm việc trên mã. Chúng ta thường thảo luận về cùng một vấn đề mà bạn mô tả.

Ý tưởng đầu tiên - các nhà phát triển của bạn đã sử dụng - là để cấu trúc lại mã chung trong một số lớp tiện ích. Vấn đề của chúng tôi với giải pháp đó, ngay cả với lập trình cặp, tư vấn và thảo luận, là chúng tôi chỉ đơn giản là quá nhiều để điều này có hiệu quả. Trong thực tế, chúng tôi phát triển trong các đường phụ, với những người chia sẻ kiến ​​thức trong subteam của họ, nhưng kiến ​​thức không chuyển tiếp giữa các subteams. Có lẽ chúng tôi đã sai nhưng tôi nghĩ rằng ngay cả lập trình cặp và các cuộc đàm phán cũng không thể giúp ích trong trường hợp này.

Chúng tôi cũng có một nhóm kiến ​​trúc. Nhóm này chịu trách nhiệm đối phó với các mối quan tâm về thiết kế và kiến ​​trúc và để tạo ra các tiện ích chung mà chúng tôi có thể cần. Nhóm này trong thực tế sản xuất một cái gì đó chúng ta có thể gọi là một khuôn khổ của công ty. Vâng, đó là một khuôn khổ, và đôi khi nó hoạt động tốt. Nhóm này cũng chịu trách nhiệm thúc đẩy thực hành tốt nhất và nâng cao nhận thức về những gì nên được thực hiện hay không, những gì có sẵn hoặc những gì không có.

Thiết kế Java API cốt lõi tốt là một trong những lý do cho sự thành công của Java. Thư viện nguồn mở của bên thứ ba tốt cũng tính rất nhiều. Ngay cả một API nhỏ được chế tạo tốt cũng cho phép cung cấp một sự trừu tượng thực sự hữu ích và có thể giúp giảm kích thước mã rất nhiều. Nhưng bạn biết đấy, việc tạo khung công tác và API công khai không phải là điều tương tự chút nào khi chỉ viết mã một lớp tiện ích trong 2 giờ. Nó có chi phí thực sự cao. Một lớp tiện ích mất 2 giờ để mã hóa ban đầu, có thể là 2 ngày với việc gỡ lỗi và kiểm thử đơn vị. Khi bạn bắt đầu chia sẻ mã chung trên các dự án/nhóm lớn, bạn thực sự tạo một API. Bạn phải đảm bảo tài liệu hoàn hảo sau đó, mã thực sự dễ đọc và có thể bảo trì. Khi bạn phát hành phiên bản mới của mã này, bạn phải tương thích ngược. Bạn phải quảng cáo cho công ty rộng (hoặc ít nhất là đội ngũ rộng). Từ 2 ngày đối với lớp tiện ích nhỏ của bạn, bạn phát triển đến 10 ngày, 20 ngày hoặc thậm chí 50 ngày đối với API chính thức.

Và thiết kế API của bạn có thể không tuyệt vời như vậy. Vâng, nó không phải là kỹ sư của bạn không sáng - thực sự họ đang có. Nhưng bạn có sẵn sàng để họ làm việc 50 ngày trên một lớp tiện ích nhỏ chỉ giúp phân tích cú pháp số theo cách nhất quán cho giao diện người dùng không? Bạn có sẵn sàng để họ thiết kế lại toàn bộ điều khi bạn bắt đầu sử dụng giao diện người dùng trên thiết bị di động với các nhu cầu hoàn toàn khác nhau không? Ngoài ra, bạn có nhận thấy cách các kỹ sư sáng nhất trong từ tạo API sẽ không bao giờ phổ biến hoặc sẽ mờ dần không? Bạn thấy đấy, dự án web đầu tiên chúng tôi thực hiện chỉ sử dụng khung nội bộ hoặc không có khung công tác nào cả. Sau đó chúng tôi đã thêm PHP/JSP/ASP. Sau đó, trong Java, chúng tôi đã thêm Struts. Bây giờ JSF là tiêu chuẩn. Và chúng tôi đang nghĩ đến việc sử dụng Spring Web Flow, Vaadin hoặc Lift ...

Tất cả những gì tôi muốn nói là không có giải pháp tốt, chi phí phát triển theo cấp số nhân với kích thước mã và kích thước nhóm. Chia sẻ một codebase lớn hạn chế sự nhanh nhẹn và sự đáp ứng của bạn.Bất kỳ thay đổi nào cũng phải được thực hiện cẩn thận, bạn phải nghĩ đến tất cả các vấn đề hội nhập tiềm năng và mọi người phải được đào tạo về các tính năng và đặc điểm mới.

Nhưng điểm năng suất chính trong công ty phần mềm không phải là để đạt được 10 hoặc thậm chí 50 dòng mã khi phân tích cú pháp XML. Một mã chung để thực hiện điều này sẽ phát triển tới hàng nghìn dòng mã và tái tạo một API phức tạp sẽ được phân lớp theo các lớp tiện ích. Khi anh ta tạo một lớp tiện ích để phân tích cú pháp XML, nó là một sự trừu tượng tốt. Anh ta đặt tên cho một tá hoặc thậm chí một trăm dòng mã chuyên biệt. Mã này rất hữu ích vì nó là chuyên ngành. API phổ biến cho phép hoạt động trên luồng, URL, chuỗi, bất kỳ thứ gì. Nó có một nhà máy để bạn có thể chọn bạn thực hiện phân tích cú pháp. Lớp tiện ích rất tốt vì nó chỉ làm việc với trình phân tích cú pháp này và với các chuỗi. Và bởi vì bạn cần một dòng mã để gọi nó. Nhưng tất nhiên, mã tiện ích này bị hạn chế sử dụng. Nó hoạt động tốt cho ứng dụng di động này hoặc để tải cấu hình XML. Và đó là lý do tại sao nhà phát triển đã thêm lớp tiện ích cho nó ngay từ đầu.

Tóm lại, những gì tôi sẽ xem xét thay vì cố gắng để củng cố các mã cho toàn bộ codebase là để chia trách nhiệm mã như các đội bóng lớn:

  • chuyển đội bóng lớn của bạn mà làm việc trên một dự án lớn thành nhỏ các nhóm hoạt động trên một số tiểu dự án;
  • đảm bảo rằng giao tiếp là tốt để giảm thiểu các vấn đề tích hợp, nhưng hãy để nhóm có mã riêng của họ;
  • nhóm bên trong đề tài và các mã tương ứng, đảm bảo bạn có các phương pháp hay nhất. Không có mã trùng lặp, trừu tượng tốt. Sử dụng các API đã được chứng minh từ cộng đồng. Sử dụng lập trình ghép đôi, tài liệu API mạnh mẽ, wiki ... Nhưng bạn nên thực sự cho phép các nhóm khác nhau lựa chọn, xây dựng mã riêng của họ, ngay cả khi điều này có nghĩa là mã trùng lặp giữa các nhóm hoặc các quyết định thiết kế khác nhau. Bạn biết đấy, nếu các quyết định thiết kế khác nhau, điều này có thể là do nhu cầu khác nhau.

Điều bạn đang thực sự quản lý là phức tạp. Cuối cùng, nếu bạn tạo một codebase nguyên khối, bạn có thể tăng thời gian cho những người mới đến tăng lên, bạn tăng nguy cơ các nhà phát triển sẽ không sử dụng mã chung của bạn, và bạn làm chậm mọi người vì bất kỳ thay đổi nào có cơ hội lớn hơn để phá vỡ chức năng hiện có.

+0

cảm ơn câu trả lời của bạn, tôi nghĩ rằng bạn là đúng. Bước hợp lý tiếp theo đối với chúng tôi là chia mã của chúng tôi thành các tiểu dự án và xác định các giao diện trong đó mã được chia sẻ là tối thiểu. Chúng tôi đã nói về nó một lúc và tôi cảm thấy rằng nó sẽ cung cấp sự thỏa hiệp mà chúng tôi tìm kiếm. Vì tôi không chắc bất kỳ công cụ nào tồn tại có thể giúp tôi giải quyết vấn đề này, tôi sẽ trao tiền thưởng cho bạn, vì bạn có lẽ gần nhất để xác định cách giảm thiểu sự cố – Asaf

+0

50 ngày chia cho 800 người dùng tiềm năng không nhiều trên không –

+0

Không ai muốn trả 50 ngày cho một thứ có thể được thực hiện trong 2 ngày. Và nếu bạn phải làm điều đó bởi vì bạn có một đội ngũ lớn, thì nhóm lớn này thực tế đang giảm năng suất. –

4

Có một số thực hành XP/nhanh nhẹn bạn có thể sử dụng để giải quyết vấn đề này, ví dụ:

  • nói chuyện với nhau (ví dụ như trong cuộc họp stand-up hàng ngày)
  • cặp lập trình/code xem xét

Sau đó tạo, tài liệu & kiểm tra một hoặc nhiều dự án thư viện tiện ích có thể được tham chiếu. Tôi khuyên bạn nên sử dụng Maven để quản lý các gói phụ thuộc.

+0

chúng tôi đang dần dần di chuyển về phía giải pháp này bằng cách mở rộng ngày càng nhiều tiện ích vào các lọ khác nhau (kinda như apache commons).Tuy nhiên, vẫn còn vấn đề sau đó đặt hàng utlities trong những lọ (những gì thuộc nơi và nơi tôi nên nhìn) Tôi biết giao tiếp là bắt buộc nhưng những người khác nhau làm việc trên các phần khác nhau của mã và không có một số cách để một kỹ sư để truy cập dữ liệu anh ta bị ràng buộc để bỏ lỡ một cái gì đó – Asaf

+0

Có lẽ tôi là sai, nhưng một cơ sở mã với 1 triệu dòng mã là quá lớn, do đó bạn không thể hy vọng quản lý mã trùng lặp chỉ với nói chuyện và lập trình cặp. –

3

Bạn có thể xem xét đề xuất rằng tất cả các lớp tiện ích được đặt trong cấu trúc gói được tổ chức tốt như com.yourcompany.util.. Nếu mọi người sẵn sàng đặt tên cho các gói con và lớp học tốt, thì ít nhất là nếu họ cần tìm một tiện ích, họ biết nơi cần tìm.Tôi không nghĩ rằng có bất kỳ câu trả lời viên đạn bạc ở đây mặc dù. Giao tiếp là quan trọng. Có thể nếu một nhà phát triển gửi một email đơn giản đến các nhân viên phát triển còn lại khi họ viết một tiện ích mới, điều đó sẽ đủ để có được nó trên radar của mọi người. Hoặc một trang wiki được chia sẻ nơi mọi người có thể liệt kê/ghi lại chúng.

+0

chúng tôi sử dụng cấu trúc gói .util, tuy nhiên khi số tiện ích phát triển nó trở thành một vấn đề tìm kiếm công cụ phù hợp cho công việc mà lại kết thúc với nhiều lớp hơn. một Wiki về tất cả các utils có sẵn có thể là một lựa chọn nhưng sau đó tìm kiếm thông tin trong wiki là (gần) cùng một vấn đề khi tìm kiếm thông tin trong Javadoc. – Asaf

1
  1. đội thông tin liên lạc (hét to: "hey không ai có một toString Document?")
  2. Giữ lớp tiện ích để một mức tối thiểu và hạn chế chúng vào một namespace đơn
  3. Luôn nghĩ: làm thế nào tôi có thể làm điều này với một vật thể. Trong ví dụ của bạn, tôi sẽ mở rộng lớp Document và thêm các phương thức toStringserialize vào nó.
+0

Tôi tin rằng chúng tôi đang nỗ lực rất tốt trong việc giữ chức năng chung trong các lớp cơ sở, tuy nhiên, thường thì thời gian mở rộng không phải là một tùy chọn, 'org.w3c.Document' là một ví dụ điển hình. Có lẽ chúng ta có thể làm tốt hơn trong việc chuyển nhiều logic hơn vào các lớp cơ sở, nhưng làm thế nào tôi có thể tìm thấy tất cả các phương thức tiện ích hiện có có thể được chuyển vào các lớp cơ sở? – Asaf

+0

@well, một cách sẽ là để grep xung quanh cho "công cộng tĩnh" hoặc một số mô hình như vậy trong các thư mục có liên quan. Và vâng, tôi cảm thấy bạn trên các lớp cơ sở "kín". Trong những trường hợp đó, bạn luôn có thể sử dụng patter trang trí để bọc tài liệu vào phiên bản của riêng bạn. – Milimetric

0

Sự cố này được trợ giúp khi kết hợp các tính năng "hoàn thành mã" IDE với các ngôn ngữ hỗ trợ tiện ích mở rộng loại (ví dụ: C# và F #). Do đó, tưởng tượng Java có một tính năng như vậy, một lập trình viên có thể khám phá tất cả các phương thức mở rộng trên một lớp dễ dàng trong IDE như:

Document doc = ... 
doc.to //list pops up with toXmlString, toJsonString, all the "to" series extension methods 

Tất nhiên, Java không có phần mở rộng kiểu. Nhưng bạn có thể sử dụng grep để tìm kiếm dự án của bạn cho "tất cả các phương thức công cộng tĩnh lấy SomeClass làm đối số đầu tiên" để có được cái nhìn tương tự về những phương thức tiện ích nào đã được viết cho một lớp nhất định.

+0

Bạn có biết bất kỳ công cụ hoặc plugin eclipse nào có thể thực hiện điều này một cách dễ dàng không? yêu cầu tất cả các lập trình viên để grep gói util của họ trước khi thêm bất kỳ phương pháp tĩnh có vẻ như một nỗi đau và tôi sợ yêu cầu rất có thể sẽ bị bỏ qua vì nó quá phức tạp tự nhiên. – Asaf

+1

Xin lỗi @Asaf, tôi không biết về một công cụ sẵn sàng như vậy. Nhưng tôi nghĩ rằng đây có thể là một dự án nhỏ vui vẻ. Tôi không sử dụng grep nhiều, nhưng tôi tưởng tượng các mô hình là hợp lý đơn giản và dễ dàng tái sử dụng cho bất kỳ lớp học. Một khi bạn nhận được rằng xuống, bạn có thể tạo một kịch bản mà có một tên lớp như là đầu vào duy nhất và chạy lệnh. Điều đó sẽ làm cho công việc được thực hiện với rất ít gánh nặng cho tất cả các lập trình viên, tôi nghĩ vậy. Nhưng yeah, nó sẽ là tuyệt vời để có một plugin Eclipse, nơi bạn nhấp chuột phải vào một tên lớp và chọn ("tìm tất cả các phương pháp tiện ích") ... đó có thể là một dự án thú vị quá! –

+0

upvoting bình luận của bạn cho tôi một ý tưởng cho nước rút "miễn phí" tiếp theo của tôi – Asaf

0

Thật khó để xây dựng một công cụ nhận ra "cùng một chức năng". (Về lý thuyết điều này là trong thực tế không thể, và nơi bạn có thể làm điều đó trong thực tế bạn có thể cần một định lý prover).

Nhưng những gì thường xảy ra là mọi người sao chép các cụm gần với những gì họ muốn, và sau đó tùy chỉnh nó. Đó là loại mã bạn có thể tìm thấy, bằng cách sử dụng trình phát hiện nhân bản.

CloneDR của chúng tôi là công cụ để phát hiện mã nhân bản chính xác và gần được bỏ lỡ dựa trên việc sử dụng các cây cú pháp được tham số hóa. Nó khớp với các phiên bản được phân tích cú pháp của mã, do đó, nó không bị nhầm lẫn bởi bố cục, các chú thích đã thay đổi, các tên biến được sửa đổi, hoặc trong nhiều trường hợp, các câu lệnh chèn vào hoặc bị xóa. Có nhiều phiên bản cho nhiều ngôn ngữ (C++, COBOL, C#, Java, JavaScript, PHP, ...) và bạn có thể xem ví dụ về phát hiện nhân bản chạy tại liên kết được cung cấp. Nó thường tìm thấy 10-20% mã trùng lặp và nếu bạn trừu tượng mã đó thành các phương thức thư viện trên cơ sở tôn giáo, cơ sở mã của bạn thực sự có thể thu nhỏ (đã xảy ra với một tổ chức sử dụng CloneDR).

0

Bạn đang tìm kiếm một giải pháp mà bạn có thể giúp bạn quản lý vấn đề không thể tránh khỏi này, sau đó tôi có thể đề nghị một công cụ:

  • TeamCity: một cách dễ dàng tuyệt vời để sử dụng sản phẩm để quản lý tất cả các tòa nhà đang tự động của mình từ bạn lưu trữ và chạy thử nghiệm đơn vị, vv
    Nó thậm chí là một sản phẩm miễn phí cho hầu hết mọi người.
    Phần còn tốt hơn: nó đã được xây dựng trong code duplicate detection across all your code.

More công cụ để đọc lên:

0
  1. một dự án ứng dụng tiện ích tiêu chuẩn.xây dựng một cái lọ với phạm vi mở rộng bị giới hạn và gói dựa trên chức năng.
  2. sử dụng các tiện ích phổ biến như apache-commons hoặc bộ sưu tập google và cung cấp một sự trừu tượng
  3. duy trì tri thức cơ bản và tài liệu hướng dẫn và JIRA theo dõi cho các lỗi và cải tiến
  4. refactoring tiến hóa
  5. FindBugs và PMD cho việc tìm kiếm việc lặp lại code hoặc lỗi
  6. xem xét và kiểm tra các công cụ tiện ích để thực hiện
  7. sử dụng nghiệp! yêu cầu các thành viên trong nhóm đóng góp vào cơ sở mã, bất cứ khi nào họ tìm thấy một trong mã rừng hiện có hoặc yêu cầu mã mới.
9

Giải pháp tốt cho vấn đề này là bắt đầu thêm nhiều hướng đối tượng hơn. Để sử dụng ví dụ của bạn:

Ví dụ: kỹ sư A có 3 nơi ông cần phải chuyển đổi XML để String nên ông viết một lớp tiện ích gọi là XMLUtil và đặt một phương thức toString tĩnh (Document) trong đó

Giải pháp là ngừng sử dụng các kiểu nguyên thủy hoặc các kiểu do JVM cung cấp (String, Integer, java.util.Date, java.w3c.Document) và bọc chúng trong các lớp riêng của dự án của bạn. Sau đó, lớp XmlDocument của bạn có thể cung cấp một phương thức toString thuận tiện và các phương thức tiện ích khác. ProjectFooDate của riêng bạn có thể chứa các phương pháp phân tích cú pháp và định dạng nếu không sẽ kết thúc trong các lớp DateUtils khác nhau, v.v.

Bằng cách này, IDE sẽ nhắc bạn với phương pháp tiện ích bất cứ khi nào bạn cố gắng làm điều gì đó với đối tượng.

+0

Đúng, chắc chắn di chuyển theo hướng nhiều hơn một mô hình điều khiển miền/kiến ​​trúc lục giác –

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