2010-02-24 41 views
18

Có rất nhiều buzz về MooseX::Method::Signatures và thậm chí trước đó, các mô-đun như Params::Validate được thiết kế để nhập kiểm tra mọi đối số cho các phương thức hoặc chức năng. Tôi đang xem xét việc sử dụng trước đây cho tất cả các mã Perl tương lai của tôi, cả cá nhân và tại nơi làm việc của tôi. Nhưng tôi không chắc liệu nó có xứng đáng với nỗ lực không.Kiểm tra đối số hàm Perl có đáng giá không?

Tôi đang nghĩ về tất cả mã Perl mà tôi đã thấy (và được viết) trước khi thực hiện không kiểm tra như vậy. Tôi rất hiếm khi thấy một mô-đun thực hiện việc này:

my ($a, $b) = @_; 
defined $a or croak '$a must be defined!'; 
!ref $a or croak '$a must be a scalar!"; 
... 
@_ == 2 or croak "Too many arguments!"; 

Có lẽ vì thực tế chúng tôi không gửi các tham số dư thừa cho các chức năng và chúng tôi không gửi arrayrefs đến các phương thức mong đợi vô hướng - hoặc nếu chúng ta làm, chúng ta có use warnings; và chúng ta nhanh chóng nghe về nó - cách tiếp cận duck typing.

Vì vậy, việc kiểm tra loại Perl có đáng để đạt hiệu suất hay là các thế mạnh của nó chủ yếu được hiển thị bằng các ngôn ngữ được biên dịch, được đánh máy mạnh như C hoặc Java?

Tôi quan tâm đến câu trả lời từ bất kỳ ai có kinh nghiệm viết Perl sử dụng các mô-đun này và đã thấy lợi ích (hoặc không) từ việc sử dụng chúng; nếu công ty/dự án của bạn có bất kỳ chính sách nào liên quan đến việc kiểm tra loại; và mọi vấn đề về kiểm tra và thực hiện loại.

CẬP NHẬT: Tôi đã đọc một bài viết thú vị về chủ đề gần đây, được gọi là Strong Testing vs. Strong Typing. Bỏ qua sự thiên vị của Python, về cơ bản, việc kiểm tra kiểu có thể bị nghẹt thở trong một số trường hợp, và ngay cả khi chương trình của bạn vượt qua kiểm tra kiểu, nó không đảm bảo tính chính xác - kiểm tra thích hợp là cách duy nhất để chắc chắn.

Trả lời

7

Tôi về cơ bản đồng tình với brian. Bao nhiêu bạn cần phải lo lắng về đầu vào của phương thức của bạn phụ thuộc rất nhiều vào số tiền bạn lo ngại rằng a) ai đó sẽ nhập dữ liệu xấu và b) dữ liệu xấu sẽ làm hỏng mục đích của phương thức. Tôi cũng sẽ thêm rằng có một sự khác biệt giữa các phương pháp bên ngoài và nội bộ. Bạn cần phải siêng năng hơn về các phương pháp công cộng bởi vì bạn đang hứa hẹn với người tiêu dùng về lớp học của bạn; ngược lại bạn có thể ít siêng năng hơn về các phương pháp nội bộ khi bạn có quyền kiểm soát lớn hơn (lý thuyết) đối với mã truy cập nó, và chỉ có bản thân bạn đổ lỗi nếu mọi thứ xảy ra sai.

MooseX :: Phương pháp :: Chữ ký là giải pháp thanh lịch để thêm một cách khai báo đơn giản để giải thích các tham số của phương thức. Phương pháp :: Chữ ký :: Đơn giản và Params :: Xác nhận là tốt đẹp nhưng thiếu một trong những tính năng tôi thấy hấp dẫn nhất về Moose: hệ thống Type. Tôi đã sử dụng MooseX :: Declare và mở rộng MooseX :: Phương pháp :: Chữ ký cho một số dự án và tôi thấy rằng thanh để viết các kiểm tra thêm là rất tối thiểu nó gần như quyến rũ.

13

Nếu điều quan trọng là bạn phải kiểm tra xem đối số có chính xác với những gì bạn cần hay không, nó đáng giá. Hiệu suất chỉ có vấn đề khi bạn đã có chức năng chính xác. Việc bạn có thể nhận được câu trả lời sai hoặc kết xuất lõi là không quan trọng. :)

