Có phải khuôn mẫu siêu dữ liệu của Java trong Java là một ý tưởng tốt?


29

Có một tệp nguồn trong một dự án khá lớn với một số chức năng cực kỳ nhạy cảm với hiệu năng (được gọi là hàng triệu lần mỗi giây). Trong thực tế, người bảo trì trước đó đã quyết định viết 12 bản sao của một chức năng, mỗi bản khác nhau rất ít, để tiết kiệm thời gian dành cho việc kiểm tra các điều kiện trong một chức năng.

Thật không may, điều này có nghĩa là mã là một PITA để duy trì. Tôi muốn xóa tất cả các mã trùng lặp và chỉ viết một mẫu. Tuy nhiên, ngôn ngữ, Java, không hỗ trợ các mẫu và tôi không chắc rằng thuốc generic có phù hợp với điều này không.

Kế hoạch hiện tại của tôi là viết thay vì một tệp tạo ra 12 bản sao của hàm (một thiết bị mở rộng mẫu chỉ sử dụng một lần, thực tế). Tất nhiên tôi sẽ cung cấp giải thích đa dạng cho lý do tại sao tệp phải được tạo theo chương trình.

Mối quan tâm của tôi là điều này sẽ dẫn đến sự nhầm lẫn của các nhà bảo trì trong tương lai và có thể đưa ra các lỗi khó chịu nếu họ quên tạo lại tệp sau khi sửa đổi hoặc (thậm chí tệ hơn) nếu họ sửa đổi thay vì tệp được tạo theo chương trình. Thật không may, viết lại toàn bộ điều trong C ++, tôi thấy không có cách nào để khắc phục điều này.

Do lợi ích của phương pháp này lớn hơn những nhược điểm? Tôi nên thay thế:

  • Thực hiện cú đánh hiệu suất và sử dụng một chức năng duy nhất, có thể duy trì.
  • Thêm giải thích cho lý do tại sao chức năng phải được nhân đôi 12 lần và ân cần bảo trì.
  • Cố gắng sử dụng thuốc generic làm mẫu (có thể chúng không hoạt động theo cách đó).
  • Hét vào bộ duy trì cũ để tạo mã sao cho hiệu năng phụ thuộc vào một chức năng duy nhất.
  • Phương pháp khác để duy trì hiệu suất và bảo trì?

PS Do thiết kế kém của dự án, cấu hình chức năng khá khó khăn ... tuy nhiên, người bảo trì trước đây đã thuyết phục tôi rằng hiệu năng đạt được là không thể chấp nhận được. Tôi cho rằng bằng cách này, anh ta có nghĩa là hơn 5%, mặc dù đó là một phỏng đoán hoàn toàn về phía tôi.


Có lẽ tôi nên giải thích một chút. 12 bản sao thực hiện một nhiệm vụ rất giống nhau, nhưng có sự khác biệt nhỏ. Sự khác biệt là ở những nơi khác nhau trong toàn bộ chức năng, vì vậy thật không may, có rất nhiều, rất nhiều, các tuyên bố có điều kiện. Có hiệu quả 6 "chế độ" hoạt động và 2 "mô hình" hoạt động (từ được tạo ra bởi chính tôi). Để sử dụng chức năng, người ta chỉ định "chế độ" và "mô hình" hoạt động. Điều này không bao giờ năng động; mỗi đoạn mã sử dụng chính xác một chế độ và mô hình. Tất cả 12 cặp mô hình chế độ được sử dụng ở đâu đó trong ứng dụng. Các hàm được đặt tên khéo léo là func1 đến func12, với các số chẵn đại diện cho mô hình thứ hai và các số lẻ đại diện cho mô hình thứ nhất.

Tôi biết rằng đây chỉ là về thiết kế tồi tệ nhất nếu tính bảo trì là mục tiêu. Nhưng nó dường như "đủ nhanh" và mã này không cần bất kỳ thay đổi nào trong một thời gian ... Điều đáng chú ý là chức năng ban đầu chưa bị xóa (mặc dù đó là mã chết theo như tôi có thể nói) , vì vậy tái cấu trúc sẽ đơn giản.


