Tôi có nên cấu trúc lại các hàm lớn mà chủ yếu bao gồm một regex không? [đóng cửa]


15

Tôi vừa viết một hàm kéo dài khoảng 100 dòng. Nghe điều đó, có lẽ bạn rất muốn nói với tôi về những trách nhiệm đơn lẻ và thúc giục tôi tái cấu trúc. Đây là bản năng ruột của tôi, nhưng đây là vấn đề: Chức năng làm một việc. Nó thực hiện một thao tác chuỗi phức tạp và phần thân hàm bao gồm chủ yếu là một biểu thức dài dòng, được chia thành nhiều dòng được ghi lại. Nếu tôi chia regex thành nhiều chức năng, tôi cảm thấy mình thực sự sẽ mất khả năng đọc, vì tôi đang chuyển đổi ngôn ngữ một cách hiệu quả và sẽ không thể tận dụng một số tính năng mà regex cung cấp. Đây là câu hỏi của tôi:

Khi nói đến thao tác chuỗi với các biểu thức thông thường, các thân hàm lớn có còn là một mô hình chống không? Có vẻ như các nhóm bắt giữ được đặt tên phục vụ một mục đích rất giống với các chức năng. Nhân tiện, tôi có các bài kiểm tra cho mọi luồng thông qua regex.


3
Tôi không nghĩ rằng có bất cứ điều gì sai với chức năng của bạn, vì cho rằng một phần lớn của nó là tài liệu . Tuy nhiên, có thể có một vấn đề duy trì với việc sử dụng một biểu thức chính quy lớn ở vị trí đầu tiên.
Joel Cornett

2
Bạn có chắc chắn một regex khổng lồ là giải pháp tốt nhất cho vấn đề của bạn? Bạn đã xem xét các lựa chọn thay thế đơn giản hơn, như thư viện trình phân tích cú pháp hoặc thay thế định dạng tệp tùy chỉnh bằng định dạng chuẩn (XML, JSON, v.v.) chưa?
lortabac

2
Có các chức năng khác, sử dụng phiên bản thay đổi / nâng cao / đơn giản hóa của biểu thức chính quy này không? Đó sẽ là một chỉ số quan trọng rằng tái cấu trúc nên diễn ra. Nếu không, tôi sẽ để nó như vậy. Cần một thao tác chuỗi phức tạp như thế là một lá cờ vàng theo đúng nghĩa của nó (tôi cũng không biết bối cảnh, do đó chỉ là màu vàng), và tái cấu trúc chức năng xuống dường như giống như một nghi thức để chuộc lỗi từ cảm giác tội lỗi mà người ta cảm nhận về nó;)
Konrad Morawski

8
Làm thế nào một regrec 100 dòng chỉ có thể làm 1 điều?
Pieter B

@lortabac: Đầu vào là văn bản do người dùng tạo (văn xuôi)
DudeOnRock

Câu trả lời:


36

Những gì bạn đang gặp phải là sự bất đồng về nhận thức xuất phát từ việc lắng nghe những người ủng hộ việc tuân thủ các hướng dẫn theo nguyên tắc "thực hành tốt nhất" đối với việc ra quyết định hợp lý.

Bạn đã hoàn thành bài tập về nhà rõ ràng:

  • Mục đích của chức năng được hiểu.
  • Các hoạt động thực hiện của nó được hiểu (nghĩa là có thể đọc được).
  • Có các bài kiểm tra bảo hiểm đầy đủ của việc thực hiện.
  • Những bài kiểm tra vượt qua, có nghĩa là bạn tin rằng việc thực hiện là chính xác.

Nếu bất kỳ điểm nào trong số đó không đúng, tôi sẽ là người đầu tiên nói rằng chức năng của bạn cần hoạt động. Vì vậy, có một phiếu bầu để giữ nguyên mã.

Phiếu bầu thứ hai đến từ việc xem xét các lựa chọn của bạn và những gì bạn nhận được (và mất) từ mỗi lựa chọn:

  • Cấu trúc lại. Điều này giúp bạn tuân thủ ý tưởng của ai đó về thời gian của một chức năng và hy sinh khả năng đọc.
  • Không làm gì cả. Điều này duy trì khả năng đọc và hy sinh tuân thủ với ý tưởng của ai đó về thời gian của một chức năng.

Quyết định này được đưa ra mà bạn đánh giá cao hơn: khả năng đọc hoặc độ dài. Tôi rơi vào trại tin rằng độ dài là tốt nhưng khả năng đọc là quan trọng và sẽ lấy cái sau hơn bất kỳ ngày nào trong tuần.

Điểm mấu chốt: nếu nó không bị hỏng, đừng sửa nó.


10
+1 cho "Nếu nó không bị hỏng, đừng sửa nó."
Giorgio

Thật. Các quy tắc của Sandy Metz ( gist.github.com/henrik/4509394 ) rất hay và tất cả, nhưng tại youtube.com/watch?v=VO-NvnZfMA4#t=1379 cô ấy nói về cách họ đến và lý do mọi người tham gia Họ quá nghiêm túc.
Amadan

