Nhà nước, Nhà nước đột biến và Nhà nước bất biến là gì?


32

Đây là một câu hỏi dành cho người mới, nhưng tôi không thể tìm thấy câu trả lời đủ bằng chứng cho người mới trên Google.

Mọi người có ý nghĩa gì khi họ nói 'trạng thái' - trong lập trình nói chung và lập trình OO nói riêng?

Ngoài ra, trạng thái có thể thay đổi và bất biến - một lần nữa, nói chung là trong lập trình và cụ thể là trong OOP là gì?


4
Chia sẻ nghiên cứu của bạn giúp mọi người . Hãy cho chúng tôi những gì bạn đã cố gắng và tại sao nó không đáp ứng nhu cầu của bạn. Điều này chứng tỏ rằng bạn đã dành thời gian để cố gắng tự giúp mình, nó giúp chúng tôi tránh nhắc lại các câu trả lời rõ ràng và hầu hết nó giúp bạn có được câu trả lời cụ thể và phù hợp hơn. Xem thêm Cách hỏi
gnat

Câu trả lời:


46

Bạn có trạng thái khi bạn liên kết các giá trị (số, chuỗi, cấu trúc dữ liệu phức tạp) với một danh tính và một thời điểm.

Ví dụ, số 10 tự nó không đại diện cho bất kỳ trạng thái nào: nó chỉ là một số được xác định rõ và sẽ luôn là chính nó: số tự nhiên 10. Như một ví dụ khác, chuỗi "HELLO" là một chuỗi gồm năm ký tự và nó được mô tả hoàn toàn bởi các ký tự mà nó chứa và trình tự xuất hiện. Trong năm triệu năm nữa, chuỗi "HELLO" vẫn sẽ là chuỗi "HELLO": một giá trị thuần túy.

Để có trạng thái, bạn phải xem xét một thế giới trong đó các giá trị thuần túy này được liên kết với một số loại thực thể có bản sắc . Danh tính là một ý tưởng nguyên thủy: nó có nghĩa là bạn có thể phân biệt hai thứ bất kể thuộc tính nào khác mà chúng có thể có. Ví dụ: hai chiếc xe cùng mẫu, cùng màu, ... là hai chiếc xe khác nhau.

Với những điều này với danh tính, bạn có thể đính kèm các thuộc tính cho chúng, được mô tả bằng các giá trị thuần túy. Ví dụ, xe của tôi có tài sản là màu xanh. Bạn có thể mô tả sự thật này bằng cách liên kết các cặp

("colour", "blue")

đến xe của tôi Cặp ("màu", "màu xanh") là một giá trị thuần túy mô tả trạng thái của chiếc xe cụ thể đó.

Nhà nước không chỉ liên quan đến một thực thể cụ thể, mà còn liên quan đến một thời điểm cụ thể. Vì vậy, bạn có thể nói rằng hôm nay, xe của tôi có trạng thái

("colour", "blue")

Ngày mai tôi sẽ sơn lại màu đen và trạng thái mới sẽ là

("colour", "black")

Lưu ý rằng trạng thái của một thực thể có thể thay đổi, nhưng danh tính của nó không thay đổi theo định nghĩa. Tất nhiên, miễn là thực thể tồn tại, tất nhiên: một chiếc xe có thể được tạo ra và phá hủy, nhưng nó sẽ giữ bản sắc của nó trong suốt cuộc đời của nó. Không có nghĩa gì để nói về danh tính của một cái gì đó chưa tồn tại / nữa.

Nếu các giá trị của các thuộc tính gắn liền với một thực thể nhất định thay đổi theo thời gian, bạn nói rằng trạng thái của thực thể đó là có thể thay đổi . Nếu không, bạn nói rằng nhà nước là bất biến .

Việc triển khai phổ biến nhất là lưu trữ trạng thái của một thực thể trong một số loại biến (biến toàn cục, biến thành viên đối tượng), tức là lưu trữ ảnh chụp nhanh hiện tại của trạng thái. Trạng thái có thể thay đổi sau đó được triển khai bằng cách sử dụng phép gán: mỗi thao tác gán thay thế ảnh chụp nhanh trước đó bằng một ảnh mới. Giải pháp này thường sử dụng các vị trí bộ nhớ để lưu trữ ảnh chụp nhanh hiện tại. Ghi đè vị trí bộ nhớ là một thao tác phá hủy thay thế ảnh chụp nhanh bằng một ảnh mới. ( Ở đây bạn có thể tìm thấy một cuộc nói chuyện thú vị về phương pháp lập trình hướng địa điểm này .)