Nếu hiệu năng đạt được đủ nghiêm trọng để đảm bảo 12 phiên bản của chức năng, thì bạn có thể bị mắc kẹt với nó. Nếu bạn cấu trúc lại một chức năng duy nhất (hoặc sử dụng thuốc generic), liệu hiệu suất đạt được có đủ tệ đến mức bạn sẽ mất khách hàng và doanh nghiệp không?
Thất

12
Sau đó, tôi nghĩ rằng bạn cần phải thực hiện các bài kiểm tra của riêng mình (tôi biết bạn nói hồ sơ rất khó, nhưng bạn vẫn có thể thực hiện các bài kiểm tra hiệu năng "hộp đen" thô sơ của hệ thống, phải không?) Để thấy sự khác biệt lớn như thế nào Là. Và nếu nó đáng chú ý, thì tôi nghĩ bạn có thể bị mắc kẹt với trình tạo mã.
Thất

1
Điều này nghe có vẻ như có thể đã được hưởng lợi từ một số đa hình tham số (hoặc thậm chí có thể là tổng quát), thay vì gọi mỗi chức năng bằng một tên khác nhau.
Robert Harvey

2
Đặt tên các hàm func1 ... func12 có vẻ điên rồ. Ít nhất hãy đặt tên cho chúng là mode1Par2, v.v ... hoặc có thể là myFuncM3P2.
dùng949300

2
"Nếu họ sửa đổi thay vì tệp được tạo theo chương trình" ... chỉ tạo tệp khi xây dựng từ " Makefile" (hoặc bất kỳ hệ thống nào bạn sử dụng) và xóa nó ngay sau khi biên dịch xong . Theo cách này, đơn giản là họ không có cơ hội sửa đổi tệp nguồn sai.
Bakuriu

Câu trả lời:


23

Đây là một tình huống rất tồi tệ, bạn cần phải cấu trúc lại ASAP này - đây là khoản nợ kỹ thuật tồi tệ nhất - bạn thậm chí không biết mã thực sự quan trọng như thế nào - chỉ suy đoán rằng nó quan trọng.

Đối với các giải pháp càng sớm càng tốt:
Một vài thứ có thể được thực hiện là thêm một bước biên dịch tùy chỉnh. Nếu bạn sử dụng Maven thực sự khá đơn giản để thực hiện, các hệ thống xây dựng tự động khác cũng có khả năng đối phó với điều này. Viết một tệp có phần mở rộng khác với .java và thêm một bước tùy chỉnh để tìm kiếm nguồn của bạn cho các tệp như thế và tạo lại .java thực tế. Bạn cũng có thể muốn thêm từ chối trách nhiệm lớn vào tệp được tạo tự động giải thích không sửa đổi nó.

Ưu điểm so với sử dụng tệp được tạo một lần: Các nhà phát triển của bạn sẽ không nhận được các thay đổi của họ đối với .java hoạt động. Nếu họ thực sự chạy mã trên máy của họ trước khi cam kết họ sẽ thấy rằng những thay đổi của họ không có hiệu lực (hah). Và sau đó có thể họ sẽ đọc từ chối trách nhiệm. Bạn hoàn toàn đúng khi không tin tưởng đồng đội và bản thân tương lai của bạn khi nhớ rằng tập tin cụ thể này phải được thay đổi theo một cách khác. Nó cũng cho phép kiểm tra tự động dưới dạng chào mừng, vì JUnit sẽ biên dịch chương trình của bạn trước khi chạy thử nghiệm (và cũng tạo lại tệp)

CHỈNH SỬA

Đánh giá bằng các ý kiến, câu trả lời được đưa ra như thể đây là một cách để làm cho công việc này vô thời hạn và có thể OK để triển khai đến các phần quan trọng khác trong dự án của bạn.