@Amdan: Với bối cảnh bổ sung từ video, những gì Metz đã làm có ý nghĩa. Khuyến nghị của cô ấy cho một khách hàng đã cố tình cực đoan ở một đầu để chống lại hành vi cực đoan ở đầu kia như một cách để kéo nó vào giữa hợp lý hơn. Phần còn lại của cuộc thảo luận sôi nổi trước câu trả lời của tôi: lý luận, không phải đức tin, là cách để xác định hướng hành động tốt nhất.
Blrfl

19

Thành thật mà nói, chức năng của bạn có thể "làm một việc", nhưng như bạn đã nói

Tôi có thể bắt đầu chia regex thành nhiều chức năng,

có nghĩa là mã reg ex của bạn làm rất nhiều thứ. Và tôi đoán nó có thể được chia thành các đơn vị nhỏ hơn, có thể kiểm tra riêng lẻ. Tuy nhiên, nếu đây là một ý tưởng tốt thì không dễ trả lời, (đặc biệt là không nhìn thấy mã thực tế). Và câu trả lời đúng có thể không phải là "có" hoặc "không", nhưng "chưa, nhưng lần sau bạn phải thay đổi một cái gì đó trong reg exp đó".

nhưng cảm giác như tôi thực sự sẽ mất khả năng đọc theo cách đó, vì tôi đang chuyển đổi ngôn ngữ một cách hiệu quả

