During the last school recruitment season, the internship Unit provided the school recruitment software service. There were a large number of online users and there were not many new features available. ledi was mainly responsible for restructuring. Think of new colleagues who have graduated this year and are able to independently develop their businesses. Ledi has recently gained a deeper understanding of the MVC Architecture and improved programming capabilities. He learned many front-end development specifications from his colleagues, here, I would like to thank Xin Sheng for his patience and selfless help.
The biggest difference between le Di and Xin Sheng is that Xin Sheng has profound theoretical knowledge in solving problems and knows why, not just a programmer. He has his own thoughts, know how to optimize code and performance. Ledi learned from Xinsheng about his theoretical system and solution to the problem. Quickly narrow down the gap with the new victory model.
In the view discussed in this article, the applicant function is provided in the unit, recruitment project, and recruitment project. So why does it need to be reconstructed?
In This refactoring, I think there are two reasons:
- The processing logic in the template is too complex, does not conform to the separation of structure and processing logic, and the code is not highly readable.
<Select data-name = "<% = Name %>" data-OBJ = "<% = controldata. object %> "class =" souce_name search_view width130 "> <option value =" "> unlimited </option> <% IF (typeof searchitems! = "Undefined") {%> <% IF (searchitems. value! = NULL) {%> <% _. each (datasource, function (item) {%> <% IF (searchitems. value. length> 0) {%> <% _. each (sew. EMS. value, function (Item1) {%> <% IF (Item1! = Item. value) {%> <option value = "<% = item. value %> "> <% = item. text %> </option> <%} else {%> <option value = "<% = item. value %> "selected =" selected "> <% = item. text %> </option> <% }%> <%}) %> <%} else {%> <option value = "<% = item. value %> "> <% = item. text %> </option> <% }%> <%}) %> <%} else {%> <% _. each (datasource, function (item) {%> <option value = "<%= item. value %> "Title =" <% = item. text %> "> <% = item. text %> </option> <%}) %> <% }%> <%} else {%> <% _. each (datasource, function (item) {%> <option value = "<%= item. value %> "> <% = item. text %> </option> <%}) %> <% }%> </SELECT>
As shown above, the template uses multi-layer if-else nesting, and contains various <%> to separate HTML and JS Code. The structure and logic are highly coupled, and the readability is relatively low, which is not conducive to modification.
- Multiple similar views have only minor differences in logic and structure. However, the source code writes a set of logic and views, which is not highly scalable, resulting in a large amount of code redundancy, which is not conducive to later maintenance.
Ledi's first step is to extract the logic from the template and extract views. searchitemview1, views. searchitemview20, views. searchitemview23, these three view templates use the beyond compare software for text comparison. We found that the differences between the three Templates include two parts: structure difference and logical difference. The tag names in the structure are the same, but the tag attribute values of each view are different. These attribute values can be processed in the view.
Ledi's initial solution was to debug each view and pass in the model value. It was found that the differences between the View Construction and the ctype attribute in the model value.
Therefore, based on the preceding three views and the ctype judgment, different attribute values of different views are constructed.
Textinputattr: function () {var isdefault = This. model. get ("isdefault"); var searchitems = This. model. get ("searchitems"); var defaultval = This. model. get ("defaultval"); var ctype = This. model. get ("ctype"); If (ctype = 1) {This. $ el. find ("input [type = 'text']"). addclass ("search_box_prev"); this. $ el. find ("input [type = 'text']"). ATTR ("data-rule-maxlength", 300);} else {This. $ el. find ("input [type = 'text']"). addclass ("Souce _ Name "); this. $ el. find ("input [type = 'text']"). ATTR ("data-rule-maxlength", 100); If (ctype = 23) {This. $ el. find ("input [type = 'text']"). addclass ("default_word") ;}// set different attributes and values of input for different ctype if (ctype = 1) {If (typeof isdefault! = "Undefined" & isdefault = 1) {This. $ el. find ("input [type = 'text']"). ATTR ("defaultvalue", defaultval) ;}; // sets the defaultvalue attribute} If (typeof searchitems! = "Undefined") {If (searchitems. value! = NULL) {This. $ el. find ("input [type = 'text']"). ATTR ("value", sew. EMS. value [0]) ;}} else {If (ctype = 1) {If (typeof isdefault! = "Undefined" & isdefault = 1) {This. $ el. find ("input [type = 'text']"). ATTR ("value", defaultval) ;}}// set the value attribute value}, checkinputattr: function () {var sew. EMS = This. model. get ("searchitems"); var ctype = This. model. get ("ctype"); If (ctype = 1) {This. $ el. find ("input [type = 'checkbox']"). addclass ("search_box_any");} else {This. $ el. find ("input [type = 'checkbox']"). addclass ("souce_name");} If (typeof searchitems! = "Undefined") & (searchitems. value! = NULL) & (sew. EMS. value. length> 0 )){_. each (sew. EMS. value, function (item, value) {var vallength = (ctype = 1 )? (Searchitems. value. length) :( sew. EMS. value. length-1); // determine the expression used if (vallength = value) {If (item) {This. $ el. find ("input [type = 'checkbox']"). ATTR ("value", item); If (sew. EMS. value [value] = "true") This. $ el. find ("input [type = 'checkbox']"). ATTR ("checked", "checked");} else {This. $ el. find ("input [type = 'checkbox']"). ATTR ("value", "") ;}}) ;}else {This. $ el. find ("input [type = 'checkbox']"). ATTR ("value", "") ;}// checkbox settings
The hidden danger of this Code is that the view is generated based on the ctype judgment, which is very related to the model, that is, highly coupled with the existing data structure, which is not conducive to expansion. If logically, I need to add a new view similar to the preceding three types of views, I also need to improve the Code on the basis of the Code and add the new view ctype judgment, this is obviously not what we want.The real requirement for refactoring is: Construct a common class, write the common parts of each view into this class, and inherit this common class from different subviews, if there are any differences, you can override common class methods to achieve personalized customization.
With the above ideas, the next step is to process parts based on the differences obtained by text comparison, that is, to construct atomic functions and determine which atomic blocks are available, and write them to common classes. The difference exists in the atomic class in the form of a null method. In the subfunction, override the general class null function to achieve personalized customization.
VaR singleinputview = talent. itemview. extend ({onbeforerender: function () {This. standlabel (); // standard label}, onrender: function () {This. textmaxlen (this. maxlength); this. setcheckboxval (this. minus); this. settextinput (); this. setcheckboxinput () ;}, standlabel: function () {var label = This. model. get ("label"); If (label. length! = 7) & (Label. length> 6) {This. model. Set ({"label": Label. substring () + "... "}) ;}}, Textaddclass: function () {}, textmaxlen: function (length) {This. $ el. find ("input [type = 'text']"). ATTR ("data-rule-maxlength", length) ;}, settextdefaultval: function () {}, settextval: function () {var isdefault = This. model. get ("isdefault"); var searchitems = This. model. get ("searchitems"); var defaultval = This. model. get ("defaultval"); If (typeof searchitems! = "Undefined") {If (searchitems. value! = NULL) {This. $ el. find ("input [type = 'text']"). val (searchitems. value [0]) ;}}, checkboxaddclass: function () {}, setcheckboxval: function (minus) {var searchitems = This. model. get ("searchitems"); If (typeof searchitems! = "Undefined") & (searchitems. value! = NULL) & (sew. EMS. value. length> 0 )){_. each (sew. EMS. value, function (item, value) {var vallength = sew. EMS. value. length-minus; // determine the expression used if (vallength = value) {If (item) {This. $ el. find ("input [type = 'checkbox']"). val (item); If (sew. EMS. value [value] = "true") This. $ el. find ("input [type = 'checkbox']"). ATTR ("checked", "checked");} else {This. $ el. find ("input [type = 'checkbox']"). val ("") ;}}) ;}else {This. $ el. find ("input [type = 'checkbox']"). val ("");}},});
Some logic code, but with different views, variables are different. In the parent class, construct a method with variables, set the attribute values in the subclass, and pass in the parent class method, for example:
Setcheckboxval: function (minus) {var searchitems = This. model. Get ("searchitems"); If (typeof searchitems! = "Undefined") & (searchitems. value! = NULL) & (sew. EMS. value. length> 0 )){_. each (sew. EMS. value, function (item, value) {var vallength = sew. EMS. value. length-minus; // determine the expression used if (vallength = value) {If (item) {This. $ el. find ("input [type = 'checkbox']"). val (item); If (sew. EMS. value [value] = "true") This. $ el. find ("input [type = 'checkbox']"). ATTR ("checked", "checked");} else {This. $ el. find ("input [type = 'checkbox']"). val ("");}}});
The onrender callback function after rendering is constructed in the parent class to automatically call common classes and subview methods:
onRender:function(){this.textMaxlen(this.maxlength);this.setCheckboxVal(this.minus);this.SetTextInput();this.SetCheckboxInput();}
Here we use two methods, this. settextinput () and this. setcheckboxinput (), which are implemented in subclasses to load and execute different subclass functions according to customization requirements:
SetTextInput:function(){this.textAddClass();this.setTextDefaultVal();this.setTextVal();}
A common class also uses an onbeforerender callback method to process data before it is rendered to a template.
Onbeforerender: function () {This. standlabel (); // standard label}
The advantage of this processing is that the logic is clearer and the timing advantages of this callback function are fully utilized.
Through the above reconstruction analysis, we can draw the General Direction of reconstruction:
- Modularize the Differential Code and write it into the empty method of common classes.
- Only codes with variable differences are written into common classes with parameter methods.
- Finally, call the method, write it in a common class, and construct a custom loading method, such as settextinput, In the subclass..
Applicant page -- text input box and single-choice multi-view Reconstruction