Nói một cách đơn giản: không phải vậy.

Gánh nặng thêm của việc tạo ra ngôn ngữ nhỏ của riêng bạn, viết một trình tạo mã cho nó và duy trì nó, chưa kể đến việc dạy nó cho các nhà bảo trì trong tương lai là điều tồi tệ về lâu dài. Ở trên chỉ cho phép một cách an toàn hơn để xử lý vấn đề trong khi bạn đang làm việc trên một giải pháp lâu dài . Những gì sẽ mất là ở trên tôi.


19
Xin lỗi, nhưng tôi phải không đồng ý. Bạn không biết đủ về mã của OP để đưa ra quyết định này cho anh ấy. Đối với tôi, việc tạo mã này có vẻ như chứa nhiều nợ kỹ thuật hơn so với giải pháp ban đầu.
Robert Harvey

6
Nhận xét của Robert không thể được phóng đại. Bất cứ khi nào bạn có "từ chối giải thích không sửa đổi tệp" là "khoản nợ kỹ thuật" tương đương với một cửa hàng thanh toán bằng séc được điều hành bởi một nhà cái bất hợp pháp có biệt danh "Cá mập".
corsiKa

8
Tất nhiên, tệp được tạo tự động không thuộc về kho lưu trữ nguồn (rốt cuộc nó không phải là nguồn được biên dịch) và nó sẽ bị xóa bởi ant cleanhoặc bất cứ thứ gì tương đương của bạn. Không cần phải từ chối trách nhiệm trong một tập tin thậm chí không có ở đó!
Jörg W Mittag

2
@RobertHarvey Tôi không đưa ra quyết định nào cho OP. OP hỏi liệu có nên có những mẫu như vậy theo cách anh ấy có chúng không và tôi đã đề xuất một cách (tốt hơn) để duy trì chúng. Đó không phải là một giải pháp lý tưởng và trên thực tế, như tôi đã nói trong câu đầu tiên, toàn bộ tình huống là xấu, và cách tốt hơn nhiều để đi tiếp là loại bỏ vấn đề, không đưa ra giải pháp run rẩy cho nó. Điều đó bao gồm việc đánh giá đúng mức độ quan trọng của mã này, mức độ ảnh hưởng của hiệu suất và liệu có cách nào để làm cho nó hoạt động mà không cần viết nhiều mã xấu.
Ngày

2
@corsiKa Tôi chưa bao giờ nói đây là giải pháp lâu dài. Đây sẽ là một khoản nợ nhiều như tình hình ban đầu. Điều duy nhất nó làm là giảm độ biến động cho đến khi tìm thấy một giải pháp có hệ thống. Điều này có thể sẽ bao gồm việc chuyển hoàn toàn sang một nền tảng / khung / ngôn ngữ mới vì nếu bạn gặp phải các vấn đề như thế này trong Java - bạn đang làm điều gì đó cực kỳ phức tạp hoặc cực kỳ sai.
Thường

16

Là bảo trì đạt thực, hoặc nó chỉ làm phiền bạn? Nếu nó chỉ làm phiền bạn, hãy để nó một mình.

Là vấn đề hiệu suất thực sự, hoặc nhà phát triển trước đó chỉ nghĩ rằng nó là? Các vấn đề về hiệu năng, thường xuyên hơn không, không phải là nơi chúng được cho là, ngay cả khi một trình lược tả được sử dụng. (Tôi sử dụng kỹ thuật này để tìm thấy chúng một cách đáng tin cậy.)

Vì vậy, có khả năng bạn có một giải pháp xấu cho một vấn đề không, nhưng nó cũng có thể là một giải pháp xấu cho một vấn đề thực sự. Khi nghi ngờ, hãy để nó một mình.


10