Bây giờ, điều đó nghe có vẻ giống như một điều ngu ngốc để nói, nhưng hãy xem xét một số trường hợp không phải vậy. Tôi có thực sự quan tâm đến những gì trong số @_ tại đây không?

sub looks_like_a_number { $_[0] !~ /\D/ } 
sub is_a_dog   { eval { $_[0]->DOES('Dog') } } 

Trong hai ví dụ đó, nếu đối số không phải là những gì bạn mong đợi, bạn vẫn sẽ nhận được câu trả lời đúng vì đối số không hợp lệ sẽ không vượt qua các bài kiểm tra. Một số người thấy điều đó là xấu, và tôi có thể thấy quan điểm của họ, nhưng tôi cũng nghĩ rằng sự thay thế là xấu. Ai thắng?

Tuy nhiên, sẽ có những lúc bạn cần điều kiện bảo vệ vì tình huống của bạn không đơn giản như vậy. Điều tiếp theo bạn phải truyền dữ liệu của bạn để có thể mong đợi chúng nằm trong phạm vi nhất định hoặc một số loại nhất định và không thất bại một cách trang nhã.

Khi tôi nghĩ về điều kiện bảo vệ, tôi nghĩ qua những gì có thể xảy ra nếu đầu vào kém và tôi quan tâm đến thất bại đến mức nào. Tôi phải phán xét rằng theo nhu cầu của từng tình huống. Tôi biết rằng hút như là một câu trả lời, nhưng tôi có xu hướng thích nó tốt hơn so với một cách tiếp cận bondage-và-kỷ luật, nơi bạn phải đi qua tất cả các mớ hỗn độn ngay cả khi nó không quan trọng.

Tôi sợ Params::Validate vì mã của mã thường dài hơn chương trình con của tôi. Các công cụ Moose rất hấp dẫn, nhưng bạn phải nhận ra rằng đó là một cách để bạn khai báo những gì bạn muốn và bạn vẫn có được những gì bạn có thể xây dựng bằng tay (bạn chỉ cần không phải nhìn thấy nó hoặc làm điều đó). Điều lớn nhất tôi ghét về Perl là thiếu chữ ký phương pháp tùy chọn, và đó là một trong những tính năng hấp dẫn nhất trong Perl 6 cũng như Moose.

3

Phản đối mà tôi đã thấy được là việc kiểm tra tham số trên mọi lệnh gọi hàm là dư thừa và lãng phí thời gian của CPU. Những người ủng hộ của đối số này ủng hộ một mô hình trong đó tất cả các dữ liệu được kiểm tra chặt chẽ khi lần đầu tiên vào hệ thống, nhưng các phương thức nội bộ không có kiểm tra tham số vì chúng chỉ được gọi bằng mã sẽ truyền dữ liệu đã qua kiểm tra tại hệ thống. biên giới, do đó, nó được cho là vẫn hợp lệ. Về lý thuyết, tôi thực sự thích âm thanh, nhưng tôi cũng có thể thấy nó dễ dàng như thế nào nếu có ai đó sử dụng hệ thống (hoặc hệ thống cần phát triển để cho phép sử dụng) theo cách đã được dự đoán khi biên giới xác nhận ban đầu được thiết lập. Tất cả phải mất là một cuộc gọi bên ngoài đến một chức năng nội bộ và tất cả các cược được tắt. Trong thực tế, tôi đang sử dụng Moose tại thời điểm này và Moose không thực sự cung cấp cho bạn tùy chọn bỏ qua xác thực ở cấp thuộc tính, cộng với MooseX :: Khai báo xử lý và xác thực tham số phương pháp ít phiền phức hơn việc bỏ đăng ký @_ bằng tay, do đó, nó khá nhiều điểm tranh luận.

+0

+1 cho thông tin Moose – DVK

1

Params :: Xác thực tác phẩm tuyệt vời, nhưng tất nhiên việc kiểm tra arg làm chậm mọi thứ. Kiểm tra là bắt buộc (ít nhất là trong mã tôi viết).

2

Tôi muốn đề cập đến hai điểm ở đây. Đầu tiên là các bài kiểm tra, câu hỏi thứ hai về hiệu suất.

1) thử nghiệm

Bạn nói rằng các xét nghiệm có thể làm được rất nhiều và rằng các xét nghiệm là cách duy nhất để chắc chắn rằng mã của bạn là chính xác. Nói chung tôi sẽ nói điều này là chính xác một cách tuyệt đối. Nhưng xét nghiệm tự nó chỉ giải quyết được một vấn đề.