Một cách khác là xem các trạng thái tiếp theo (lịch sử) của một thực thể dưới dạng luồng (có thể là chuỗi vô hạn) của các giá trị, xem ví dụ Chương 3 của SICP . Trong trường hợp này, mỗi ảnh chụp nhanh được lưu trữ tại một vị trí bộ nhớ khác nhau và chương trình có thể kiểm tra các ảnh chụp nhanh khác nhau cùng một lúc. Ảnh chụp nhanh không sử dụng có thể được thu gom rác khi không còn cần thiết.

Ưu điểm / nhược điểm của hai phương pháp

  • Cách tiếp cận 1 tiêu thụ ít bộ nhớ hơn và cho phép xây dựng một ảnh chụp nhanh mới hiệu quả hơn vì nó không liên quan đến việc sao chép.
  • Cách tiếp cận 1 ngầm đẩy trạng thái mới đến tất cả các phần của chương trình có tham chiếu đến nó, cách tiếp cận 2 sẽ cần một số cơ chế để đẩy ảnh chụp nhanh đến các quan sát viên của nó, ví dụ như dưới dạng một sự kiện.
  • Cách tiếp cận 2 có thể giúp ngăn ngừa các lỗi trạng thái không nhất quán (ví dụ: cập nhật trạng thái một phần): bằng cách xác định một hàm rõ ràng tạo ra trạng thái mới từ trạng thái cũ, việc phân biệt giữa các ảnh chụp nhanh được tạo ra tại các thời điểm khác nhau sẽ dễ dàng hơn.
  • Cách tiếp cận 2 mang tính mô đun hơn ở chỗ nó cho phép dễ dàng tạo ra các khung nhìn về trạng thái độc lập với chính trạng thái, ví dụ như sử dụng các hàm bậc cao hơn như mapfilter.

1
Lưu ý rằng các đối tượng không phải là những thứ duy nhất có trạng thái. Nếu một chương trình sử dụng các biến toàn cục (có thể thay đổi), thì chính chương trình đó được cho là có trạng thái. Tương tự, nếu một hàm có một biến ghi nhớ các giá trị trong các lệnh gọi hàm, thì hàm đó là trạng thái.
Doval

2
@Doval: Bạn có thể nghĩ trạng thái toàn cầu là trạng thái của một đối tượng thế giới toàn cầu. Theo tôi biết, chế độ xem này được sử dụng, ví dụ như trong Ruby. Một hàm nhớ trạng thái đẳng cấu với một đối tượng chỉ bằng một phương thức. Ý tưởng cơ bản phổ biến là bạn liên kết các giá trị với danh tính hoặc địa điểm, tức là những thứ nhất định có thể giữ các giá trị (có thể là giá trị có thể thay đổi) nhưng vẫn giữ được danh tính của chúng.
Giorgio

3
Chắc chắn, tôi đồng ý về nguyên tắc. Tôi chỉ chắc chắn rằng Prog hiểu rằng trạng thái không phải là thứ dành riêng cho OOP. Tôi không nghĩ dòng suy nghĩ "mọi thứ là một đối tượng" xuất hiện một cách tự nhiên.
Doval

@Doval: Bạn đã đề cập đến các hàm trạng thái ghi nhớ các giá trị qua các cuộc gọi khác nhau. Một ví dụ tôi có thể nghĩ là các biến cục bộ tĩnh trong C. Một ví dụ khác là các bao đóng (các hàm nắm bắt các biến được xác định trong ngữ cảnh của chúng). Các bao đóng có phần kép đối với các đối tượng: bao đóng là một đối tượng có chính xác một phương thức, trong khi một đối tượng là một tập hợp các bao đóng được xác định trên cùng các biến. Bạn có thể biết tất cả điều này nhưng tôi muốn tóm tắt nó ở đây. Nói chung, bạn có thể lưu trữ trạng thái ở một số vị trí bộ nhớ và truy cập nó bằng các cơ chế khác nhau, như bạn đã chỉ ra.
Giorgio

11

Nhà nước chỉ đơn giản là thông tin về một cái gì đó được giữ trong bộ nhớ.

Là một bài tập đơn giản trong định hướng đối tượng, hãy nghĩ về một lớp như một trình cắt cookie và cookie là các đối tượng. Bạn có thể tạo cookie (khởi tạo một đối tượng) bằng cách sử dụng trình cắt cookie (lớp). Giả sử một trong những thuộc tính của cookie là màu sắc của nó (có thể thay đổi bằng cách sử dụng màu thực phẩm). Màu sắc của cookie đó là một phần trạng thái của nó, cũng như các thuộc tính khác.