Thực sự chắc chắn hơn trong điều kiện sản xuất, thực tế (tiêu thụ bộ nhớ thực tế, kích hoạt Bộ sưu tập rác thực tế, v.v.), tất cả các phương pháp riêng biệt đó thực sự tạo ra sự khác biệt về hiệu suất (trái ngược với chỉ có một phương pháp). Điều này sẽ mất một vài ngày trong thời gian của bạn, nhưng có thể giúp bạn tiết kiệm hàng tuần bằng cách đơn giản hóa mã bạn làm việc kể từ bây giờ.

Ngoài ra, nếu bạn làm phát hiện ra rằng bạn cần tất cả các chức năng, bạn có thể muốn sử dụng Javassist để tạo mã chương trình khác. Như những người khác đã chỉ ra, bạn có thể tự động hóa nó với Maven .


2
Ngoài ra, ngay cả khi hóa ra các vấn đề về hiệu năng là có thật, việc thực hiện nghiên cứu chúng sẽ giúp bạn biết rõ hơn những gì có thể với mã của bạn.
Carl Manaster

6

Tại sao không bao gồm một số loại tiền xử lý mẫu \ tạo mã cho hệ thống này? Bước xây dựng bổ sung tùy chỉnh thực thi và phát ra các tệp nguồn java bổ sung trước khi biên dịch phần còn lại của mã. Đây là cách bao gồm các máy khách web tắt các tệp wsdl và xsd thường hoạt động.

Tất nhiên bạn sẽ phải bảo trì bộ tạo mã tiền xử lý đó, nhưng bạn sẽ không phải lo lắng về việc bảo trì mã lõi trùng lặp.

Vì thời gian biên dịch mã Java đang được phát ra, không có hình phạt hiệu năng nào được trả cho mã bổ sung. Nhưng bạn có được sự đơn giản hóa bảo trì bằng cách có mẫu thay vì tất cả các mã trùng lặp.

Các khái quát về Java không mang lại lợi ích về hiệu năng do xóa kiểu trong ngôn ngữ, việc tạo khuôn đơn giản được sử dụng ở vị trí của nó.

Theo hiểu biết của tôi về các mẫu C ++, chúng được biên dịch thành nhiều hàm cho mỗi lần gọi mẫu. Bạn sẽ kết thúc với mã thời gian giữa biên dịch trùng lặp cho mỗi loại bạn lưu trữ trong một std :: vector chẳng hạn.


2

Không có phương thức Java lành mạnh nào có thể đủ dài để có 12 biến thể ... và JITC ghét các phương thức dài - đơn giản là nó từ chối tối ưu hóa chúng đúng cách. Tôi đã thấy hệ số tăng tốc của hai bằng cách chia một phương thức thành hai phương thức ngắn hơn. Có lẽ đây là con đường để đi.

