首页 > 代码库 > 应聘者页面——文本输入框及单选多视图重构

应聘者页面——文本输入框及单选多视图重构

             最近校招季,实习单位提供校招软件服务,线上用户比较多,并没有太多新功能上线,乐帝主要负责做一些重构的工作。想想今年毕业新来的同事,已经能够独立承担业务开发。乐帝最近对MVC架构有了更深的了解,编程能力也有一定的入门提高,从同事新胜那儿,学到很多前端开发的规范,在这里再次感谢新胜耐心教导、无私帮助。

    乐帝与新胜最大的差别在于,新胜处理问题解决问题,都有深厚的理论功底,即知其所以然,而不单单是一个程序员,他有自己的思考,懂得如何优化代码与性能。乐帝向新胜学习他,构建的理论体系及解决问题的方法。快速缩小与新胜这个榜样的差距。

    这篇文章所讨论视图,在单位,招聘项目里,应聘者功能下。那为什么需要重构呢?

    在这次重构工作中,我想有两方面原因:

  • 模板中处理逻辑过于复杂,不符合结构与处理逻辑分离,代码可读性不高。
<select data-name="<%=Name%>" data-obj="<%=controlData.Object%>" class="souce_name search_view width130">
		<option value=http://www.mamicode.com/"">不限>
      如上所示,模板中用了多层if-else嵌套,夹杂各种<% %>以分割HTML和JS代码,结构与逻辑耦合度非常高,可读性比较低,不利于修改。
  • 多个同类视图,在逻辑和结构上,只有微小差异,但源代码各写一套逻辑和视图,扩展性不高,造成大量代码冗余,不利于后期维护。
   乐帝做的第一步是将模板中逻辑提取出来,并将views.SearchItemView1、views.SearchItemView20、views.SearchItemView23,这三个视图模板采用Beyond Compare软件进行文字比对,发现三个模板差异,这里的差异包括两部分内容:结构差异与逻辑差异。其中结构上标签名都相同,只是每个视图部分标签属性值不同,这些属性值可在View中处理。
   乐帝最初的解决方案,是通过调试每个视图传入model值,发现视图构建差异在model值中Ctype属性上。

  于是针对以上三个视图,针对Ctype判断,构建不同视图不同的属性值。
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");
					}
				}//针对不同ctype设置input不同属性及值
				if(cType==1){
					if(typeof isDefault !="undefined"&&isDefault==1)
					{
						this.$el.find("input[type='text']").attr("defaultValue",defaultVal);
					};// 设置defaultValue属性
				}
				
				if(typeof searchItems !="undefined")
				{
					if(searchItems.Value!=null)
					{
						this.$el.find("input[type='text']").attr("value",searchItems.Value[0]);
					}
				}
				else{
					if(cType==1){
						if(typeof isDefault !="undefined"&&isDefault==1){
						this.$el.find("input[type='text']").attr("value",defaultVal);
						}
					}
					
				}//设置value属性值
			},
			checkInputAttr:function(){
				var searchItems = 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)&&(searchItems.Value.length>0)){
							_.each(searchItems.Value,function(item,value){
								var valLength = (cType==1)?(searchItems.Value.length):(searchItems.Value.length-1);//判断采用何种表达式
								if(valLength==value){
									if(item){
										this.$el.find("input[type='checkbox']").attr("value",item);
										if(searchItems.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设置

              这段代码存在的隐患是,根据Ctype做判断生成各视图,与model非常相关,即与已有数据结构高度耦合,不利于扩展。假如在此逻辑上,我需要新加一个与上述三类视图同类的视图,那么还需在代码基础上,再次改进代码,加入对新视图Ctype判断,这显然不是我们想要的。
   重构真正的需求是:构造一个通用类,将每个视图共通的地方写入此类中,在不同子视图中继承此通用类,如有差异可覆写通用类方法,实现个性化定制。
   有了如上思路,接下来做的工作,就是根据之前采用文本比对得出的差异部分,进行分块处理,即构造原子函数,确定都有哪些原子块,并且一并写到通用类中。差异的部分在原子类中以空方法形式存在,在子函数中覆写通用类空函数,实现个性化订制。
var SingleInputView = Talent.ItemView.extend({
			onBeforeRender:function(){
				this.standLabel();//标准化标签
			},
			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(0,6)+"…"});
				}
			},
			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)&&(searchItems.Value.length>0)){
							_.each(searchItems.Value,function(item,value){
								var valLength =searchItems.Value.length-minus;//判断采用何种表达式
								if(valLength==value){
									if(item){
										this.$el.find("input[type='checkbox']").val(item);
										if(searchItems.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("");
					}
			},
		});

      有些逻辑代码,只是随着视图不同,变量不同,这里在父类中,构造带变量的方法,并在子类中设置属性值,传入父类方法中,如:
setCheckboxVal:function(minus){
				var searchItems = this.model.get("searchItems");
				if((typeof searchItems !="undefined")&&(searchItems.Value!=null)&&(searchItems.Value.length>0)){
					_.each(searchItems.Value,function(item,value){
						var valLength =searchItems.Value.length-minus;//判断采用何种表达式
						if(valLength==value){
							if(item){
								this.$el.find("input[type='checkbox']").val(item);
								if(searchItems.Value[value]=="true")
									this.$el.find("input[type='checkbox']").attr("checked","checked");
								}else{
									this.$el.find("input[type='checkbox']").val("");
								}
							}
					});

   在父类中构造了渲染后onRender回调函数,自动调用通用类及子视图方法:
onRender:function(){
				this.textMaxlen(this.maxlength);
				this.setCheckboxVal(this.minus);
				this.SetTextInput();
				this.SetCheckboxInput();

			}

             这里用到两个this.SetTextInput()和this.SetCheckboxInput()两个方法,分别是在子类中实现,用以按照定制化需求,加载执行不同子类函数:
SetTextInput:function(){
				this.textAddClass();
				this.setTextDefaultVal();
				this.setTextVal();
			}

        通用类,还用到一个onBeforeRender回调方法,用以在数据还没有渲染到模板时,对数据进行处理。
onBeforeRender:function(){
				this.standLabel();//标准化标签
			}

    这样处理的优势在于,逻辑更清晰,并且充分利用此回调函数时序上的优势。
    通过对以上重构分析,我们可以得出重构的大体方向:
  • 对差异代码模块化,写入通用类的空方法中。
  • 只有变量差异的代码,写入通用类中带参数方法中。
  • 最后调用方法,写在通用类中,并在子类中,构造定制化加载方法如SetTextInput方法的职能

应聘者页面——文本输入框及单选多视图重构