How to use state pattern correctly?

后端 未结 9 696
既然无缘
既然无缘 2021-01-29 21:54

I\'ve encountered a few implementations of state pattern in my programming experience, and done a few. I\'ve seen them used in various scenarios (mostly UI and parsing). The tro

相关标签:
9条回答
  • 2021-01-29 21:59

    Most of the times, the states in a state pattern design are handling more that one state (or substates of the state) which makes them harder to maintain.

    If a state has any kind of selection in there, its mostly handling more than one state.

    I takes a lot of discipline to keep the states clean.

    A possible solution to this is to make more complex states statemachines themselves (HSM). This makes it a lot more readable at the upper level because it has to deal with fewer states.

    0 讨论(0)
  • 2021-01-29 21:59

    I am building a expression evaluator which has the ability to evaluate sets of elements. I found the state pattern very useful for discriminating what can and can't be done to a set depending on its state. ie: open,closed, inactive, active ect. FSM are very easy to draw and reduce the complexity of code by removing the need for huge blocks of ifelse statements to define what the feature should do depending on its enclosed attributes. It makes these conditions more explicit by making the conditions into classes. It's one of my favourite patterns so far.

    0 讨论(0)
  • 2021-01-29 22:06

    As you probably have read, the State Design Pattern is useful when state varies the behavior of some object whose composition includes that state. This implies the idea of a State abstract class, interface, or enumerated type - albeit depending on the language Duck Typing will do as well - that defines any common behavior and/or required methods.

    Key Aspects

    There are really two key aspects to consider in working with the state pattern: enumeration and transition. Enumeration simply means identifying the set of possible states (e.g. days of the week), or more abstractly the types of states (i.e. meta states) such as starting, ending, and in between for a workflow engine.Transition means deciding how to model movement between states where this is typically either done by capturing all possible transitions in a tabular representation (i.e. Finite State Machine) or make each state know its possible "transitions" to other states.

    Typically transitions go hand in hand with meta states because its not possible to know all states and relationships ahead of time in such a dynamic system where new states, and thus transitions, can be added at runtime. In addition, with the transition approach, certain behavior - notifications for instance - becomes part of the transition, instead of the state itself.

    Examples

    There are several scenarios I've either worked on or discussed where this is a use facility:

    1. Workflow
    2. Computer Game Opponent A.I.
    3. Process Orchestration

    By workflow I mean something like jBPM. Systems like this are concerned with commanding the right attention, of the right people, at the right time. They usually send lots of emails or some other sort of notification. And, the process they represent needs the ability to change as the organization changes, whereas the data being managed typically changes much more slowly.

    Computer Game Opponent A.I. is self explanatory. Not something I've written, but in conversation with those who have, these systems are usually self contained. In other words, unlike workflow, the game usually doesn't have the facility to alter the process used to control computer opponents.

    Process Orchestration is similar to workflow, but focused on system integration, instead of people interaction. The Apache Mule framework is an example. Here state can describe status (e.g. started, in process, ended) and type (e.g. ftp integration point, sql integration point).

    Conclusion

    Unlike other answers, I think state encapsulation is an excellent way to manage change in software systems. Done well, it facilitates those changes or enables users to do so at runtime. You make a tradeoff of more flexibility in exchange for increased implementation complexity. So such an approach is probably not useful for shopping cart for instance, where the behavior is probably very well known and not like to change. On the other hand when the process is subject to change, it makes a very good fit.

    0 讨论(0)
  • 2021-01-29 22:09

    You should use the state pattern, if you have a different behaviour for each state. Maybe you need to reconfigure the transitions on runtime. Another reason for using it is you might have to add more states later on.

    Imagine a boardgame like Chinese Checkers you have different GUI States for picking a Pawn, select a target slot and so on. In each state the GUI should behave differently, some inputs should be handled others ignored. Using a simple switch/case is possible but state pattern comes handy as the logic is encapsulated, related code is toghether. This makes it easier to introduce new states without affecting most/all of the other states (depending on who is responsible to set the transitions: either the state knows its outgoing transitions, or they could be given at runtime e.g. using the constructor).

    As you can see in this example, the GuiController uses an IGuiState Interface to change its behaviour on demand. An implementation can be seen here.

    The main pitfall is to use switch/case, when you need to be flexible. Since the indirection takes a bit more time, I would recommend that for a fixed amount of rather simple staates. I you have to implement a rather fast low level network protocol, that is usually to much overhead.

    0 讨论(0)
  • 2021-01-29 22:12

    Just my 2 cents, the state pattern always turns to be hard to maintain as it is hard to understand by those who has not coded it. I usually fallback to old standard array of function/method pointers, as I've in my old C experience. You just build a two dimensions array of function pointers with state/signal for lines/columns. Easier to understand. you have a class that manage that and you delegate to other class to handle complexity ...

    my2c

    0 讨论(0)
  • 2021-01-29 22:16

    It's just a difficult pattern to use well, but it can be highly effective when used well. Here's one example. Let's say you're implementing an iterator for a diverse collection. You want the conditional logic contained within the iterator so that you can easily alter its state & its behavior. You embed a state object in the iterator. Each object in the collection will pass its class to the iterator, which in turn passes the class to the State object.

    So let's say you're designing a web-watcher parental monitoring app. This app takes in all data coming in from the web - pictures, text (both input text and parsed HTML text), GPS coordinates (it's a phone app), ... all this diverse data from activity is taken in and stored in a diverse collection.

    You need to iterate over this diverse collection in a variety of ways, analyze it in a variety of ways, trigger parental flags, & do other things.

    In a certain mode the state object examines searched text strings, and compares their text with a list of flagged keywords. In another mode the state object examines the GPS coordinates & determines whether they fall within a certain radius of the home or the school in a certain time range. In another mode the state object takes any images and passes them through a machine learning algorithm that can identify nudity. And so on.

    It's nice and modular to have all this logic encapsulated in this state object, and to be able to just switch modes like this. You switch the mode, the state object - and the iterator - behave like a completely different class. Maybe the parent currently wishes to do a scan for whether their kid was at home at 4AM. So... there's a button within the app that allows them to specify a timeframe and hit "search". The state object will change its state to examine just the GPS coordinates & their timestamps, it will skip over the other data.

    Maybe there's a state defined in the app... where every single piece of information is analyzed. Complete monitoring. So in the state object, the state itself is implemented as a string of bits, and read as a bitmask. To monitor everything... well you just set this entire string of bits to 1s. Now your state object will check everything. On the other hand, maybe the parent feels that's too invasive. So the parent can configure the app such that it doesn't monitor location. Perhaps they configure it so that it only monitors for nudity. Ok... so all these bits are set to 0 except the one designated for nudity. Now your app only monitors nudity.

    This is really quite configurable, isn't it? Every combination of behavior you like can be easily set.

    So the parent is just monitoring for nudity... but wait! The kid is misbehaving. Now maybe the parent changes their mind, and decides... you know, this kid is out of control. I'd like to look more closely at what they've been doing on the internet over the past month. Are there any explicit search terms they've been using?

    No problem. You did collect that data, you just were skipping over it. So fine, just push a few buttons... reconfigure that state object, and it'll do what you want it to do.

    Another common use is in implementing dynamic commands. So lets say in world of warcraft... you've got these buttons. They're commands. You press them in combinations. So if you press attack... and then attack .5 seconds or less afterwards... you get a special 2nd attack. And actually there's a little icon on screen that changes after the first press, for a brief second, and then switches back. So the Command will implement its functionality with a State object. And the first command... will alter that State briefly, and it'll default back after 0.5 seconds have expired. The whole world of warcraft interface - series of buttons, attacks and so on - is very dynamic in this manner. All the commands are very state-dependent.
    What if your character upgraded, and learned a new attack? It's very easy to just modify the state object. But what if he changes his weapon...? And no longer can do that attack, which is only for the sword? Well the state object for axe, his new weapon, does not have that added functionality... it's all very modular.

    So yeah, with this ability to change functionality based on state you get alot of options and configurability that can be immediately implemented in the UI of this app. It's really quite powerful when used in the right context. Your original comment does touch on this - parsing and UI do seem to be a nice place where this can be used.

    Besides with streams, you can also just use it to indicate the type of an API. So it can be used to indicate which functionality is supported. From there, you may respond as you see fit. So let's say you've designed a remote PS4 controller that works with iphone games. Natively, iphones game engine has this State object that encapsulates the games native controls, and defines what buttons / operations the native controls support. Certain buttons are pressed, and the game controls State is changed accordingly (similar to WOW example kind of, but broader). Maybe some games use vibration feedback of the phone for certain button presses, others do not. Maybe you'd like to implement that as dual shock in your PS4 controller. So one particular game doesn't support vibration on any button states. Well ... you can tell that by just reading the state. If the game supports vibration on a button state... you know to activate dual shock on your controller. Hell, if a game doesn't support vibration you might even fix that by wrapping the state object in a decorator that implements dual shock in a way that interfaces with certain states appropriately.

    Problem with design patterns, in my experience... if used properly they're extremely powerful, they can enhance the functionality your app supports in ways that will just make them great. If used improperly, however, they just can totally convolute the code. Derail the entire design, make maintenance a hassle...

    Generally I prefer simple applications of design patterns that fit the specific problem. If the design is too complex, especially if you can't see how it fits the problem domain... then you have to begin asking yourself... did I implement this right? Is the added complexity actually worth what I'm dong here? What is this actually adding to the app...?

    That's all I've got. Best of luck to you in your design efforts

    0 讨论(0)
提交回复
热议问题