Và đây là điểm cốt lõi - bạn có một đoạn mã được viết bằng ngôn ngữ reg ex . Ngôn ngữ này không cung cấp bất kỳ phương tiện trừu tượng tốt nào trong chính nó (và tôi không coi "các nhóm bắt giữ có tên" là một thay thế cho các chức năng). Vì vậy, việc tái cấu trúc "trong ngôn ngữ reg ex" là không thực sự có thể và việc đan xen các reg nhỏ hơn với ngôn ngữ máy chủ có thể không thực sự cải thiện khả năng đọc (ít nhất, bạn cảm thấy như vậy, nhưng bạn có nghi ngờ, nếu không bạn sẽ không đăng câu hỏi) . Vì vậy, đây là lời khuyên của tôi

  • hiển thị mã của bạn cho một nhà phát triển nâng cao khác (có thể trên /codereview// ) để đảm bảo người khác nghĩ về khả năng đọc theo cách bạn làm. Hãy cởi mở với ý tưởng rằng những người khác có thể không tìm thấy một reg reg 100 dòng có thể đọc được như bạn. Đôi khi khái niệm "nó không dễ bị phá vỡ thành những mảnh nhỏ hơn" có thể được khắc phục chỉ bằng một đôi mắt thứ hai.

  • quan sát khả năng tiến hóa thực tế - liệu reg exp sáng bóng của bạn vẫn trông rất tốt khi có yêu cầu mới và bạn phải thực hiện và kiểm tra chúng? Miễn là reg exp của bạn hoạt động, tôi sẽ không chạm vào nó, nhưng bất cứ khi nào phải thay đổi, tôi sẽ xem xét lại nếu thực sự nên đưa mọi thứ vào một khối lớn này - và (nghiêm túc!) Suy nghĩ lại nếu chia thành mảnh nhỏ hơn sẽ không phải là một lựa chọn tốt hơn.

  • quan sát khả năng bảo trì - bạn có thể gỡ lỗi reg exp một cách hiệu quả ở dạng hiện tại không? Đặc biệt là sau khi bạn phải thay đổi một cái gì đó, và bây giờ các xét nghiệm của bạn cho bạn biết rằng có điều gì đó không ổn, bạn có trình gỡ lỗi reg exp giúp bạn tìm ra nguyên nhân gốc không? Nếu việc sửa lỗi trở nên khó khăn, đó cũng sẽ là dịp để xem xét lại thiết kế của bạn.


Tôi muốn nói các nhóm chụp có tên (các nhóm chụp nói chung, thực sự) giống với các biến cuối cùng / ghi một lần, hoặc có lẽ là các macro. Chúng cho phép bạn tham chiếu các phần cụ thể của trận đấu, từ đối tượng khớp được trả về từ bộ xử lý regex hoặc sau đó trong chính biểu thức chính quy.
JAB

4

Đôi khi một chức năng dài hơn làm một việc là cách thích hợp nhất để xử lý một đơn vị công việc. Bạn có thể dễ dàng có được các chức năng rất dài khi bạn bắt đầu xử lý truy vấn cơ sở dữ liệu (sử dụng ngôn ngữ truy vấn yêu thích của bạn). Để làm cho một hàm (hoặc phương thức) dễ đọc hơn trong khi giới hạn nó vào mục đích đã nêu của nó là điều tôi sẽ xem xét kết quả mong muốn nhất của hàm.

Độ dài là một "tiêu chuẩn" tùy ý khi nói đến kích thước mã. Trong đó một hàm 100 dòng trong C # có thể được coi là dài, thì nó sẽ rất nhỏ trong một số phiên bản lắp ráp. Tôi đã thấy một số truy vấn SQL nằm trong phạm vi 200 dòng mã trả về một bộ dữ liệu rất phức tạp cho báo cáo.

Mã làm việc đầy đủ , đơn giản như bạn có thể làm cho nó là mục tiêu một cách hợp lý .

Đừng thay đổi nó chỉ vì nó dài.


3

Bạn luôn có thể chia regex thành các biểu thức con và dần dần soạn biểu thức cuối cùng. Điều này có thể hỗ trợ sự hiểu biết cho một mẫu rất lớn, đặc biệt nếu cùng một mẫu phụ được lặp lại nhiều lần. Ví dụ trong Perl;

my $start_re = qr/(?:\w+\.\w+)/;
my $middle_re = qr/(?:DOG)|(?:CAT)/;
my $end_re = qr/ => \d+/;

my $final_re = $start_re . $middle_re . $end_re;
# or: 
# my $final_re = qr/${start_re}${middle_re}${end_re}/

Tôi sử dụng cờ verbose, thậm chí còn thuận tiện hơn những gì bạn đang đề xuất.
DudeOnRock

1

Tôi sẽ nói phá vỡ nó nếu nó là phá vỡ. từ quan điểm duy trì và có thể khả năng phục hồi nó có ý nghĩa để phá vỡ nó, nhưng tất nhiên bạn phải xem xét tự nhiên chức năng của bạn và cách bạn nhận được đầu vào và những gì nó sẽ trở lại.

Tôi nhớ rằng tôi đang làm việc để phân tích luồng dữ liệu được phân đoạn thành các đối tượng, vì vậy về cơ bản, tôi đã chia nó thành hai phần chính, một phần là xây dựng đơn vị hoàn chỉnh của Chuỗi từ văn bản được mã hóa và trong phần thứ hai phân tích các đơn vị đó thành từ điển dữ liệu và sắp xếp chúng (có thể là thuộc tính ngẫu nhiên cho các đối tượng khác nhau) và hơn là cập nhật hoặc tạo đối tượng.

Ngoài ra tôi có thể chia mỗi phần chính thành nhiều chức năng nhỏ hơn và cụ thể hơn để cuối cùng tôi có 5 chức năng khác nhau để thực hiện toàn bộ và tôi có thể sử dụng lại một số chức năng ở những nơi khác nhau.


1

Một điều mà bạn có thể hoặc không thể xem xét là viết một trình phân tích cú pháp nhỏ bằng ngôn ngữ bạn đang sử dụng thay vì sử dụng biểu thức chính quy trong ngôn ngữ đó. Điều này có thể dễ dàng hơn để đọc, kiểm tra và duy trì.


Tôi đã nghĩ về điều này bản thân mình. Vấn đề là đầu vào là văn xuôi và tôi đang lấy tín hiệu từ bối cảnh và định dạng. Nếu có thể viết một trình phân tích cú pháp cho một cái gì đó như thế này, tôi rất thích tìm hiểu thêm về nó! Tôi không thể tìm thấy bất cứ điều gì bản thân mình.
DudeOnRock

1
Nếu một regex có thể phân tích cú pháp, bạn có thể phân tích cú pháp. Phản hồi của bạn cho tôi thấy rằng bạn có thể không thành thạo trong việc phân tích cú pháp. Nếu đó là trường hợp, bạn có thể muốn gắn bó với regex. Hoặc là hoặc học một kỹ năng mới.
Thomas Eding

Tôi thích học một kỹ năng mới. Bất kỳ nguồn lực tốt, bạn có thể đề nghị? Tôi quan tâm đến lý thuyết đằng sau nó là tốt.
DudeOnRock

1

Regex khổng lồ là một lựa chọn tồi trong hầu hết các trường hợp. Theo kinh nghiệm của tôi, chúng thường được sử dụng vì nhà phát triển không quen với việc phân tích cú pháp (xem câu trả lời của Thomas Eding ).

Dù sao đi nữa, giả sử bạn muốn bám vào một giải pháp dựa trên regex.

Vì tôi không biết mã thực tế, tôi sẽ kiểm tra hai tình huống có thể xảy ra:

  • Regex rất đơn giản (rất nhiều kết hợp theo nghĩa đen và một vài lựa chọn thay thế)

    Trong trường hợp này, các tính năng nâng cao được cung cấp bởi một regex duy nhất là không thể thiếu. Điều này có nghĩa là bạn có thể sẽ được hưởng lợi từ việc tách nó.

  • Regex rất phức tạp (rất nhiều lựa chọn thay thế)

    Trong trường hợp này, bạn thực sự không thể có phạm vi kiểm tra đầy đủ, bởi vì bạn có thể có hàng triệu luồng sở hữu. Vì vậy, để kiểm tra nó, bạn cần tách nó ra.

Tôi có thể thiếu trí tưởng tượng, nhưng tôi không thể nghĩ về bất kỳ tình huống thực tế nào trong đó regex 100 dòng là một giải pháp tố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.