'Trạng thái' là một thuật ngữ lập trình phổ biến được tất cả các nhà phát triển trải nghiệm khi họ tiến bộ từ lập trình sơ cấp đến trung cấp. Vậy, chính xác thì thuật ngữ "Nhà nước" có nghĩa là gì?
Nói chung, trạng thái của một đối tượng chỉ đơn thuần là ảnh chụp nhanh hiện tại của đối tượng hoặc một phần của nó. Trong khi đó, trong khoa học máy tính, trạng thái của một chương trình được định nghĩa là vị trí của nó về các đầu vào đã được lưu trữ trước đó. Trong ngữ cảnh này, thuật ngữ "trạng thái" được sử dụng giống như trong khoa học: trạng thái của một vật thể, chẳng hạn như khí, lỏng hoặc rắn, biểu thị bản chất vật lý hiện tại của nó và trạng thái của một chương trình máy tính. phản ánh các giá trị hoặc nội dung hiện tại của nó.
Các đầu vào được lưu trữ được bảo toàn dưới dạng các biến hoặc hằng số trong chương trình máy tính. Trong khi đánh giá trạng thái của một chương trình, các nhà phát triển có thể kiểm tra các giá trị có trong các đầu vào này. Trạng thái của chương trình có thể thay đổi trong khi nó chạy - các biến có thể thay đổi và các giá trị bộ nhớ có thể thay đổi. Một biến điều khiển, chẳng hạn như một biến được sử dụng trong một vòng lặp, chẳng hạn, thay đổi trạng thái của chương trình tại mỗi lần lặp. Việc kiểm tra trạng thái hiện tại của một chương trình có thể được sử dụng để kiểm tra hoặc phân tích cơ sở mã.
Trong các hệ thống đơn giản hơn, quản lý trạng thái thường được xử lý bằng các câu lệnh if-else, if-then-else, try-catch hoặc cờ boolean; tuy nhiên, điều này là vô ích khi có quá nhiều trạng thái có thể tưởng tượng được trong một chương trình. Chúng có thể dẫn đến mã phức tạp, khó hiểu, khó hiểu, bảo trì và gỡ lỗi.
Một nhược điểm của các mệnh đề if-else-hoặc boolean là chúng có thể trở nên khá rộng và việc thêm một trạng thái khác là rất khó vì nó đòi hỏi phải viết lại mã của nhiều lớp khác nhau. Giả sử bạn muốn tạo một trò chơi có menu chính, vòng lặp trò chơi và màn hình hoàn thành.
Hãy xây dựng một trình phát video chẳng hạn:
class Video: def __init__(self, source): self.source = source self.is_playing = False self.is_paused = False self.is_stopped = True # A video can only be played when paused or stopped def play(self): if not self.is_playing or self.is_paused: # Make the call to play the video self.is_playing = True self.is_paused = False else: raise Exception( 'Cannot play a video that is already playing.' ) # A video can only be paused when it is playing def pause(self): if self.is_playing: # Make the call to pause the video self.is_playing = False self.is_paused = True else: raise Exception( 'Cannot pause a video that is not playing' ) # A video can only be stopped when it is playing or paused def stop(self): if self.is_playing or self.is_paused: # Make the call to stop the video self.is_playing = False self.is_paused = False else: raise Exception( 'Cannot stop a video that is not playing or paused' )
Đoạn mã trên là triển khai if-else của một ứng dụng trình phát video đơn giản, trong đó ba trạng thái cơ bản là - phát, tạm dừng và dừng. Tuy nhiên, nếu chúng ta cố gắng thêm nhiều trạng thái hơn, mã sẽ nhanh chóng trở nên phức tạp, cồng kềnh, lặp đi lặp lại và khó hiểu và khó kiểm tra. Hãy xem mã trông như thế nào khi thêm một trạng thái 'tua lại':
class Video: def __init__(self, source): self.source = source self.is_playing = False self.is_paused = False self.is_rewinding = False self.is_stopped = True # A video can only be played when it is paused or stopped or rewinding def play(self): if self.is_paused or self.is_stopped or self.is_rewinding: # Make the call to play the video self.is_playing = True self.is_paused = False self.is_stopped = False self.is_rewinding = False else: raise Exception( 'Cannot play a video that is already playing.' ) # A video can only be paused when it is playing or rewinding def pause(self): if self.is_playing or self.is_rewinding: # Make the call to pause the video self.is_playing = False self.is_paused = True self.is_rewinding = False self.is_stopped = False else: raise Exception( 'Cannot pause a video that is not playing or rewinding' ) # A video can only be stopped when it is playing or paused or rewinding def stop(self): if self.is_playing or self.is_paused or self.is_rewinding: # Make the call to stop the video self.is_playing = False self.is_paused = False self.is_stopped = True self.is_rewinding = False else: raise Exception( 'Cannot stop a video that is not playing or paused or rewinding' ) # 4. A video can only be rewinded when it is playing or paused. def rewind(self): if self.is_playing or self.is_paused: # Make the call to rewind the video self.is_playing = False self.is_paused = False self.is_stopped = False self.is_rewinding = True else: raise Exception( 'Cannot rewind a video that is not playing or paused' )
Nếu không có State-pattern, bạn sẽ phải kiểm tra trạng thái hiện tại của chương trình trong toàn bộ mã, bao gồm cả các phương pháp cập nhật và vẽ. Nếu bạn muốn thêm trạng thái thứ tư, chẳng hạn như màn hình cài đặt, bạn sẽ phải cập nhật mã của nhiều lớp riêng biệt, điều này thật bất tiện. Đây là lúc mà ý tưởng về các cỗ máy trạng thái trở nên hữu ích.
Máy trạng thái không phải là một khái niệm mới trong khoa học máy tính; chúng là một trong những mẫu thiết kế cơ bản được sử dụng trong kinh doanh phần mềm. Nó hướng về hệ thống hơn là định hướng mã hóa và được sử dụng để mô hình hóa các trường hợp sử dụng.
Hãy xem một ví dụ thực tế đơn giản về việc thuê taxi thông qua Uber:
Màn hình 1 là màn hình đầu tiên mà tất cả người dùng trong trường hợp sử dụng này nhìn thấy và nó là màn hình độc lập. Màn hình 2 phụ thuộc vào Màn hình 1 và bạn sẽ không thể chuyển đến Màn hình 2 cho đến khi bạn cung cấp dữ liệu chính xác trên Màn hình 1. Tương tự, màn hình 3 phụ thuộc vào màn hình 2, trong khi màn hình 4 phụ thuộc vào màn hình 3. Nếu không phải bạn cũng không phải tài xế của bạn hủy chuyến đi của bạn, bạn sẽ được chuyển sang màn hình 4, nơi bạn sẽ không thể lên kế hoạch cho một chuyến đi khác cho đến khi chuyến đi hiện tại của bạn kết thúc.
Giả sử trời đang mưa lớn và không có tài xế nào chấp nhận chuyến đi của bạn hoặc không tìm thấy tài xế nào có sẵn trong khu vực của bạn để kết thúc chuyến đi của bạn; thông báo lỗi cảnh báo bạn về tình trạng không có trình điều khiển hiển thị và bạn vẫn ở trên màn hình 3. Bạn vẫn có thể quay lại màn hình 2, màn hình 1 và thậm chí là màn hình đầu tiên.
Bạn đang ở một bước khác của quy trình đặt chỗ taxi và bạn chỉ có thể chuyển sang cấp độ tiếp theo nếu một hành động cụ thể trong giai đoạn hiện tại thành công. Ví dụ: nếu bạn nhập sai vị trí trên màn hình 1, bạn sẽ không thể chuyển sang màn hình 2 và bạn sẽ không thể tiếp tục màn hình 3 trừ khi bạn chọn một tùy chọn du lịch trên màn hình 2, nhưng bạn có thể luôn quay lại chặng trước trừ khi chuyến đi của bạn đã được đặt trước.
Trong ví dụ trên, chúng tôi đã chia quy trình đặt xe taxi thành nhiều hoạt động, mỗi hoạt động có thể có hoặc không được phép gọi một hoạt động khác dựa trên trạng thái của đặt xe. Một máy trạng thái được sử dụng để mô hình hóa điều này. Về nguyên tắc, mỗi giai đoạn / trạng thái này nên được tự trị, với một lần triệu hồi tiếp theo chỉ sau khi giai đoạn hiện tại đã hoàn thành, thành công hoặc bằng cách khác.
Nói cách kỹ thuật hơn, máy trạng thái cho phép chúng tôi chia nhỏ một hành động phức tạp lớn thành một chuỗi các hoạt động nhỏ hơn riêng biệt, chẳng hạn như hoạt động đặt xe taxi trong ví dụ trước.
Sự kiện kết nối các nhiệm vụ nhỏ hơn và chuyển từ trạng thái này sang trạng thái khác được gọi là quá trình chuyển đổi. Chúng tôi thường thực hiện một số hành động sau khi thay đổi từ trạng thái này sang trạng thái khác, chẳng hạn như tạo đặt chỗ ở phía sau, xuất hóa đơn, lưu dữ liệu phân tích người dùng, thu thập dữ liệu đặt chỗ trong cơ sở dữ liệu, kích hoạt thanh toán sau khi chuyến đi kết thúc, v.v. .
Do đó, công thức chung cho một máy trạng thái có thể được đưa ra là:
Trạng thái hiện tại + Một số hành động / sự kiện = Trạng thái khác
Hãy xem một máy trạng thái được thiết kế cho một ứng dụng trình phát video đơn giản sẽ trông như thế nào:
Và chúng ta có thể triển khai nó trong mã bằng cách sử dụng các chuyển đổi như sau:
from transitions import Machine class Video: # Define the states PLAYING = 'playing' PAUSED = 'paused' STOPPED = 'stopped' def __init__(self, source): self.source = source # Define the transitions transitions = [ # 1. A video can only be played when it is paused or stopped. {'trigger': 'play', 'source': self.PAUSED, 'dest': self.PLAYING}, {'trigger': 'play', 'source': self.STOPPED, 'dest': self.PLAYING}, # 2. A video can only be paused when it is playing. {'trigger': 'pause', 'source': self.PLAYING, 'dest': self.PAUSED}, # 3. A video can only be stopped when it is playing or paused. {'trigger': 'stop', 'source': self.PLAYING, 'dest': self.STOPPED}, {'trigger': 'stop', 'source': self.PAUSED, 'dest': self.STOPPED}, ] # Create the state machine self.machine = Machine{ model = self, transitions = transitions, initial = self.STOPPED } def play(self): pass def pause(self): pass def stop(self): pass
Bây giờ, trong trường hợp, chúng ta muốn thêm một trạng thái khác, chẳng hạn như tua lại, chúng ta có thể thực hiện điều đó một cách dễ dàng như sau:
from transitions import Machine class Video: # Define the states PLAYING = 'playing' PAUSED = 'paused' STOPPED = 'stopped' REWINDING = 'rewinding' # new def __init__(self, source): self.source = source # Define the transitions transitions = [ # 1. A video can only be played when it is paused or stopped. {'trigger': 'play', 'source': self.PAUSED, 'dest': self.PLAYING}, {'trigger': 'play', 'source': self.STOPPED, 'dest': self.PLAYING}, {'trigger': 'play', 'source': self.REWINDING, 'dest': self.PLAYING}, # new # 2. A video can only be paused when it is playing. {'trigger': 'pause', 'source': self.PLAYING, 'dest': self.PAUSED}, {'trigger': 'pause', 'source': self.REWINDING, 'dest': self.PAUSED}, # new # 3. A video can only be stopped when it is playing or paused. {'trigger': 'stop', 'source': self.PLAYING, 'dest': self.STOPPED}, {'trigger': 'stop', 'source': self.PAUSED, 'dest': self.STOPPED}, {'trigger': 'stop', 'source': self.REWINDING, 'dest': self.STOPPED}, # new # 4. A video can only be rewinded when it is playing or paused. {'trigger': 'rewind', 'source': self.PLAYING, 'dest': self.REWINDING}, #new {'trigger': 'rewind', 'source': self.PAUSED, 'dest': self.REWINDING}, # new ] # Create the state machine self.machine = Machine{ model = self, transitions = transitions, initial = self.STOPPED } def play(self): pass def pause(self): pass def stop(self): pass def rewind(self): pass
Do đó, chúng ta có thể thấy cách các máy trạng thái có thể đơn giản hóa một quá trình triển khai phức tạp và giúp chúng ta không viết sai mã. Sau khi học các khả năng của máy trạng thái, bây giờ điều quan trọng là phải hiểu tại sao và khi nào sử dụng máy trạng thái.
Máy trạng thái có thể được sử dụng trong các ứng dụng có trạng thái riêng biệt. Mỗi giai đoạn có thể dẫn đến một hoặc nhiều trạng thái tiếp theo, cũng như kết thúc quy trình. State Machine sử dụng đầu vào của người dùng hoặc tính toán ở trạng thái để chọn trạng thái sẽ vào tiếp theo.
Nhiều ứng dụng yêu cầu giai đoạn "khởi tạo", sau đó là trạng thái mặc định cho phép thực hiện một loạt các hành động. Các đầu vào trước đây và hiện tại, cũng như các trạng thái, đều có thể có tác động đến các hành động được thực thi. Các biện pháp làm sạch sau đó có thể được thực hiện khi hệ thống "tắt".
Một cỗ máy trạng thái có thể giúp chúng ta khái niệm và quản lý các đơn vị đó một cách trừu tượng hơn nếu chúng ta có thể chia nhỏ một công việc cực kỳ phức tạp thành các đơn vị nhỏ hơn, độc lập, nơi chúng ta chỉ cần mô tả khi nào một trạng thái có thể chuyển sang trạng thái khác và điều gì sẽ xảy ra khi quá trình chuyển đổi xảy ra. Chúng ta không cần quan tâm đến việc quá trình chuyển đổi diễn ra như thế nào sau khi thiết lập. Sau đó, chúng ta chỉ cần nghĩ về thời gian và cái gì, không phải như thế nào.
Hơn nữa, các máy trạng thái cho phép chúng ta xem toàn bộ tiến trình trạng thái theo cách rất dễ đoán trước; một khi quá trình chuyển đổi được thiết lập, chúng tôi không phải lo lắng về việc quản lý kém hoặc chuyển đổi trạng thái có sai sót; quá trình chuyển đổi không thích hợp chỉ có thể xảy ra nếu máy trạng thái được cấu hình đúng cách. Chúng tôi có một cái nhìn toàn diện về tất cả các trạng thái và quá trình chuyển đổi trong một máy trạng thái.
Nếu chúng ta không sử dụng máy trạng thái, chúng ta không thể hình dung hệ thống của mình ở các trạng thái có thể khác nhau, hoặc chúng ta cố ý hoặc vô tình kết hợp chặt chẽ các thành phần của chúng ta với nhau hoặc chúng ta đang viết nhiều điều kiện if-else để mô phỏng quá trình chuyển đổi trạng thái, làm phức tạp kiểm thử đơn vị và tích hợp vì chúng ta phải đảm bảo rằng tất cả các trường hợp kiểm thử được viết để xác nhận khả năng của tất cả các điều kiện và phân nhánh được sử dụng.
Máy trạng thái, ngoài khả năng phát triển các thuật toán ra quyết định, là các dạng chức năng của kế hoạch ứng dụng. Khi các ứng dụng ngày càng phức tạp, nhu cầu thiết kế hiệu quả ngày càng tăng.
Sơ đồ trạng thái và lưu đồ rất hữu ích và đôi khi cần thiết trong suốt quá trình thiết kế. Máy trạng thái không chỉ quan trọng đối với việc lập kế hoạch ứng dụng mà còn rất đơn giản để tạo ra.
Sau đây là một số ưu điểm chính của máy trạng thái trong máy tính hiện đại:
Không phải mọi thứ về máy trạng thái đều tốt, đôi khi chúng cũng có thể dẫn đến những hạn chế và thách thức. Dưới đây là một số sự cố thường gặp với máy trạng thái:
Khi sử dụng máy trạng thái, hệ thống của bạn lý tưởng nên có hai thành phần logic:
Máy trạng thái có thể được coi là cơ sở hạ tầng thúc đẩy quá trình chuyển đổi trạng thái; nó xác minh các chuyển đổi trạng thái và thực hiện các hành động được định cấu hình trước, trong và sau khi chuyển đổi; tuy nhiên, nó không nên biết logic nghiệp vụ nào được thực hiện trong những hành động đó.
Vì vậy, nói chung, bạn nên tách biệt cỗ máy trạng thái khỏi logic kinh doanh cốt lõi bằng cách sử dụng các phần trừu tượng hóa chính xác; nếu không, quản lý mã sẽ là một cơn ác mộng.
Dưới đây là một số tình huống thực tế khác mà chúng ta cần sử dụng logic máy trạng thái một cách thận trọng:
Sau đây là một số ứng dụng thực tế có lợi từ khái niệm máy trạng thái trong cuộc sống hàng ngày của chúng ta:
Ví dụ: khi bạn mua thứ gì đó từ một trang thương mại điện tử trực tuyến, nó sẽ trải qua nhiều giai đoạn khác nhau, chẳng hạn như Đã đặt hàng, Đóng gói, Đã giao hàng, Đã hủy, Đã giao, Đã thanh toán, Đã hoàn tiền, v.v. Quá trình chuyển đổi diễn ra tự động khi mọi thứ di chuyển qua nhà kho hoặc trung tâm hậu cần và được quét ở các giai đoạn khác nhau, chẳng hạn như khi người dùng hủy hoặc muốn hoàn lại tiền.
Khái niệm về trạng thái máy cực kỳ hữu ích trong lập trình. Nó không chỉ hợp lý hóa quá trình phát triển các ứng dụng ca sử dụng phức tạp hơn mà còn giảm bớt công việc phát triển cần thiết. Nó cung cấp một sự nắm bắt đơn giản và thanh lịch hơn về các sự kiện thời hiện đại và khi được áp dụng đúng cách, nó có thể mang lại những điều kỳ diệu.