Trạng thái có thể thay đổi là trạng thái có thể thay đổi sau khi bạn tạo đối tượng (cookie). Trạng thái bất biến là trạng thái không thể thay đổi.

Các đối tượng không thay đổi ( không có trạng thái nào có thể thay đổi) trở nên quan trọng khi bạn xử lý đồng thời, khả năng nhiều bộ xử lý trong máy tính của bạn hoạt động cùng lúc với đối tượng đó. Tính không thay đổi đảm bảo rằng bạn có thể dựa vào trạng thái ổn định và hợp lệ trong suốt cuộc đời của đối tượng.

Nói chung, trạng thái của một đối tượng được giữ trong "các biến riêng tư hoặc thành viên" và được truy cập thông qua "các thuộc tính" hoặc các phương thức getter / setter.


3
Vì lợi ích của Prog, thực tế là một giá trị không bao giờ thay đổi cũng rất quan trọng vì lý do dễ dàng hơn nhiều. Nó có thể được sử dụng trong nhiều chức năng / phương thức như bạn muốn và bạn biết họ không thể thay đổi nó. Với trạng thái có thể thay đổi, bạn phải theo dõi lịch sử về cách đối tượng được sử dụng để tìm ra giá trị của nó hiện tại . Đó là chi phí tinh thần không cần thiết nếu làm cho nó bất biến không làm phức tạp chương trình.
Doval

Cảm ơn đã trả lời. Vì vậy, về cơ bản, trong OOP, khi ai đó nói 'trạng thái', họ thường có nghĩa là "biến thành viên của đối tượng"? Nếu vậy, 'trạng thái có thể thay đổi' là các biến công khai, hoặc phổ biến hơn trong OOP, các biến riêng tư có thể được thay đổi thông qua các phương thức setter - trong khi 'trạng thái bất biến' chỉ đơn giản là các biến thành viên riêng?
Aviv Cohn

1
Tính không thay đổi có thể được mô phỏng bằng cách đơn giản là không bao giờ viết thư cho các thành viên riêng của đối tượng một khi chúng được điền với các giá trị ban đầu. Tính bất biến có thể được thi hành bằng một số phương thức: không cung cấp phương thức setter, yêu cầu giá trị ban đầu được đặt bằng tham số hàm tạo, viết theo kiểu chức năng, sử dụng hằng số, v.v.
Robert Harvey

1
Tôi nghĩ về nhà nước như giá trị của một số tài sản của một số thực thể. "Vận chuyển" là một trạng thái. "Thuế suất" cũng vậy. Trọng lượng của một cái gì đó là một trạng thái. Cho dù bạn đang thức hay ngủ là một trạng thái. Màu sắc của một cái gì đó là một trạng thái. Thông tin có ý nghĩa về một cái gì đó, được giữ trong một số loại bộ nhớ máy tính.
Robert Harvey

1
Trong nhiều ngôn ngữ, tính bất biến có thể được thực thi bằng cách khai báo các biến thành viên là "const" hoặc "cuối cùng". Các biến như vậy chỉ có thể được khởi tạo bởi các nhà xây dựng. Đừng cho rằng các biến riêng là bất biến - chúng vẫn có thể được sửa đổi bởi các hàm thành viên (phương thức) của lớp.
Simon B

7

Tôi nghĩ thuật ngữ "trạng thái" (trái ngược với một loại trạng thái cụ thể như "biến thành viên") là hữu ích nhất khi so sánh API trạng thái với trạng thái không trạng thái. Cố gắng xác định "trạng thái" mà không đề cập đến API cũng giống như cố gắng xác định "biến" hoặc "hàm" mà không đề cập đến ngôn ngữ lập trình; hầu hết các câu trả lời đúng chỉ có ý nghĩa với những người đã biết những từ đó có nghĩa gì.

Stateful vs Statless

  • Một stateful API là một trong đó "nhớ" những gì các chức năng bạn đã gọi cho đến nay và với những gì tranh cãi, vì vậy lần sau khi bạn gọi một chức năng nó sẽ sử dụng thông tin đó. Phần "ghi nhớ" thường được thực hiện với các biến thành viên, nhưng đó không phải là cách duy nhất.
  • Một stateless API là một trong những nơi mọi cuộc gọi chức năng phụ thuộc hoàn toàn vào các đối số được truyền cho nó, và không có gì khác.

Ví dụ, OpenGL có lẽ là API trạng thái nhất mà tôi biết. Nếu tôi có thể lố bịch quá mức trong một lúc, chúng ta có thể nói nó trông giống như thế này:

glSetCurrentVertexBufferArray(vba1);
glSetCurrentVertexBufferObject(vbo1);
glSetCurrentVertexShader(vert1);
glSetCurrentFragmentShader(frag1);
// a dozen other things
glActuallyDrawStuffWithCurrentState(GL_TRIANGLES);

