Đây có phải là ý định thiết kế của Raku?
Thật công bằng khi nói rằng Raku không hoàn toàn không bị ảnh hưởng trong lĩnh vực này. Câu hỏi của bạn chạm vào hai chủ đề trong thiết kế của Raku, cả hai đều đáng để thảo luận.
Raku có giá trị l hạng nhất
Raku sử dụng dồi dào các giá trị l là một thứ hạng nhất. Khi chúng tôi viết:
has $.x is rw;
Phương thức được tạo là:
method x() is rw { $!x }
Ở is rw
đây chỉ ra rằng phương thức đang trả về một giá trị l - nghĩa là, một cái gì đó có thể được gán cho. Do đó, khi chúng tôi viết:
$obj.x = 42;
Đây không phải là cú pháp đường: nó thực sự là một cuộc gọi phương thức, và sau đó toán tử gán được áp dụng cho kết quả của nó. Điều này hoạt động được, bởi vì lệnh gọi phương thức trả về vùng Scalar
chứa của thuộc tính, sau đó có thể được gán vào. Người ta có thể sử dụng ràng buộc để chia điều này thành hai bước, để xem nó không phải là một biến đổi cú pháp tầm thường. Ví dụ: cái này:
my $target := $obj.x;
$target = 42;
Sẽ được gán cho thuộc tính đối tượng. Cơ chế tương tự này đứng sau nhiều tính năng khác, bao gồm cả việc gán danh sách. Ví dụ: cái này:
($x, $y) = "foo", "bar";
Hoạt động bằng cách xây dựng một List
chứa đựng $x
và $y
, và sau đó là toán tử gán trong trường hợp này lặp mỗi cặp bên này sang làm nhiệm vụ. Điều này có nghĩa là chúng ta có thể sử dụng các bộ truy cập rw
đối tượng ở đó:
($obj.x, $obj.y) = "foo", "bar";
Và tất cả chỉ tự nhiên hoạt động. Đây cũng là cơ chế đằng sau việc gán cho các lát của mảng và hàm băm.
Người ta cũng có thể sử dụng Proxy
để tạo một thùng chứa giá trị l trong đó hành vi đọc và viết nó nằm dưới sự kiểm soát của bạn. Vì vậy, bạn có thể đặt các hành động phụ vào STORE
. Tuy nhiên...
Raku khuyến khích các phương pháp ngữ nghĩa trên "setters"
Khi chúng tôi mô tả OO, các thuật ngữ như "đóng gói" và "ẩn dữ liệu" thường xuất hiện. Ý tưởng chính ở đây là mô hình trạng thái bên trong đối tượng - nghĩa là cách nó chọn để biểu diễn dữ liệu cần thiết để thực hiện các hành vi của nó (các phương thức) - ví dụ như được phát triển miễn phí để xử lý các yêu cầu mới. Đối tượng càng phức tạp, điều này càng trở nên tự do.
Tuy nhiên, getters và setters là các phương thức có một kết nối ngầm với nhà nước. Mặc dù chúng tôi có thể cho rằng chúng tôi đang ẩn dữ liệu vì chúng tôi đang gọi một phương thức, không truy cập trực tiếp vào trạng thái, nhưng kinh nghiệm của tôi là chúng tôi nhanh chóng kết thúc tại một nơi mà mã bên ngoài đang thực hiện các chuỗi lệnh gọi setter để đạt được một hoạt động - đó là một hình thức của tính năng chống mẫu ghen tị. Và nếu chúng ta đang làm điều đó , chắc chắn chúng ta sẽ kết thúc bằng logic bên ngoài đối tượng thực hiện pha trộn các hoạt động getter và setter để đạt được một hoạt động. Thực sự, các hoạt động này đã được đưa ra như các phương thức với một tên mô tả những gì đang đạt được. Điều này càng trở nên quan trọng hơn nếu chúng ta ở trong một môi trường đồng thời; một đối tượng được thiết kế tốt thường khá dễ bảo vệ ở ranh giới phương thức.
Điều đó nói rằng, nhiều cách sử dụng class
thực sự là các loại bản ghi / sản phẩm: chúng tồn tại để đơn giản nhóm lại với nhau một loạt các mục dữ liệu. Không phải ngẫu nhiên mà .
sigil không chỉ tạo ra một phụ kiện, mà còn:
- Mở thuộc tính thành được thiết lập bởi logic khởi tạo đối tượng mặc định (nghĩa là a
class Point { has $.x; has $.y; }
có thể được khởi tạo là Point.new(x => 1, y => 2)
) và cũng biểu hiện điều đó trong .raku
phương thức kết xuất.
- Mở thuộc tính vào
.Capture
đối tượng mặc định , có nghĩa là chúng ta có thể sử dụng nó trong việc hủy hoại (ví dụ sub translated(Point (:$x, :$y)) { ... }
).
Đó là những điều bạn muốn nếu bạn đang viết theo phong cách thủ tục hoặc chức năng hơn và sử dụng class
như một phương tiện để xác định loại bản ghi.
Thiết kế Raku không được tối ưu hóa để thực hiện những điều thông minh trong setters, vì đó được coi là một điều kém để tối ưu hóa. Nó vượt quá những gì cần thiết cho một loại hồ sơ; trong một số ngôn ngữ, chúng tôi có thể lập luận rằng chúng tôi muốn xác thực những gì được chỉ định, nhưng trong Raku, chúng tôi có thể chuyển sangsubset
các loại cho điều đó. Đồng thời, nếu chúng ta thực sự thực hiện một thiết kế OO, thì chúng ta muốn có một API các hành vi có ý nghĩa che giấu mô hình trạng thái, thay vì suy nghĩ về các getters / setters, điều này có xu hướng dẫn đến thất bại trong việc colocate dữ liệu và hành vi, đó là phần lớn của việc thực hiện OO.