You are on page 1of 10
1670412026, 09:58 Stale Machines in Practice: Implementing Solutions for Real Challenges -DEV Community State Machines in Practice: Implementing Solutions for Real Challenges ¥computerscience#programming#pythonitcodenewbie If you've studied engineering, you've probably heard about State Machines. But beyond the theory, you might not have seen how they're used. Did you know that State Machines play a role in many everyday applications? In this article, we'll refresh our understanding of State Machines and explore their practical applications. Plus, we'll learn how to implement them quickly using Python. Ready to dive in? Let's go! ‘State’ is a common programming term that is experienced by all developers as they advance from beginning to intermediate-level programming. So, what exactly does the term "State" mean? In general, an object's state is merely the current snapshot of the object or a portion of it. Meanwhile, in computer science, a program's state is defined as its position about previously stored inputs. In this context, the term "state" is used in the same manner as it is in science: the state of an object, such as a gas, liquid, or solid, represents its current physical nature, and the state of a computer program reflects its current values or contents. The stored inputs are preserved as variables or constants in a computer program. While assessing the state of a program, developers might examine the values contained in these inputs. The state of the program may change while it runs - variables may change, and memory values may change. A control variable, such as one used in a loop, for example, changes the state of the program at each iteration. Examining the present state of a program may be used to test or analyze the codebase. In simpler systems, state management is frequently handled with if-else, if-then-else, try-catch statements, or boolean flags; however, this is useless when there are too many states imaginable in a program, They can lead to clunky, complicated code that is difficult to understand, maintain, and debug. One disadvantage of if-else-clauses or booleans is that they may get fairly extensive, and adding another state is difficult because it necessitates rewriting the code of many different classes. Let's build a video player for example: class Video: def _init_(self, source): self source = source seltis_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 nips: dev tolpcagativerma 8state-machines-r-practce-Implementng-solution-forseal-challenges-3176 sno ‘1042024, 09:56 Siale Machines in Pracce: Implementing Soluion for Real Challenges - DEV Community def pause(selt) if selfis_playing: # Make the call to pause the video self.is_playing = False selfis_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 selfis_paused # Make the call to stop the video seltis_playing = False self.is_paused = False else: raise Exception( ‘Cannot stop a video that is not playing or paused! ) The above code snippet is an if-else implementation of a simple video player application, where the three basic states are - playing, paused, and stopped. However, if we try to add more states, the code will rapidly become complex, bloated, repetitive, and hard to understand and test. Let's see what the code looks like when adding another state ‘rewind’: 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 selfis_paused or setfis_stopped or selt.is_rewinding: # Make the call to play the video selfis_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.’ ) nitps: dev tolpcagativerma 8state-machines-r-practce-Implementng-solution-forseal-hallenges-3176 2110 ‘1042024, 09:56 Siale Machines in Pracce: Implementing Soluion for Real Challenges - DEV Community # A video can only be paused when it is playing or rewinding def pause(self) if self.is_playing or selfis_rewinding: # Make the call to pause the video selfis_playing = False selfis_paused = True self.is_rewinding = False seltis_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 selfis_playing or self'is_paused or self.is_rewinding # Make the call to stop the video self.is_playing = False selfis_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\sel): if self.is_playing or selfis_paused: # Make the call to rewind the video selfis_playing = False selfis_paused = False selfis_stopped = False self.is_rewinding = True else: raise Exception( “Cannot rewind a video that is not playing or paused’ ) Without the state pattern, you'd have to examine the program's current state throughout the code, including the update and draw methods. If you want to add a fourth state, such as a settings screen, you'll have to update the code of many distinct classes, which is inconvenient. This is where the idea of state machines comes in handy. What is a state machine? State machines are not a novel concept in computer science; they are one of the basic design patterns utilized in the software business. It is more system-oriented than coding-oriented and is used to nips: dev tolpcagativerma 8state-machines-r-practce-Implementng-solution-forseal-challenges-3176 30 ‘1042024, 09:56 Siale Machines in Pracce: Implementing Soluion for Real Challenges - DEV Community model around use cases. Let's look at a simple real-life example of hiring a cab through Uber When you initially launch the program, it takes you to the home screen, where you type in your destination in the search area. Once the right location has been identified, Uber displays recommended travel options such as Pool, Premier, UberGo, Uber XL, and others, along with a pricing estimate. The trip is confirmed and a driver is assigned once you select the payment option and press the ‘Confirm’ button with the specified journey time if necessary. Uber now displays a map on which you can locate your driver. Screen 1 is the first screen that all users in this use case see, and itis self-contained. Screen 2 is reliant on Screen 1, and you will not be able to go to Screen 2 until you give accurate data on Screen 1. Likewise, screen 3 is dependent on screen 2, while screen 4 is dependent on screen 3. If neither you nor your driver cancels your trip, you'll be moved to screen 4, where you won't be able to plan another trip until your current one ends. Let's say it's raining severely and no driver accepts your trip or no available driver in your region is found to finish your travel; an error notification warning you of driver unavailability shows, and you remain on screen 3. You may still return to screen 2, screen 1, and even the very first screen. You are in a different step of the cab reservation process, and you may only go to the next level if a specified action in the current stage is successful. For example, if you input the wrong location on screen 1, you won't be able to proceed to screen 2, and you won't be able to proceed to screen 3 unless you pick a travel option on screen 2, but you may always return to the previous stage unless your trip is already booked. In the above example, we've divided the cab booking process into several activities, each of which may or may not be authorized to call another activity based on the status of the booking. A state machine is used to model this. In principle, each of these stages/states should be autonomous, with one summoning the next only after the current one has been finished, successfully or otherwise. In more technical words, the state machine enables us to split down a large complicated action into a succession of separate smaller activities, such as the cab booking activity in the preceding example. Events connect smaller tasks, and shifting from one state to another is referred to as transition. We normally conduct some actions after changing from one state to another, such as creating a booking in the back end, issuing an invoice, saving user analytics data, capturing booking data in a database, triggering payment after the trip is finished, and so on. Hence, the general formula for a state machine can be given as: Current State + Some Action / Event= Another State Let's see what a state machine designed for a simple video player application would look like: nitps: dev tolpcagativerma 8state-machines-r-practce-Implementng-solution-forseal-hallenges-3176 40 1670412026, 09:58 Stale Machines in Practice: Implementing Solutions for Real Challenges - DEV Community Player And we can implement it in code using transitions as follows 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), nitps: dev tolpcagativerma 8state-machines-r-practce-Implementng-solution-forseal-hallenges-3176 510 1670412026, 09:58 Stale Machines in Practice: Implementing Solutions for Real Challenges -DEV Community # Create the state machine self.machine = Machine{ model = self, transitions = transitions, initial = self STOPPED } def play(self): pass def pause(selt) pass def stop(self) pass Now, in case, we want to add another state, say rewind, we can do that easily as follows: from transitions import Machine class Video: hitps:iidevofpragatverma 8isate-machines in-practice-mplementing-slutions-for-ea-challenges-3176 610 1670412026, 09:58 Stale Machines in Practice: Implementing Solutions for Real Challenges -DEV Community # 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. Avideo 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(selt) pass def stop(self) pass def rewind(selt) pass nips: dev tolpcagativerma 8state-machines-r-practce-Implementng-solution-forseal-challenges-3176 70 1670412026, 09:58 Stale Machines in Practice: Implementing Solutions for Real Challenges -DEV Community Thus, we can see how state machines can simplify a complex implementation and save us from writing incorrect code. Having leamed the capabilities of state machines, it's now important to understand why and when to use state machines. Why & When to Use State Machines? State Machines can be utilized in applications that have distinct states. Each stage can lead to one or more subsequent states, as well as end the process flow. A State Machine employs user input or in- state computations to choose which state to enter next. Many applications necessitate an “initialize” stage, followed by a default state that allows for a wide range of actions, Previous and present inputs, as well as states, can all have an impact on the actions that are executed. Clean-up measures can then be carried out when the system is "shut down.” Astate machine can help us conceptualize and manage those units more abstractly if we can break down a hugely complex job into smaller, independent units, where we simply need to describe when a state can transition to another state and what happens when the transition occurs. We don't need to be concerned with how the transition occurs after setup. After that, we only need to think about when and what, not how. Furthermore, state machines let us see the entire state process in a very predictable way; once transitions are set, we don't have to worry about mismanagement or erroneous state transitions; the improper transition may occur only if the state machine is configured properly. We have a comprehensive view of all states and transitions in a state machine. if we don't use a state machine, we are either unable to visualize our systems in various possible states, or we are knowingly or unknowingly coupling our components tightly together, or we are writing many if-else conditions to simulate state transitions, which complicates unit and integration testing because we must ensure that all test cases are written to validate the possibility of all the conditions and branching used Advantages of State Machines State machines, in addition to their ability to develop decision-making algorithms, are functional forms of application planning. As applications get more complex, the need for effective design grows. State diagrams and flowcharts are useful and occasionally essential throughout the design process. State Machines are important not just for application planning but are also simple to create. Following are some of the major advantages of state machines in modern-day computing: It helps you eliminate hard coding conditions. On your behalf, the state machine abstracts all logic related to states and transitions. State machines often have a finite number of states with definite transitions, making it simple to identify which transition/data/event triggered the present state of a request. After establishing a state machine, developers may concentrate on building actions and preconditions. With sufficient validation and preconditioning, state machines restrict out-of-order operations. As in the Uber example, a driver cannot be rewarded until the voyage is done. State machines can be quite easy to maintain. The actions taken during each transition are logically independent of one another. As a result, the corresponding code can be isolated. State machines are less prone to change and are more stable. It becomes much easier to maintain such systems if the current and future use cases are very obvious. Disadvantages of State Machines Not everything about state machines is good, they can sometimes lead to drawbacks and challenges too. Here are some of the common problems with state machines: nips: dev tolpcagativerma 8state-machines-r-practce-Implementng-solution-forseal-challenges-3176 ano 1670412026, 09:58 Stale Machines in Practice: Implementing Solutions for Real Challenges -DEV Community State machines are usually synchronous. So, if you need asynchronous background API calls/job execution, you'll have to weigh the pros and cons carefully before deciding on the best option. The code can quickly get jumbled. Because state machines are data-driven, your product team may ask you to execute different transitions from the same state based on different data/input parameters. As a result, this type of demand may result in multiple transitions with a clumsy precondition check. It is entirely dependent on the product and the machine's current configuration, If you need to load balance state machine instances, go with the one that has persistence enabled; otherwise, you'll need to implement your persistence layer and necessary validations to ensure that multiple requests fired at separate state machine instances produce consistent results. Because there are few resources or communities dedicated to distinct state machine implementations, assistance may be limited once you've chosen a library. Things to keep in mind when using State Machines When using a state machine, your system should ideally have two logical components: the state machine/workflow system itself the business logic contained in one or more services. The state machine may be thought of as the infrastructure that drives state transitions; it verifies state transitions and executes configured actions before, during, and after a transition; however, it should not know what business logic is done in those actions. So, in general, it's a good idea to isolate the state machine from the core business logic by using correct abstractions; otherwise, managing the code would be a nightmare Here are some other real-life scenarios where we need to employ state machine logic with caution: A state machine is more than just states, transitions, and actions. It should also be capable of defining a border around a state change. A transition can only be successful in particular cases if it is triggered by a trustworthy system or user. There might be a variety of similar situations. As a consequence, we should be able to develop appropriate state transition guarding logic. We commonly end up with many processes for the same business entity that can run concurrently. In such cases, one process does not obstruct other workflows; they may or may not be triggered concurrently, but they may coexist; the second workflow may begin from one of the first workflow's eligible phases, after which it may branch off and work independently. This sort of use case is established by the business; not every organization will have it. In principle, workflow systems are independent of the business domain. As a consequence, in the same workflow system, many processes with no link to the same business organization can be established. They may have a shared or distinct starting point, depending on whether the workflow system enables multiple starting points. When numerous separate workflows are formed in the same workflow system, you get a global picture of all business processes running across various business entities in your system. Depending on the business use cases, different processes may also have certain identical stages. Practical or real-life use cases for state machines: Following are some of the practical applications that benefit from the concept of state machines in our daily lives: Single-page or tabbed dialogue boxes A tab in the conversation box represents each state. A user can initiate a state transition by selecting a certain tab. The status for each tab includes any actions that the user can take. A self-service banking machine (ATM). In this application, states such as waiting for user input, confirming the needed amount against the account balance, distributing the money, printing the nitps: dev tolpcagativerma 8state-machines-r-practce-Implementng-solution-forseal-hallenges-3176 et0 ‘1042024, 09:56 Siale Machines in Pracce: Implementing Soluion for Real Challenges - DEV Community receipt, and so on are all conceivable. A software that takes a single measurement, stores it in memory, and then waits for the user to take another action. This program's steps include waiting for user input, performing the measurement, recording the data, displaying the results, and so on, Configuring ETL Jobs, for example. State machines are commonly used for designing user interfaces. While designing a user interface, distinct user actions shift the user interface into separate processing segments. Each of these elements will function as a state in the State Machine. These segments can either lead to another segment for further processing or wait for another user event. In this scenario, the State Machine monitors the user to determine what action they should take next When you purchase something from an online e-commerce site, for example, it goes through various phases, such as Ordered, Packed, Shipped, Cancelled, Delivered, Paid, Refunded, and so on. The transition occurs automatically as things move through a warehouse or logistics center and are scanned at various stages, such as when a user cancels or wants a refund. Process testing is another typical application for State Machines. In this example, each stage of the process is represented by a state. Depending on the results of each state's exam, a separate state may be declared. This might happen frequently if the process under examination is thoroughly studied. Conclusion The notion of state machines is extremely useful in programming. It not only streamlines the process of developing more complicated use case apps but also reduces the development work necessary. It provides a more simple and elegant grasp of modern-day events and when correctly applied, may work miracles. If you find this insightful, do let me know your views in the comments. Also, any kind of feedback is welcome, In case you want to connect with me, follow the links below: nips: dev tolpcagativerma 8state-machines-r-practce-Implementng-solution-forseal-challenges-3176 10110

You might also like