Các loại nói chung không thay đổi được tạo bằng các ngôn ngữ không xoay quanh tính bất biến sẽ có xu hướng tốn nhiều thời gian của nhà phát triển hơn cũng như có khả năng sử dụng nếu chúng yêu cầu một số loại đối tượng "trình tạo" để thể hiện các thay đổi mong muốn (điều này không có nghĩa là tổng thể công việc sẽ nhiều hơn, nhưng có một chi phí trả trước trong những trường hợp này). Ngoài ra, bất kể ngôn ngữ có giúp dễ dàng tạo các loại bất biến hay không, nó sẽ có xu hướng luôn yêu cầu một số chi phí xử lý và bộ nhớ cho các loại dữ liệu không tầm thường.
Làm cho các chức năng không có tác dụng phụ
Nếu bạn đang làm việc trong các ngôn ngữ không xoay quanh tính bất biến, thì tôi nghĩ rằng phương pháp thực dụng không phải là tìm cách làm cho mọi loại dữ liệu đơn lẻ trở nên bất biến. Một tư duy có năng suất cao hơn nhiều mang lại cho bạn nhiều lợi ích tương tự là tập trung vào tối đa hóa số lượng chức năng trong hệ thống của bạn mà không gây ra tác dụng phụ .
Một ví dụ đơn giản, nếu bạn có một chức năng gây ra tác dụng phụ như thế này:
// Make 'x' the absolute value of itself.
void make_abs(int& x);
Sau đó, chúng ta không cần một kiểu dữ liệu số nguyên bất biến cấm các toán tử như gán sau khởi tạo để làm cho hàm đó tránh được các tác dụng phụ. Chúng ta chỉ đơn giản có thể làm điều này:
// Returns the absolute value of 'x'.
int abs(int x);
Bây giờ chức năng không gây rối với x
hoặc bất cứ điều gì ngoài phạm vi của nó, và trong trường hợp tầm thường này, chúng tôi thậm chí có thể đã cạo một số chu kỳ bằng cách tránh bất kỳ chi phí nào liên quan đến việc xác định / răng cưa. Ít nhất thì phiên bản thứ hai không nên đắt hơn tính toán so với phiên bản đầu tiên.
Những thứ đắt tiền để sao chép đầy đủ
Tất nhiên hầu hết các trường hợp không phải là tầm thường này nếu chúng ta muốn tránh làm cho một chức năng gây ra tác dụng phụ. Một trường hợp sử dụng trong thế giới thực phức tạp có thể giống như thế này:
// Transforms the vertices of the specified mesh by
// the specified transformation matrix.
void transform(Mesh& mesh, Matrix4f matrix);
Tại thời điểm đó, lưới có thể cần vài trăm megabyte bộ nhớ với hơn một trăm nghìn đa giác, thậm chí nhiều đỉnh và cạnh hơn, nhiều bản đồ kết cấu, mục tiêu hình thái, v.v ... Sẽ rất tốn kém khi sao chép toàn bộ lưới chỉ để thực hiện điều này transform
Chức năng không có tác dụng phụ, như vậy:
// Returns a new version of the mesh whose vertices been
// transformed by the specified transformation matrix.
Mesh transform(Mesh mesh, Matrix4f matrix);
Và trong những trường hợp này, việc sao chép toàn bộ một cái gì đó thường sẽ là một chi phí sử thi mà tôi thấy hữu ích khi biến Mesh
thành một cấu trúc dữ liệu bền vững và một loại bất biến với "trình xây dựng" tương tự để tạo ra các phiên bản sửa đổi của nó để nó được sửa đổi có thể chỉ đơn giản là bản sao nông và phần tham chiếu không phải là duy nhất. Đó là tất cả với trọng tâm là có thể viết các hàm lưới không có tác dụng phụ.
Cấu trúc dữ liệu liên tục
Và trong những trường hợp sao chép mọi thứ rất tốn kém, tôi đã thấy nỗ lực của việc thiết kế một thứ bất biến Mesh
để thực sự được đền đáp mặc dù nó có chi phí hơi cao, vì nó không đơn giản hóa sự an toàn của luồng. Nó cũng đơn giản hóa việc chỉnh sửa không phá hủy (cho phép người dùng tạo lớp hoạt động của lưới mà không sửa đổi bản sao gốc của mình), hoàn tác các hệ thống (giờ đây hệ thống hoàn tác chỉ có thể lưu trữ một bản sao bất biến của lưới trước những thay đổi được thực hiện bởi một thao tác mà không làm nổ bộ nhớ sử dụng) và an toàn ngoại lệ (hiện tại nếu có ngoại lệ xảy ra trong chức năng trên, chức năng này không phải quay lại và hoàn tác tất cả các tác dụng phụ của nó vì nó không gây ra bất kỳ sự khởi đầu nào).
Tôi có thể tự tin nói rằng trong những trường hợp này, thời gian cần thiết để làm cho các cấu trúc dữ liệu khổng lồ này tiết kiệm được nhiều thời gian hơn so với chi phí, vì tôi đã so sánh chi phí bảo trì của các thiết kế mới này so với các thiết kế cũ xoay quanh khả năng biến đổi và chức năng gây ra tác dụng phụ, và các thiết kế đột biến trước đây tốn nhiều thời gian hơn và dễ bị lỗi của con người hơn, đặc biệt là trong các lĩnh vực thực sự hấp dẫn các nhà phát triển bỏ bê trong thời gian khủng hoảng, như ngoại lệ - an toàn.
Vì vậy, tôi nghĩ rằng các loại dữ liệu bất biến thực sự có hiệu quả trong những trường hợp này, nhưng không phải mọi thứ đều phải được làm bất biến để làm cho phần lớn các chức năng trong hệ thống của bạn không có tác dụng phụ. Nhiều thứ đủ rẻ để chỉ cần sao chép đầy đủ. Ngoài ra, nhiều ứng dụng trong thế giới thực sẽ cần phải gây ra một số tác dụng phụ ở đây và đó (ít nhất là giống như lưu tệp), nhưng thông thường có nhiều chức năng hơn có thể không có tác dụng phụ.
Quan điểm của việc có một số loại dữ liệu bất biến đối với tôi là đảm bảo rằng chúng ta có thể viết số lượng hàm tối đa để không bị tác dụng phụ mà không phải chịu chi phí sử thi dưới dạng sao chép sâu các cấu trúc dữ liệu khổng lồ sang trái và phải khi chỉ có các phần nhỏ trong số họ cần phải được sửa đổi. Có các cấu trúc dữ liệu liên tục trong các trường hợp đó sau đó trở thành một chi tiết tối ưu hóa để cho phép chúng tôi viết các chức năng của mình để không có tác dụng phụ mà không phải trả chi phí sử thi để làm việc đó.
Chi phí bất biến
Bây giờ về mặt khái niệm các phiên bản có thể thay đổi sẽ luôn có lợi thế về hiệu quả. Luôn luôn có chi phí tính toán liên quan đến cấu trúc dữ liệu bất biến. Nhưng tôi thấy đó là một sự trao đổi xứng đáng trong các trường hợp tôi đã mô tả ở trên, và bạn có thể tập trung vào việc làm cho chi phí đủ tối thiểu trong tự nhiên. Tôi thích kiểu tiếp cận đó trong đó tính chính xác trở nên dễ dàng và tối ưu hóa trở nên khó hơn thay vì tối ưu hóa dễ hơn nhưng tính chính xác trở nên khó hơn. Nó gần như không làm mất uy tín để có mã hoạt động hoàn toàn chính xác khi cần thêm một số điều chỉnh mã không hoạt động chính xác ngay từ đầu cho dù nó có đạt được kết quả không chính xác nhanh như thế nào.