2012-03-02 39 views
14

Tôi hiểu rằng việc sử dụng builder cho phép các lớp con ghi đè dễ dàng thuộc tính mặc định và vai trò có thể require chúng. Đây cũng có thể được thực hiện bằng default như vậy:Moose "người xây dựng" và "mặc định"

has 'foo' => 
    is  => 'rw', 
    isa  => 'Str', 
    default => sub { $_[0]->_build_foo }; 

Tôi tự hỏi nếu có những lợi thế hơn nữa để sử dụng builder Tôi không biết? Tôi đã đưa ra một số bản thân mình:

  • builder là khai báo để bạn có thể nội quan mà foo được xây dựng bởi _build_foo
  • builder loại bỏ một wrapper chương trình con làm cho nó nhanh hơn một chút
  • builder cho phép việc sử dụng các hữu ích lazy_build.

CẬP NHẬT Để làm rõ, đây không phải là về default vs builder nói chung nhưng default => sub { $_[0]->_build_foo } vs builder => '_build_foo'.

Trả lời

12

Tôi nghĩ bạn đã trả lời câu hỏi của riêng bạn. Sử dụng builder cho phép kết buộc muộn, đóng vai trò độc đáo với vai trò và lớp học được dự định để được phân lớp. Nó cũng có giá trị nếu người xây dựng là khá dài - tôi không bao giờ đặt nhiều hơn một dòng dài vào một định nghĩa thuộc tính. Không có sự khác biệt chức năng thực sự; default có thể dễ dàng mô phỏng builder, nhưng kết quả không phải là rất đẹp.

+0

Tại sao bạn nghĩ 'default => \ & builder' không làm mọi thứ trong số đó? – ikegami

+2

@ikegami vì '\ & builder' sẽ được giải quyết thành' & builder' trong gói hiện tại. Một lớp con cung cấp một 'builder' mới sẽ phải làm' has '+ attr' => builder => \ & builder' một lần nữa để liên kết nó với phương thức ghi đè. OTOH 'builder => 'builder'' được giải quyết khi chạy. – hobbs

+0

Là một điểm đối kháng, một phân lớp có thể ghi đè thuộc tính và thay đổi 'mặc định'. Điều này không đòi hỏi phải biết tên phương thức 'builder' có thể là private. * Cả hai * yêu cầu biết nếu thuộc tính đang sử dụng 'default' hoặc' builder' bởi vì bạn không thể có cả hai. Đây thường là thông tin cá nhân và có thể thay đổi. Việc ghi đè hành vi mặc định của thuộc tính trong Moose là có vấn đề, bạn phải xuất bản chi tiết về cách hành vi mặc định đó được triển khai hoặc lớp con phải nhìn vào. :/ – Schwern

3

Không có sự khác biệt giữa

default => sub { $_[0]->_build_foo } 

builder => '_build_foo' 

Sự khác biệt chính giữa defaultbuilder là một ai gọi một phụ anon và người kia gọi một phương thức có tên.

has created_time_stamp => (
    default => sub { time() }, 
); 

so

has created_time_stamp => (
    builder => '_build_created_time_stamp', 
); 

sub _build_created_time_stamp { time() } 

Sử dụng default làm giảm di chuyển qua các mã như tất cả mọi thứ là nơi bạn cần đến nó. Tôi sử dụng nó vì lý do đó. Đó là sử dụng ít đánh máy hơn là tiền thưởng.

Nó cũng buộc bạn phải rõ ràng hơn về việc ghi đè người xây dựng. Các câu trả lời khác đã coi đây là một con, nhưng tôi xem xét việc gọi các phương thức ảo trên một đối tượng mà thậm chí chưa được xây dựng để trở thành một thực hành tồi! Đó là những gì BUILD là dành cho.

+0

Câu hỏi đặc biệt về 'default => sub {$ _ [0] -> _ build_foo}' vs 'builder => '_build_foo'', vì vậy các lớp con có thể ghi đè lên trình tạo mặc định, không phải mặc định so với trình tạo chung. Đó là lý do tại sao cuộc gọi phụ bổ sung. – Schwern

+0

@Schwern, Tại sao bạn lại làm vậy ?! Không có lý do để làm điều đó. Vâng, có thể cho sự nhất quán nếu bạn đã sử dụng 'mặc định 'ở mọi nơi khác. – ikegami

+2

"Tại sao bạn làm điều đó" là chính xác những gì tôi yêu cầu. Dường như với tôi rằng 'builder => '_build_foo'' là đường cú pháp cho' default => sub {$ _ [0] -> _ build_foo} '. Còn gì nữa không? – Schwern

4

Sử dụng 'trình tạo' và 'mặc định' một cách thích hợp có thể làm cho mã của bạn dễ đọc và sắp xếp hơn.

'người xây dựng' cũng có thể phù hợp với một mô hình lập trình quen thuộc, nơi các phương pháp riêng bắt đầu bằng dấu gạch dưới.

has json => (is => 'ro', default => sub { JSON->new }) 
has schema => (is => 'ro', builder => '_schema' } 

sub _schema { 
    my $self = shift; 
    $self->log_debug('constructing schema') if($self->debug); 
    My::App::Schema->connect($self->dsn,$self->username,$self->password) 
} 

Bên cạnh đó, sử dụng xây dựng cho phép bạn bật chức năng đắt tiền vào accessors memoized mà không cần chạm vào phương pháp ban đầu:

sub get_things { 
    my $self = shift; 
    return +{ map { $_ => $self->price_for($_) } 
    $self->wodgets->calulate_expensive_things }; 

Refactor với memoization:

has things => (is => 'ro', lazy => 1, builder => 'get_things'); 

Đó là hầu hết các cách Tôi đã sử dụng công cụ xây dựng để làm rõ mã của mình.

+0

Tôi cũng thích sử dụng chúng cho các thuộc tính nói, vai trò, mà ai đó có thể muốn xác định lại trong một lớp học tiêu thụ vai trò đã nói. Có vẻ dễ hơn một chút so với việc xác định lại toàn bộ thuộc tính bằng dấu '+' ở phía trước tên. – dhoss

+0

Xin lỗi, đây không phải là về 'builder' so với' default' nói chung nhưng 'default => sub {$ _ [0] -> _ build_foo}' vs 'builder => '_build_foo''. – Schwern

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