How to use state pattern correctly?

后端 未结 9 857
既然无缘
既然无缘 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 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

提交回复
热议问题