I recently learned the State mode of the design mode. I have encountered the problem of HTML parsing, and I need to use the State machine for analysis. So I tried to use the State mode to write the HTML parsing process, although this is a bit of a cool smell, there are also a lot of gains in understanding the design model. I would like to share with you here.
I. HTML parsing and AnalysisIn HTML parsing, each character must be judged. The characters can be divided into the following types: <: >://=: letter: Number: underline: chinese characters are also used for comments <! -- And --> are not processed here. For the analysis of HTML can be analyzed using the status chart: 650) this. width = 650; "onclick = 'window. open (" http://blog.51cto.com/viewpic.php? Refimg = "+ this. src) 'alt = "" border = "0" forumid_144 = "" src = "http://oa.headware.cn: 8888/general/bbs2/attachments/forumid_144/large"/> figure 1) as you can see, the status of HTML is basically processing the tag name, tag value, and tag attribute. If the status is further subdivided, the tag name can be further divided into the left tag name and the right Tag name, and the attribute can be further divided into the attribute name and attribute value. Of course, there are still some situations, such as comments and some wrong data, these details are not considered for the moment. If a switch branch is used for processing, the code will be very complicated. At least two layers of switches should be used. The first layer is used to judge the characters currently read, and the second layer needs to judge the current status. The pseudocode is as follows:
- Do {
- Char ch = * itChar;
- Switch (ch)
- {
- Case '<':
- Switch (State)
- {
- Case start status:
- Start processing...
- Switch to left label status
- Break;
- Case tag value status:
- Tag value status processing...
- Break;
- }
- Break;
- Case '> ':
- Switch (State)
- {
- Case left label status:
- Processing left tag name status...
- Switch to the label name status
- Break;
- Case right Tag name status:
- Right Tag name status processing...
- Break;
- }
- Break;
- ...
- }
- } While (string reading has not ended)
Each branch on the second layer can be written as a function. If the status increases, the branch processing of case is required. Is there a better way to skip the switch statement? Of course, you will say that you can use loop processing. If you take a look at your ideas, you need to create a ing table for loop processing to switch the status. If you do not need to map tables, is there any good solution.
Ii. state ModeThe State mode in the design mode can solve this problem well. Of course, many classes are needed here. Let's first look at the diagram below
650) this. width = 650; "onclick = 'window. open (" http://blog.51cto.com/viewpic.php? Refimg = "+ this. src) 'alt = "" border = "0" forumid_144 = "" src = "http://oa.headware.cn: 8888/general/bbs2/attachments/forumid_144/large"/>
Figure 2) First, it is explained that at least three classes should be created here. Context, State, and ConcreteStateX: Context are responsible for processing the execution status, it contains a base class pointer of State *. Each time a State is processed, the Request is called, in the Request, the base class Pointer "State *" is used to call the Handle () of the ConcreteStateA or ConcreteStateB object (). We can write a state as a ConcreteStateX object and put the processing in Handle so that all the State processing can be completed by calling Request () cyclically. In this case, you may have to ask how to implement direct switching between the status and status? The key is here, because there is a State * in the Context, we only need to change the object referred to by State. So when can we change this pointer? We can put this processing in Handle. When we need to switch the status, call ChangeState (..., State: State *) method, pass the State object through the parameter for reference code), and then change the State * pointer in the Context. Note that, in the Context, State must be declared as firend. In this way, you can directly access the private field in the Context and reject other classes. We also added a method ChangeState In the Context. Of course, you can also assign values directly without calling this method. However, this method has some advantages. We will discuss it later. Here, you may ask, a State requires a class, so when there are more states, you need to create many class objects. How to manage so many class objects, A simple method is to implement each state class in a Singleton, so that you do not need to care about object creation and release. Another way is to use the factory mode to create and release these classes in a centralized manner. But do you consider the release stage? Where is the most suitable release. By the way, in the ChangeState we just mentioned, when we re-assign the state value, we can delete the previous State object. If it is a Singleton, we will call Release () method, which is good for State objects that require a large amount of memory. If you understand the above content, you can easily see the following code:
// State. h # ifndef _ STATE_H _ # define _ STATE_H _ class Context; // declare class State {public: State (); virtual ~ State (); virtual void Handle (Context *) = 0; protected: bool ChangeState (Context * con, State * st) ;}; class ConcreteStateA: public State {public: concreteStateA () {}; virtual ~ ConcreteStateA () {}; virtual void Handle (Context *) ;}; class ConcreteStateB: public State {public: ConcreteStateB () {}; virtual ~ ConcreteStateB () {}; virtual void Handle (Context *) ;};# endif //~ _ STATE_H _ |
// State. cpp # include "Context. h "# include <iostream> using namespace std; void State: Handle (Context * con) {cout <" State ::.. "<endl;} bool State: ChangeState (Context * con, State * st) {con-> ChangeState (st); return true;} void ConcreteStateA :: handle (Context * con) {cout <"ConcreteStateA: OperationInterface ...... "<endl; this-> ChangeState (con, new ConcreteStateB ();} void ConcreteStateB: Handle (Context * con) {cout <" ConcreteStateB :: operationInterface ...... "<endl; this-> ChangeState (con, new ConcreteStateA ());} |
// Context. h # ifndef _ CONTEXT_H _ # define _ CONTEXT_H _ class State; class Context {public: Context (); Context (State * state );~ Context (); void Request (); protected: private: friend class State; // indicates that the private field bool ChangeState (State * State) of the Context class can be accessed in the state class ); private: State * _ state;}; # endif //~ _ CONTEXT_H _ |
// Context. cpp # include "stdafx. h "# include" Context. h "# include" State. h "Context: Context () {} Context: Context (State * state) {this-> _ state = state;} Context ::~ Context () {delete _ state;} void Context: Request () {_ state-> Handle (this);} bool Context: ChangeState (State * state) {if (_ state) delete _ state; _ state = state; return true ;} |
// Test code int StateTest () {State * st = new ConcreteStateA (); Context * con = new Context (st); con-> Request (); con-> Request (); if (con! = NULL) delete con; if (st! = NULL) st = NULL; return 0 ;} |
The story tells us that it is not over yet. As shown in Figure 1), each status usually includes Entry processing and Exit Processing, we often need to perform some processing, because each State will have repeated processing, And we separate the repetitive processing. There are three solutions for specific implementation: 1. We can call the Entry method () before calling the Request, and add a status check to check whether the Request is in the same status, in this way, you need to add an additional variable to record the current state. The Exit () method can be added to the State: ChangeState () method and called before the Context: ChangState () method. 2. implement each status class using a singleton. Call Entry () in the class structure and Exit () in the destructor. In this way, the Release () of the object is called in ChangeState () you can call Exit (). The Entry can be called when the object's Instance (). This method leaves a defect. The Entry in the next state will Exit () in the previous state () called before. 3. You can use the factory mode to call Entry () in the class structure and Exit () in the destructor. Of course, you need to call delete and new in ChangeState, since the factory mode is used, you do not need to use the object to call the input parameter of ChangeState. Instead, you can use the status ID in Constext: ChangeState, delete the previous State object and call new to create a new object. The code is perfect. The State mode application experiences are mentioned here. The implementation of HTML code is not so simple, and the parsing process is only to display the HTML service, which is only part of the implementation, it is more complicated to implement HTML browsing, and stack processing may be more efficient. Here we just describe it as an application of State. Each mode in the design mode is not isolated and needs to be used in combination according to specific situations. Several modes may be used in a code, making good use of these features is good for improving code quality and reusability. If you have any better suggestions or ideas, please give me some advice, or have any questions or mistakes in my description.
This article is from "floating ~~~" Blog, please be sure to keep this source http://xulin.blog.51cto.com/264387/410565