Hầu như mọi chức năng chỉ được sử dụng để vượt qua trong một số trạng thái OpenGL cần ghi nhớ, sau đó, cuối cùng bạn gọi một chức năng đơn giản đối nghịch để thực hiện tất cả các bản vẽ.

Một phiên bản không trạng thái của OpenGL (quá cỡ) có thể sẽ trông giống như thế này:

glActuallyDrawStuff(vba1, vbo1, vert1, frag1, /* a dozen other things */, GL_TRIANGLES);

Bạn sẽ thường nghe mọi người nói rằng các API có ít trạng thái dễ lý do hơn. Nếu bạn có thể giữ số lượng đối số trong tầm kiểm soát, tôi thường đồng ý với điều đó.

Mutable vs bất biến

Theo tôi biết, sự khác biệt này chỉ có ý nghĩa khi bạn có thể chỉ định trạng thái ban đầu . Ví dụ: sử dụng các hàm tạo C ++:

// immutable state
ImmutableWindow windowA = new ImmutableWindow(600, 400);
windowA = new ImmutableWindow(800, 600); // to change the size, I need a whole new window

// mutable state
MutableWindow windowB = new MutableWindow(600, 400);
windowB.width = 800; // to change the size, I just alter the existing object
windowB.height = 600;

Thật khó để thực hiện một lớp cửa sổ không "nhớ" kích thước của nó, nhưng bạn có thể quyết định liệu người dùng có thể thay đổi kích thước của cửa sổ hay không sau khi tạo.

PS Trong OOP đúng là "trạng thái" thường có nghĩa là "biến thành viên", nhưng nó có thể nhiều hơn thế. Chẳng hạn, trong C ++, một phương thức có thể có một biến tĩnh và lambdas có thể trở thành các bao đóng bằng cách bắt các biến. Trong cả hai trường hợp, các biến đó vẫn tồn tại qua nhiều lệnh gọi đến hàm và do đó có thể đủ điều kiện là trạng thái. Các biến cục bộ trong một hàm thông thường cũng có thể được coi là trạng thái tùy thuộc vào cách chúng được sử dụng (những biến tôi có trong hàm main () thường được tính).


Câu trả lời TUYỆT VỜI. Cảm ơn bạn rất nhiều, bạn thực sự đã giúp tôi nhận điều này nhanh chóng. Tôi ít biết, tôi đã làm việc với nó trong một thời gian dài và không biết nó được gọi là gì.
the_endian

2

Trong lời nói của giáo dân

Các từ điển quốc gia:

a. Một điều kiện hoặc phương thức tồn tại, liên quan đến hoàn cảnh.

  1. trạng thái - cách một cái gì đó liên quan đến các thuộc tính chính của nó;

Trạng thái của một cái gì đó là tập hợp các giá trị mà các thuộc tính của nó có trong bất kỳ thời điểm nào.

Trong OOP, trạng thái của một đối tượng là một ảnh chụp nhanh về giá trị của các thuộc tính của nó trong bất kỳ thời điểm nào.

Thing t = new Thing();
t.setColor("blue");
t.setPrice(100)
t.setSize("small");

Trạng thái của nó là màu xanh lam, giá của nó là 100 và kích thước của nó là nhỏ.

Nếu sau này bạn làm:

t.setColor("red");

Bạn thay đổi một trong các thuộc tính của nó nhưng bạn cũng thay đổi toàn bộ trạng thái vì đối tượng không còn giống như trước.

Đôi khi các lớp được thiết kế để giá trị thuộc tính của chúng không thể thay đổi sau khi được tạo. Tất cả các giá trị thuộc tính của chúng được chuyển đến hàm tạo hoặc đọc từ một số nguồn như cơ sở dữ liệu hoặc tệp, nhưng không có cách nào để thay đổi các giá trị đó sau thời điểm đó, vì không có phương thức "setter" hoặc bất kỳ cách nào khác của thay đổi các giá trị bên trong đối tượng.

Thing t = new Thing("red",100,"small");
t.setColor("blue") -->> ERROR, the programmer didn't provide a setter or any other way to change the properties values after initialization.

Đó gọi là trạng thái không thể thay đổi của đột biến. Tất cả những gì bạn có thể làm là phá hủy đối tượng, tạo một đối tượng mới và khẳng định nó với cùng một tham chiếu hoặc biến.

Thing t = new Thing("red",100,"small");
t = new Thing("blue",100,"small");
// I had to create a new Thing with another color since this thing is inmutable.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.