Nếu bạn viết mô-đun, bạn có hai vấn đề hoặc cho phép nói hai khác nhau những người sử dụng mô-đun của bạn.

Bạn là nhà phát triển và người dùng sử dụng mô-đun của bạn. Xét nghiệm giúp với các đầu tiên mô-đun của bạn là chính xác và làm điều đúng, nhưng nó không giúp người dùng chỉ sử dụng mô-đun của bạn.

Để biết sau, tôi có một ví dụ. tôi đã viết một mô-đun bằng cách sử dụng Moose và một số nội dung khác, mã của tôi đã kết thúc luôn trong lỗi Phân đoạn. Sau đó, tôi bắt đầu gỡ lỗi mã của mình và tìm kiếm sự cố. Tôi dành khoảng 4 giờ để tìm lỗi.Cuối cùng, vấn đề là tôi có đã sử dụng Moose with the Array Trait. Tôi đã sử dụng chức năng "bản đồ" và tôi đã không cung cấp chức năng chương trình con, chỉ là một chuỗi hoặc một thứ khác.

Chắc chắn đây là lỗi tuyệt đối ngu ngốc của tôi, nhưng tôi dành một thời gian dài để gỡ lỗi. Cuối cùng chỉ là một kiểm tra đầu vào mà đối số là một subref sẽ chi phí cho nhà phát triển 10 giây của thời gian, và sẽ chi phí cho tôi và propably khác rất nhiều thời gian hơn.

Tôi cũng biết các ví dụ khác. Tôi đã viết một REST Client đến một giao diện hoàn toàn OOP với Moose. Cuối cùng, bạn luôn nhận được các đối tượng, bạn có thể thay đổi các thuộc tính nhưng chắc chắn nó không gọi API REST cho mọi thay đổi bạn đã làm. Thay vào đó bạn thay đổi giá trị của mình và cuối cùng, bạn gọi phương thức update() để chuyển dữ liệu và thay đổi giá trị.

Bây giờ tôi đã có một người dùng mà sau đó đã viết:

 
$obj->update({ foo => 'bar' }) 

Chắc chắn tôi đã nhận một lỗi trở lại, rằng bản cập nhật() không hoạt động. Nhưng chắc chắn nó không làm việc , bởi vì phương thức update() không chấp nhận một hashref. Chỉ đồng bộ hóa trạng thái thực tế của đối tượng với dịch vụ trực tuyến . Mã đúng sẽ là.

 
$obj->foo('bar'); 
$obj->update(); 

Điều đầu tiên hoạt động vì tôi chưa bao giờ kiểm tra đối số. Và tôi không ném một lỗi nếu ai đó đưa ra nhiều lý lẽ hơn tôi mong đợi. Phương pháp chỉ bắt đầu bình thường như thế nào.

 
sub update { 
    my ($self) = @_; 
    ... 
} 

Chắc chắn tất cả các thử nghiệm của tôi hoàn toàn hoạt động tốt 100%. Nhưng xử lý các lỗi này cũng không phải là lỗi khiến tôi mất thời gian. Và nó chi phí người sử dụng propably rất nhiều của thời gian hơn.

Vì vậy, cuối cùng. Có, kiểm tra là cách chính xác duy nhất để đảm bảo rằng mã của bạn hoạt động chính xác. Nhưng điều đó không có nghĩa là việc kiểm tra kiểu là vô nghĩa. Kiểm tra loại có sẵn để giúp tất cả những người không phát triển (trên mô-đun của bạn) để sử dụng mô-đun của bạn một cách chính xác. Và tiết kiệm cho bạn và những người khác tìm kiếm các lỗi kết xuất .

2) Hiệu suất

Viết tắt: Bạn không quan tâm đến hiệu suất cho đến khi bạn quan tâm.

Điều đó có nghĩa là cho đến khi mô-đun của bạn hoạt động chậm, Hiệu suất luôn nhanh chóng đủ và bạn không cần phải quan tâm đến điều này. Nếu mô-đun của bạn thực sự hoạt động để làm chậm, bạn cần điều tra thêm. Nhưng đối với những điều tra này bạn nên sử dụng một hồ sơ như Devel :: NYTProf để xem những gì là chậm.

