cleaning code

时间限制:1秒空间限制:32768K热度指数:13590

本题知识点:代码优化之函数。

①无副作用,一个函数应该功能单一,一目了然。总而言之就是函数应该只做一件事,副作用就是它 在做一件事的时候还会做被隐藏的事,有时,它会对自己类中的变量做出未能预期的改动,有时它会 把变量搞成向函数传递的参数或是系统全局变量,无论哪种情况都是具有破坏性的,会导致古怪的时 序性耦合及顺序依赖。 如以下例子,该函数使用标准算法来匹配userName和password,如果匹配成功返回true,如果 失败,返回false,但是它有副作用,分析分析副作用在哪? public class UserValidate{ private Cryptographer cryptographer;

		public boolean checkPassword(String userName, String password){
			User user = UserGateway.findByName(userName);
			if(user!=User.NULL){
				String codedPhrase = user.getPhraseEncodeByPassword();
				String phrase  = cryptographer.decltype(codedPhrase,password);
				if("Valid password".equals(phrase)){
					Session.initialize();
					return true;
				}
			}
			return false;

		}
	}
	副作用就是对于Session.initialize()函数的调用,这个函数顾名思义就是检查密码,但是它
加入了会话的初始化,所以当有人光看函数名时,想要检查用户的有效性时,就会冒抹除现有会话
数据的风险,
这次副作用就是我们把Session的初始化放在了判断里,只有验证正确的时候,会话才会开启,如
果验证不通过会话就不会开启,这就叫做时序耦合性。

 ②输出参数问题
 参数多数会被看做函数的输入,但是我们一定被用作输出而非输入的的参数迷惑过,例如:
 appendFoot(s);
 这个函数是什么意思呢,我们要对参数s做什么操作呢,让人很疑惑,我们还必须去检查它的函数
 签名。这又中断了我们的思路,所以我们应该避免使用参数输出,如果函数要修改某种状态,就修
 改对象的状态吧。

 -----------------------------------------------------------------------------

 ③分隔指令和询问

 函数要做什么事和要回答什么事,但二者不可得兼,函数应该修改某对象的状态或是返回对象的有 效信息,两者都干通常导致混乱,如下例:
	public boolean set(String attribute,String value);
	该函数设置某个指定属性,如果成功返回true,如果不存在那个属性则返回false,这样就导致
	了一下语句:
	if (set("username","unclebob"))...
	从读者的角度考虑,它是问username属性值是否之前已设置为unclebob,还是在问username的
属性值是否成功设置为unclebob,难以判断它的含义,是动词或者是形容词不清楚。
我们应该把函数的指令和询问分开。例:
		if(attributeExists("username")){//先询问
			setAttributeExists("username","unclebob");

		}



		--------------------------------------------------------------------------
		④使用异常代替返回错误码

		从指令式函数返回错误码轻轻微违反了指令和询问分隔的规则,它鼓励了在if语句判断中把指
令当做表达式用,例:
		if(deletePage(page)==E_OK){};
这不会引起动词形容词的混淆,但是它会导致更深层次的嵌套结构,当返回错误码时,就是在要求
使用者立刻处理错误。
		if(deletePage(page)==E_OK){
			if(registry.deleteFererce(page.name)==E_OK){
				if(configKeys.deleteKey(page.name.key==E_OK)){
					logger.log("page deleted");
				}else{
					logger.log("configKeys not deleted");
				}else{
					logger.log("deleteFererce from registry failed");
				}
			}else{
				logger.log("delete failed");
				return E_ERROR;
			}
		} 另一方面,如果使用异常代替返回错误码,错误处理代码就能从主路径代码中分离出来的到简化。
	try{
		deletePage(page);
		registry.deleteFererce(page.name);
		configKeys.deleteKey(page.name.makeKey());
	}catch(Exception){
		logger.log(e.getMessage());

	}


	----------------------------------------------------------------------------

	⑤抽离try/catch代码块

	 Try/Catch代码块丑陋不堪,它们搞乱了代码结构,把错误处理和正常流程混为一谈,最好把
	 try和catch代码块的主体部分抽离出来,另外形成函数。例:
	 public void delete(Page page){
		 try{
			 deletePageAndAllReferences( page);
		 }catch(Exception e){
			 logError(e);

		 }
	 }

	 public void deletePageAndAllReferences(Page page)rhrows Exception{
		 deletePage(page);
		 registry.deleteReference(page.name);
		 configKeys.deleteKey(page.name.makeKey());
	 }
	 private void logError(Exception e){
		 logger.log(e.getMessage);
	 }
	 这里就很好的阐释了,一个函数只负责一个功能,delet函数只与错误处理有关,deletePage
AndAllReferences只与删除page这个功能有关,这样美妙的分隔,代码更容易理解和修改。


-----------------------------------------------------------------------------
⑥错误处理就是一件事
		函数应该只做一件事,错误处理就是一件事。因此错误处理的函数不该做其他事,这就意味着
如果关键字try在某个函数中出现,它就该是这个函数的第一个单词,而且catch/finally代码块
也不该有其他内容。

------------------------------------------------------------------------------

⑦Error.java依赖磁铁
返回错误码通常暗示某处有个类或者枚举,定义了所有的错误码;例
public enum Error{
	ok;
	INVALLD;
	NO_SUCF;
	LOCKED;
	OUT_OF_RESOURCES;
}
这样的类就是一块依赖磁铁,其他很多类都得导入和使用它,当Error类修改时,所有其他的类都
需要重新编译和部署,这对Error类造成了负面压力,程序员不愿增加新的错误代码,因为这样他
门就得重新构建和部署所有东西,于是他们就复用旧的错误码,而不用添加新的。
		使用异常替代错误码,新异常就可以从异常类派生出来,无需重新编译或重新部署。

⑧结构化编程

有些程序员遵循结构化编程规则: 每个函数,函数中的每个代码都应该只有一个入口,一个出口。

-----------------------------------------------------------------------------

⑨如何写出复合这些规则的函数
	写代码和写别的东西很像,写论文时,你就是想到什么写什么,然后再去打磨它,初稿也许粗糙
	和无序,你就得斟酌推敲,直到到达你心目中的样子。
	我写函数时,一开始都冗长而复杂,有太多的缩进和嵌套循环,有过长的参数列表,名称是随意
	取得,也会有重复的代码。不过我会配上一套单元测试,覆盖每行丑陋的代码。
	然后我打磨这些代码,分解函数,修改名称,消除重复,我缩短和重新安置方法,有时我还拆散
	类,同时保持测试通过。
	最后,好代码都是一次次的打磨出来的,一开始就写出来,我想没人做得到。

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