2011-06-19 18 views
5

Tôi đã nghĩ rằng tôi không thực sự hiểu tại sao tất cả mọi thứ trong chất xúc tác đều sử dụng đối tượng bối cảnh. Có vẻ như tất cả mọi thứ chỉ là về bắt đầu vớiTại sao chúng ta sử dụng đối tượng bối cảnh của Catalyst? Mục đích của nó là gì?

my ($self, $c) = @_; 

chúng tôi quấn DBIC với một mô hình chất xúc tác và kết thúc với

$c->model('DBIC::Table') ... 

hoặc có lẽ chúng ta làm

$c->log->warn('foo'); 

nhưng tôi không hiểu tại sao không, chúng tôi chỉ cần làm

log('warn', 'foo'); # or whatever the API for some log library is. 

Tại sao chúng ta làm mọi thứ mặc dù đối tượng ngữ cảnh? điều gì làm cho nó trở nên đặc biệt?

Trả lời

5

Nếu tôi hiểu những gì đang xảy ra một cách chính xác (và tôi chưa xem chất xúc tác rất khó, do đó dễ dàng thực hiện được), Biến ngữ cảnh là khung gọi. Khi có yêu cầu, khung công tác xây dựng tất cả thông tin vào chính nó và gọi một phương thức trong lớp của bạn tự đi qua để phương thức của bạn có quyền truy cập vào tất cả thông tin đó và phần còn lại của khung công tác. Bạn có thể thấy rằng đọc khoảng inversion of control (hoặc IoC) giúp bạn hiểu.

Ngoài ra, bằng cách gói tất cả chức năng trong biến ngữ cảnh, bạn không gặp phải bất kỳ vấn đề về không gian tên nào. Các lớp điều khiển, mô hình, vv chỉ phải có các phương thức mà chúng khai báo trong không gian tên của chúng.

2

Một thành ngữ phổ biến trong Perl và các ngôn ngữ khác là chuyển các đối tượng "thần" có hiệu quả cung cấp giao diện cho không gian tên. Điều này cho phép các phương thức gọi thay vì yêu cầu tất cả các hàm được nhập vào không gian tên của bạn. Mỗi không gian tên này cũng có thể được tùy chỉnh với dữ liệu thời gian chạy khác nhau (các cá thể của đối tượng).

Nếu bạn không thích cú pháp của các phương thức gọi trên đối tượng, có vẻ như bạn đang tìm kiếm thứ gì đó tương tự như khối with của Javascript. Trong khi Perl không có một cấu trúc bản địa mà thực hiện điều này, nó cung cấp các công cụ để làm một:

use warnings; 
use strict; 
use Carp(); 

sub with ($&) { 
    my ($obj, $code) = @_; 
    my $auto = (caller).'::AUTOLOAD'; 
    no strict 'refs'; 
    local *$auto = sub { 
     my ($name) = $$auto =~ /([^:]+)$/; 
     my $method = $obj->can($name) 
        || $obj->can(lcfirst $name) 
      or Carp::croak "no method '$name' on '$obj' in with block"; 
     unshift @_, $obj; 
     goto &$method 
    }; 
    $code->() 
} 

Với đối tượng giả:

{package Obj; 
    sub new {bless []} 
    sub log {shift; say "logging @_"} 
    sub model {shift; say "setting model to @_"} 
} 

Sau đó bạn có thể viết:

my $c = Obj->new; 

with $c => sub { 
    model('DBIC::Table'); 
    Log('hello world'); # ucfirst 
    &log('hello again'); # or with a & since 'log' is a builtin 
}; 

Bản in nào:

 
setting model to DBIC::Table 
logging hello world 
logging hello again 

Hãy vui vẻ, chỉ cần nhớ rằng tên dựng sẵn hoặc tên của các chương trình con đã được định nghĩa sẽ không bị ghi đè trong khối with. Bạn có thể sử dụng phiên bản ucfirst của tên hoặc chỉ cần gọi phương thức trong các trường hợp đó. Tất cả các chương trình con mới trong khối with cũng phải được gọi với các số parens Log('hello') và không phải là Log 'hello' vì tên đó không được biết tại thời gian biên dịch.

+0

nó không thực sự là một câu hỏi về cú pháp ... mặc dù tôi thích cách thức gọi của Rakudo ... – xenoterracide

+1