Và tôi sẽ nói. Trong sự chậm chạp 99% không phải vì bạn gõ kiểm tra, nó là thuật toán của bạn nhiều hơn. Bạn làm rất nhiều tính toán, gọi hàm thường xuyên vv Thường thì nó sẽ giúp nếu bạn hoàn thành các giải pháp khác sử dụng một thuật toán tốt hơn, làm bộ nhớ đệm hoặc cái gì khác, và hit hiệu suất không phải là loại kiểm tra của bạn. Nhưng ngay cả khi kiểm tra là hit hiệu suất . Sau đó, chỉ cần loại bỏ nó ở những nơi quan trọng.

Không có lý do gì để loại kiểm tra ở nơi hiệu suất không vấn đề. Bạn có nghĩ rằng việc kiểm tra kiểu không quan trọng trong một trường hợp như trên? Tôi đã viết một ứng dụng khách REST ở đâu? 99% các vấn đề về hiệu suất ở đây là số lượng yêu cầu chuyển đến dịch vụ web hoặc thời gian cho yêu cầu như vậy. Không sử dụng kiểm tra kiểu hoặc MooseX :: Tuyên bố vv sẽ có thể đẩy nhanh tăng tốc một cách tuyệt đối.

Và ngay cả khi bạn thấy bất lợi về hiệu suất. Đôi khi nó được chấp nhận. Bởi vì tốc độ không quan trọng hoặc đôi khi một cái gì đó mang đến cho bạn giá trị lớn hơn. DBIx :: Class là chậm hơn sau đó SQL tinh khiết với DBI, nhưng DBIx :: Class cung cấp cho bạn rất nhiều cho các.

4

Có giá trị của nó - lập trình phòng thủ là một trong những điều mà luôn luôn có giá trị nó.

0

Tôi đang sử dụng Moose rộng rãi cho một dự án OO khá lớn mà tôi đang làm việc. Kiểm tra loại nghiêm ngặt của Moose đã cứu thịt xông khói của tôi vào một vài dịp. Quan trọng nhất là nó đã giúp tránh các tình huống mà các giá trị "undef" không chính xác được truyền cho phương thức. Chỉ trong những trường hợp đó, nó đã giúp tôi tiết kiệm thời gian gỡ lỗi của tôi ..

Lần thực hiện chắc chắn ở đó, nhưng nó có thể được quản lý. 2 giờ sử dụng NYTProf đã giúp tôi tìm thấy một vài thuộc tính Moose mà tôi đã quá khó và tôi chỉ tái cấu trúc mã của mình và cải thiện hiệu suất gấp 4 lần.

Sử dụng kiểm tra loại. Mã hóa phòng thủ là giá trị nó.

Patrick.

0

Có nó hoàn toàn xứng đáng, bởi vì nó sẽ giúp quá trình phát triển, bảo trì, gỡ lỗi, vv

Nếu một nhà phát triển vô tình gửi các thông số sai đến một phương pháp, một thông báo lỗi hữu ích sẽ được tạo ra, thay vì lỗi được truyền đến một nơi khác.

0

Đôi khi. Tôi thường làm điều đó bất cứ khi nào tôi đang đi qua các tùy chọn thông qua băm hoặc hashref. Trong những trường hợp này rất dễ nhầm lẫn hoặc sai chính tả tên tùy chọn và việc kiểm tra với Params::Check có thể tiết kiệm rất nhiều thời gian khắc phục sự cố.

Ví dụ:

sub revise { 
    my ($file, $options) = @_; 

    my $tmpl = { 
     test_mode => { allow => [0,1], 'default' => 0 }, 
     verbosity => { allow => qw/^\d+$/, 'default' => 1 }, 
     force_update => { allow => [0,1], 'default' => 0 }, 
     required_fields => { 'default' => [] }, 
     create_backup => { allow => [0,1], 'default' => 1 }, 
    }; 

    my $args = check($tmpl, $options, 1) 
     or croak "Could not parse arguments: " . Params::Check::last_error(); 
    ... 
} 

Trước khi thêm các kiểm tra, tôi muốn quên dù tên được sử dụng dấu gạch dưới hoặc dấu gạch nối, vượt qua require_backup thay vì create_backup, vv Và đây là mã tôi đã viết myself-- nếu những người khác sẽ sử dụng nó, bạn nên chắc chắn thực hiện một số loại chống giả mạo. Params::Check làm cho việc kiểm tra loại, kiểm tra giá trị được phép, giá trị mặc định, tùy chọn bắt buộc, lưu trữ các giá trị tùy chọn cho các biến khác và hơn thế nữa là khá dễ dàng.

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