`
zhang_xzhi_xjtu
  • 浏览: 536539 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

实践中的重构10_平铺直叙的代码(new)

阅读更多
    很多应用程序的主要目的就是用计算机来代替人处理真实世界中的问题。真实世界和计算机世界之间有着巨大的差异。编程语言,编程方法一直以来都有一个重要的发展方向,提供一个平台,尽量减少这个差异。面向对象语言,面向领域编程,为解决该问题在不同层次提供了解决方案。
    在代码级别,也可以通过种种方式减少这个差异。
先看下面一段代码,需求和场景是这样的。
    查询用户列表,传入一个整数,指定一次查询返回用户数的上限。
    有3个不同的数据源可以用来查询用户信息,这3个数据源在查询的时候是有优先级的。即先查询第1个数据源,如果得到的用户数少于上限,则到第2个数据源查询,如果还是少于上限,则到第3个数据源查询。
	public List<User> getUserList_0(int maxSize) {

		List<User> userList;
		int selectSize = 0;
		userList = getUserListFromSource1(maxSize);

		if (null != userList) {
			if (userList.size() < maxSize) {
				selectSize = maxSize - userList.size();
				List<User> userList2 = getUserListFromSource2(selectSize);

				if (null != userList2 && !userList2.isEmpty()) {
					userList.addAll(userList2);
					if (userList.size() < maxSize) {
						selectSize = maxSize - userList.size();
						List<User> userList3 = getUserListFromSource3(selectSize);
						if (null != userList3 && !userList3.isEmpty()) {
							userList.addAll(userList3);
						}
					}
				}
			}

		} else {
			userList = getUserListFromSource2(maxSize);
			if (userList.size() < maxSize) {
				selectSize = maxSize - userList.size();
				List<User> userList4 = getUserListFromSource3(selectSize);
				if (null != userList4 && !userList4.isEmpty()) {
					userList.addAll(userList4);
				}
			}
		}

		return userList;
	}

    如果你耐心的看完了这段代码,我只能说佩服了。一眼看到5层的if嵌套,我已经是半晕状态了。我承认,我没有耐心看完这段代码。
    该方法的需求和场景都是比较简单的,因此应该存在简单的写法。
重写的代码如下:
	public List<User> getUserList_1(final int maxSize) {

		List<User> userList = new ArrayList<User>();
		List<User> temList;

		int selectSize = maxSize - userList.size();
		temList = getUserListFromSource1(selectSize);
		if (temList != null) {
			userList.addAll(temList);
		}
		if (userList.size() >= maxSize) {
			return userList;
		}

		selectSize = maxSize - userList.size();
		temList = getUserListFromSource2(selectSize);
		if (temList != null) {
			userList.addAll(temList);
		}
		if (userList.size() >= maxSize) {
			return userList;
		}

		selectSize = maxSize - userList.size();
		temList = getUserListFromSource3(selectSize);
		if (temList != null) {
			userList.addAll(temList);
		}

		return userList;
	}

    这里首先对参数加上final修饰符,明确表明方法中不会修改该参数。其次,去除掉了if嵌套,用相似的代码结构试图更清晰更直白的表明这段代码的意图。
    这段代码有这样一些小的问题,可以持续改进。
    1 如果可以预期该方法返回的用户列表大小的话,可以设置初始的列表大小。
    2 如果遵循查询结果为空,返回空列表而不是null的话,可以是代码更紧凑。
	public List<User> getUserList_2(final int maxSize) {

		List<User> userList = new ArrayList<User>(maxSize);

		int selectSize = maxSize - userList.size();
		userList.addAll(getUserListFromSource1(selectSize));
		if (userList.size() >= maxSize) {
			return userList;
		}

		selectSize = maxSize - userList.size();
		userList.addAll(getUserListFromSource2(selectSize));
		if (userList.size() >= maxSize) {
			return userList;
		}

		selectSize = maxSize - userList.size();
		userList.addAll(getUserListFromSource3(selectSize));

		return userList;
	}

    这样看上去好多了。这里遵循的原则就是平铺直叙的用编程语言翻译需求,试图用接近自然语言的表达方式实现需求,从而减少两个世界的差异。
    更好的一种方式是先用自然语言(或者伪代码)描述需求的实现,然后翻译成真正的代码。
    用自然语言描述这个需求的实现如下:
    1 新建一个空的用户列表。
    2 计算下一次查询的上限。
    3 到数据源1去查询。
    4 添加结果到用户列表。
    5 如果用户列表达到上限则返回。
    6 计算下一次查询的上限。
    7 到数据源2去查询。
    8 添加结果到用户列表。
    9 如果用户列表达到上限则返回。
    10 计算下一次查询的上限。
    11 到数据源3去查询。
    12 添加结果到用户列表。
    13 返回。
    这个实现的描述和真实的实现很像,即使没有实现,看着这个自然语言的实现翻译成对应的代码实现也很容易。
    当然,计算机编程是复杂的,这里的实现还是有一个潜在的问题。代码中有重复的结构。鉴于这里的实现已经比较简单了,所以没有进一步去掉这个重复的结构。但是,如果需求改变,需要查询更多的数据源,或者可配置的数据源,代码可以做以下演进。
	interface UserQueryService {
		List<User> queryUserList(int maxSize);
	}

	class UserQueryServiceImpl_1 implements UserQueryService {

		@Override
		public List<User> queryUserList(int maxSize) {
			return getUserListFromSource1(maxSize);
		}
	}

	class UserQueryServiceImpl_2 implements UserQueryService {

		@Override
		public List<User> queryUserList(int maxSize) {
			return getUserListFromSource2(maxSize);
		}
	}

	class UserQueryServiceImpl_3 implements UserQueryService {

		@Override
		public List<User> queryUserList(int maxSize) {
			return getUserListFromSource3(maxSize);
		}
	}

	public List<User> getUserList_3(final int maxSize) {

		// 这里的userQueryServices可以从配置文件中生成,也可以用户传入。
		// 这里为了演示,硬编码一下。
		List<UserQueryService> userQueryServices = new ArrayList<UserQueryService>();
		userQueryServices.add(new UserQueryServiceImpl_1());
		userQueryServices.add(new UserQueryServiceImpl_2());
		userQueryServices.add(new UserQueryServiceImpl_3());

		List<User> userList = new ArrayList<User>(maxSize);

		// 方式1
		for (UserQueryService service : userQueryServices) {
			int selectMaxSize = maxSize - userList.size();
			userList.addAll(service.queryUserList(selectMaxSize));

			if (userList.size() >= maxSize) {
				break;
			}
		}

		// 方式2
		for (int i = 0; i < userQueryServices.size()
				&& userList.size() < maxSize; i++) {
			UserQueryService service = userQueryServices.get(i);
			int selectMaxSize = maxSize - userList.size();
			userList.addAll(service.queryUserList(selectMaxSize));
		}

		return userList;
	}

    在方法体中,方式1和方式2都是可行的。对于简单的循环,尾部的一个判断跳出看上去比一个多行的条件判断更清晰一点。
0
1
分享到:
评论

相关推荐

    MATLAB图像处理_能力提高与应用案例

    在文字叙述上,本书摒弃了枯燥的平铺直叙,采用案例与问题引导式;同时,本书还增加了“温馨提示”、“例程一点通”、“经验分享”、“一语中的”等版块,彰显了本书以读者为本的人性化的特点。

    人事面试题150问_人事面试题大全_net程序员面试题__软件开发工程师面试题.xls

    【思路】:建议大家用2分钟得自我介绍,面试官较喜欢的自我介绍(1)有亮点,每一小段都有一个亮点,而不是平铺直叙(2) 有互动:每一小段都会和面试官互动,而不是自说自话,但是切记,这种互动并不需要面试官配合...

    鲁迅散文的艺术特点.doc

    例如在《朝花夕拾》中,鲁迅采用平铺直叙的方式,如《从百草园到三味书屋》中的景物描写,展现了童年记忆的真实场景,让人感受到作者对生活的热爱和对过去的怀念。这种平铺直叙的手法使读者能够与作者产生共鸣,感受...

    《C语言程序设计》教材改革探讨.pdf

    C语言作为一门基础且重要的编程语言,其教学方式长期以来多采用传统的平铺直叙方式,导致学生难以产生浓厚的学习热情。针对这一问题,文章提出了以案例为中心的教材改革策略。 C语言自1972年诞生以来,因其功能强大...

    基于《PLC技术及应用》课程的大学生创新能力的培养与实践.pdf

    由于学生的基础层次不一,教师平铺直叙的讲解方式难以激发学生的兴趣,导致学生对PLC的工作原理和程序设计的理解不深入,难以做到熟练应用和独立思考。这直接影响了学生在实验中的操作能力和创新能力。 2. 缺乏实际...

    细推敲、巧衔接,一枝一叶总关“技”——Python教学背景下的IT课堂实策.pdf

    信息技术学科包含许多专业性的知识和术语,传统的平铺直叙往往难以激发学生的学习兴趣。作者建议在课前设置自主体验任务,例如让学生为Python制作自我介绍,这样既可以使学生带着问题走进课堂,又可以在教师的引导下...

    重庆市第一中学2018_2019学年高一语文上学期期中试题含解析

    "平铺直叙"形容叙述方式,符合语境;"言不及义"指言语空洞,与翻译不准确不符;"山高水低"常指意外的不幸,用在领略路途中不妥;"目不交睫"形容未睡觉,用在缝补衣裳的场景下不恰当。 这些题目均属于高一语文基础...

    导游资格考试知识点:导游业务10.docx

    5. **概述法**:这是一种按照时间顺序、逻辑层次或因果关系进行讲解的方法,也称作平铺直叙,常用于概括性介绍景点或事件。 6. **触景生情法**:此方法要求导游根据眼前的景色引发情感,借题发挥进行讲解,要求自然...

    徐州市、宿迁市2013年高三三模语文试题及答案精选.doc

    例子中提到成语“平铺直叙”、“出言无状”、“墨守成规”和“噤若寒蝉”,要求学生判断其在句子中的合适与否,旨在培养学生的语言表达能力和批判思考能力。 第三部分是概括主要内容,训练学生的阅读理解能力,要求...

    任务驱动教学法在SQL Server数据库课程教学中的应用.pdf

    教学内容不再仅仅是知识点的平铺直叙,而是围绕核心教学目标和任务进行合理地组织与展开。这种方法有助于学生明确学习目标,减少学习的盲目性,让学生在学习过程中更有方向感和目的性。 对于学生而言,任务驱动教学...

    2021年小学教育初等教育实习总结.docx

    初期的习题课和新课教学让实习生积累了宝贵经验,同时也暴露了一些问题,如紧张、讲解过于平铺直叙、课堂控制不够灵活等。通过指导教师的反馈和自我反思,实习生逐渐改进,提高自己的教学水平。 在班主任实习方面,...

    公文写作新手最易犯错误的几点精选.doc

    6. 平铺直叙:增强文章的吸引力,避免过于平淡。通过转折、悬念等手法使文章更具起伏,让读者保持阅读兴趣。 7. 比例失衡:各部分篇幅应保持相对均衡,避免长篇大论与简短寥寥之间的强烈对比。如内容确实有限,可...

    如何提升演讲汇报能力_自我管理与提升_求职职场_实用文档.pptx

    4. **突出重点和亮点**:在汇报中,强调方案的独特性和价值点,避免平铺直叙。这可以通过使用案例、数据和视觉工具来增强说服力。 5. **共鸣与互动**:尝试与听众建立共鸣,让他们参与到讨论中来,而不是单方面的...

    Head First Python_ A Brain-Frie - Barry, Paul

    本书的标题“Head First Python”暗示了其教学方法,即通过直观、互动的方式,以问题和答案的形式引导读者思考和学习,与传统的平铺直叙的教学方法有所不同。这种方法也被其他“Head First”系列书籍所采用,例如在...

    心得参考.doc

    在写作时,我们要避免平铺直叙,而是选取一个或几个关键点进行深入探讨,让文章有重点,有层次。 5. 表达技巧:在写作过程中,我们应注意语言的流畅性和表达的准确性。可以适当运用修辞手法,如比喻、对比等,增强...

    浅谈思想政治课教学中幽默艺术.doc

    在思想政治课中,由于其政策性强、理论抽象的特点,如果仅依赖平铺直叙的语言,可能会使学生感到枯燥。而运用生动幽默的语言,能够使抽象的概念变得形象易懂,激发学生的学习热情。 其次,幽默应当具备教育性,旨在...

    2020年高中语文第14课一名物理学家的教育历程课件6新人教版必修3

    文章在材料处理上的特点在于,它没有按照时间顺序平铺直叙,而是通过精选的童年趣事和高中时期的实验室经历,突显了他成为物理学家的关键历程。这种处理方式使得故事更加聚焦,也更易于读者理解和感受科学探索的过程...

    泳者_csdn

    故事叙述上,采用了一种先平铺直叙,再逐步展现高潮的手法,使情节发展具有一定的曲折性和吸引力;场景描写则通过细致的环境描写和动作描写,把读者带入了一个紧张刺激的氛围中;主题表达上,通过对老警察的英雄事迹...

    -一名物理学家的教育历程(共30张PPT)(共30张PPT).ppt

    4. 文章材料处理上,作者没有平铺直叙,而是选择关键事件进行深入阐述,如对爱因斯坦理论的“侦探式”阅读和自己动手实验,这显示出科学家所需的独立思考和实践能力。 5. “教育”对于成为优秀科学家的重要性体现在...

    201611徐盈盈评课.doc

    然而,课程也存在一些改进空间,如教师的语言表达可以更加生动活泼,避免平铺直叙;在讲解作画方法和欣赏作品环节,应有更清晰的逻辑顺序,减少语言的冗余,以提高教学效率。 总的来说,这堂课充分体现了教学目标的...

Global site tag (gtag.js) - Google Analytics