@xenoterracide => bạn hỏi tại sao chất xúc tác bạn viết '$ c-> log-> cảnh báo (...) 'thay vì' log (warn => ...) '. Đó là một sự khác biệt về cú pháp. Lý do tại sao là bởi vì đó là những gì các tác giả của chất xúc tác nghĩ là tốt nhất. Vì vậy, tôi đã cho bạn một cách để viết nó theo cách bạn muốn (mặc dù trong trường hợp này, nó sẽ là 'Log() -> warn (...)' vì đó là cách thức hoạt động 'log' của chất xúc tác), loại câu trả lời là bạn mong đợi? –

+0

hỏi thêm về lý do tại sao mọi thứ được thực hiện thông qua đối tượng ngữ cảnh, ngay cả những thứ bên ngoài được truy cập thông qua nó thường. chính xác lý do tại sao cú pháp rõ ràng hơn lý do tại sao nó thông qua đối tượng ngữ cảnh. trái với việc nói, chỉ cần sử dụng nhật ký api trực tiếp. – xenoterracide

1

Ai đó thường sẽ viết mọi thứ bạn đã thể hiện trong Bộ điều khiển Catalyst ::. Bây giờ bạn phải nhớ rằng một bộ điều khiển Catalyst tồn tại để thực hiện ánh xạ URL của bạn. Chắc chắn, có thể nhập rất nhiều chức năng vào bộ điều khiển, nhưng khi Catalyst tự nhập chức năng log, bạn sử dụng chức năng này cho ánh xạ URL như thế nào?

Ví dụ sub log : Local { ... }. Một thời gian ngắn sẽ không thể, hoặc nó sẽ phức tạp hơn sau đó nó nên được. Bộ điều khiển gần như không có chức năng nào, do đó bạn không cần phải nhớ nhiều chức năng và không có bất kỳ xung đột nào.

Đó cũng là lý do tại sao chính Perl chọn có các ký tự đặc biệt trong các biến đặc biệt. Giống như $/, $_, $] và cứ tiếp tục như vậy. Chắc chắn họ cũng có thể sử dụng $INPUT_RECORD_SEPARATOR hoặc $RS làm mặc định, nhưng sau đó bạn cần biết chúng và có thể xung đột với mã của bạn nếu bạn không biết tất cả các biến đặc biệt.

Lý do khác là các tính năng bổ sung mà bạn gọi trên $c có một số ngữ cảnh. Ví dụ: bạn có thể bật hoặc tắt ghi nhật ký với $c->log->disable('warn', 'error') hoặc chỉ bật chúng. Ngữ cảnh này được truyền vào bộ điều khiển sâu hơn. Và họ không phải là toàn cầu, bạn có thể đặt chúng trên mọi yêu cầu đến một tiểu bang khác.

Một lý do khác là chức năng bổ sung mà bạn sử dụng có thể và đôi khi cần phải đọc tệp cấu hình hoặc những thứ khác. Sử dụng một đối tượng mà bạn truyền xung quanh cho mọi yêu cầu (Mỗi $c là đặc biệt cho mọi yêu cầu) và có thể sửa đổi cho mọi yêu cầu cho phép mở rộng của bạn khả năng yêu cầu thông tin từ ứng dụng của bạn hoặc xử lý trạng thái cho một yêu cầu cụ thể.

Nhưng nếu bạn vẫn không muốn điều này, bạn không bị buộc phải sử dụng $c. Ví dụ bạn chỉ có thể tải Log :: Log4Perl bằng tay, và sử dụng một cấu hình đặc biệt cho nó, và không sử dụng $c->log ở tất cả. Hoặc bạn có thể nhập nhiều chức năng của chính mình. Nhưng tôi nghĩ rằng mặc định để không gây ô nhiễm không gian tên và cung cấp cho bạn khả năng để làm một cái gì đó đặc biệt cho mọi yêu cầu là một mặc định tốt.

Và ít nhất không có quy tắc nào bạn phải sử dụng $c. Ví dụ, bản thân tôi sử dụng DateTime trực tiếp và tạo các đối tượng mới và không sử dụng Catalyst :: Plugin :: DateTime cho phép tôi làm $c->datetime. Và tôi không thấy bất kỳ lợi ích của việc làm cuối cùng. Chỉ vì tồn tại rất nhiều plugin mở rộng $c, không có nghĩa là chúng hữu ích hoặc bạn phải sử dụng chúng.

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