OTOH có nhiều bản sao có thể có ý nghĩa ngay cả khi có giống hệt nhau. Khi mỗi trong số chúng được sử dụng ở những nơi khác nhau, chúng được tối ưu hóa cho các trường hợp khác nhau (JITC cấu hình chúng và sau đó đặt các trường hợp hiếm gặp trên một con đường đặc biệt.

Tôi muốn nói rằng việc tạo mã không phải là vấn đề lớn, giả sử có một lý do chính đáng. Chi phí khá thấp và các tệp được đặt tên đúng sẽ dẫn ngay đến nguồn của chúng. Cách đây rất lâu khi tôi tạo mã nguồn, tôi đặt // DO NOT EDITtrên mỗi dòng ... Tôi đoán đó là tiết kiệm đủ.


1

Hoàn toàn không có gì sai với "vấn đề" bạn đã đề cập. Từ những gì tôi biết đây là loại thiết kế và cách tiếp cận chính xác được sử dụng bởi máy chủ DB để có hiệu suất tốt.

Họ có rất nhiều phương pháp đặc biệt để đảm bảo rằng họ có thể tối đa hóa hiệu suất cho tất cả các loại hoạt động: tham gia, chọn, tổng hợp, v.v ... khi áp dụng một số điều kiện nhất định.

Nói tóm lại, việc tạo mã như cái mà bạn nghĩ là một ý tưởng tồi. Có lẽ bạn nên xem sơ đồ này để xem cách DB giải quyết vấn đề tương tự như của bạn:nhập mô tả hình ảnh ở đây


1

Bạn có thể muốn kiểm tra xem bạn vẫn có thể sử dụng một số trừu tượng hợp lý hay không và sử dụng ví dụ mẫu phương thức mẫu để viết mã dễ hiểu cho chức năng chung và chuyển sự khác biệt giữa các phương thức thành "hoạt động nguyên thủy" (theo mô hình khử) trong 12 các lớp con. Điều này có thể sẽ cải thiện khả năng bảo trì và khả năng kiểm tra và nó thực sự có thể có hiệu năng tương tự như mã hiện tại, vì JVM có thể nội tuyến gọi phương thức cho các hoạt động nguyên thủy sau một thời gian. Tất nhiên, bạn phải kiểm tra điều này trong một bài kiểm tra hiệu suất.


0

Bạn có thể giải quyết vấn đề của các phương pháp chuyên dụng bằng cách sử dụng ngôn ngữ Scala.

Scala có thể phương thức nội tuyến, điều này (kết hợp với việc sử dụng hàm bậc cao dễ dàng hơn) giúp tránh việc sao chép mã miễn phí - đây có vẻ như là vấn đề chính được đề cập trong câu trả lời của bạn.

Nhưng ngoài ra, Scala có các macro cú pháp, điều này cho phép thực hiện rất nhiều thứ với mã tại thời gian biên dịch theo cách an toàn kiểu.

Và vấn đề chung của các kiểu nguyên thủy đấm bốc khi được sử dụng trong tướng cũng có thể giải quyết trong Scala: nó có thể chuyên môn hóa chung cho người nguyên thủy để tránh quyền anh tự động bằng cách sử dụng @specializedchú thích - điều này được tích hợp vào ngôn ngữ. Vì vậy, về cơ bản, bạn sẽ viết một phương thức chung trong Scala và nó sẽ chạy với tốc độ của các phương thức chuyên dụng. Và nếu bạn cũng cần các số liệu chung chung nhanh, bạn có thể dễ dàng thực hiện bằng cách sử dụng "mẫu" Kiểu chữ để tiêm các hoạt động và giá trị cho các loại số khác nhau.

Ngoài ra, Scala là tuyệt vời theo nhiều cách khác nhau. Và bạn không phải viết lại tất cả mã cho Scala, vì Scala <-> Khả năng tương tác Java là tuyệt vời. Chỉ cần đảm bảo sử dụng SBT (công cụ xây dựng scala) để xây dựng dự án.


-1

Nếu hàm trong câu hỏi lớn, biến các bit "mode / paradigm" thành một giao diện và sau đó chuyển một đối tượng thực hiện giao diện đó làm tham số cho hàm có thể hoạt động. GoF gọi đây là mô hình "Chiến lược", iirc. Nếu chức năng là nhỏ, chi phí gia tăng có thể là đáng kể ... ai đó đã đề cập đến hồ sơ chưa? ... nhiều người nên đề cập đến hồ sơ.


điều này đọc giống như một bình luận hơn là một câu trả lời
gnat

-2

Chương trình này bao nhiêu tuổi? Hầu hết các phần cứng mới hơn có thể sẽ loại bỏ nút cổ chai và bạn có thể chuyển sang phiên bản dễ bảo trì hơn. Nhưng cần phải có một lý do để bảo trì, vì vậy trừ khi nhiệm vụ của bạn là cải thiện cơ sở mã, hãy để nó như đang hoạt động.


1
điều này dường như chỉ lặp lại các điểm được thực hiện và giải thích trong câu trả lời trước được đăng vài giờ trước
gnat

1
Cách duy nhất để xác định nơi tắc nghẽn thực sự là cấu hình nó - như đã đề cập trong câu trả lời khác. Không có thông tin quan trọng đó, bất kỳ sửa chữa được đề xuất cho hiệu suất là suy đoán thuần túy.
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.