科技探索的每一次进步,不只拓展着人类认知的疆界,也为经济社会发展带来了强大的动力,几次重大的科技革命带来机械化、电气化、自动化、信息化等多次产业革命,世界发展面貌和格局之深刻改变。
DUT ~ 计算机科学与技术 ~ 殷祺云
一、设计方法与实践介绍
(1)软件设计原则
软件设计是为了使软件在长期范围内能够容易的进行变化。①高内聚
内聚指的是一个软件内部间元素相关联的程度,高内聚追求的是紧密相关联的元素要放在一起。②低耦合
低耦合指的是单位之间尽可能少的关联与依赖。在高内聚低耦合之上有很多其他的原则:
(2)clean code
我们写的代码应该能够在尽可能短的时间内被别人读懂,且代码排版整洁、逻辑清晰、扩展性好;命名规则需要做到自注释,使用有意义的循环迭代遍历,避免拼音缩写;同时,好的注释也是必不可少的,其中注释中包含版权信息、设计意图以及警示信息。在进行函数的书写时,要注意每个函数只做一件事且每个函数都是单一职责的,函数可以做如下分类。
- 骨架函数:业务逻辑和算法在高层次上的抽象描述
- 步骤函数:业务逻辑和算法的一些实现细节
- 使用自然的比较顺序
- 简化逻辑层次,避免多层嵌套
- 在写三元表达式时不要出现复杂逻辑以及过长条件
- 控制变量作用域
(3)单元测试
单元测试是最底层的测试,可以更早的发现问题,更容易集成以及有着更安全的代码修改,写好单元测试可以降低产品开发的成本。我们通常将单元测试视为文档工作。在之后对项目进行修改时,只需要查看单元测试即可。单元测试可实现自检,判断测试是否成功,不需要人为判定。同时,不能单独为测试创建特别的逻辑,更不能破坏原有代码的逻辑。
(4)重构
- 业务导向
- 小步快跑(主干、分支)
- 演进式设计
- 正交设计原则(分变化方向)
(5)配置化架构
配置化架构是在领域建模的基础上表述业务,以配置组织架构元素,如数据、服务、组件等。应用配置化架构
- 业务配置化改造
- 提高配置的开发效率
- 降低配置的维护成本
二、高效研发流程脚本
(1)用户故事地图
- 一步一步写出你的故事
- 组织情节
- 探索替代故事
- 提取故事地图的主干
- 切分出能帮你达成特定目标的任务
(2)制定发布计划
制定计划:- Big Story进行细化讨论
- 按照价值和重要程度进行版本规划
- 确定每个版本的期望达成目标
- 确定每个版本的内容
- 团队达成共识
(3)从发布计划到迭代计划
在集中发布模式中,一次发布包含多次迭代;在迭代发布模式中,一次发布等于一次迭代。对用户故事的拆分
对用户故事的拆分要做到拆分出的故事尽量小,但是要适当,而并不是越小越好,避免出现一个迭代内无法完成的故事。用户故事的优先级
在原本的迭代计划基础上,会插入更高优先级的需求,替换低优先级的需求,把低优先等级的需求放入下一次的迭代中。用户故事估算
对用户故事工作量进行估算,根据团队的均值能力进行估算。迭代计划制定
(4)从迭代计划到迭代的落地执行
三、研发工具链介绍
(1)iCafe需求管理
iCafe 是一个专业、好用的互联网研发管理工具,内嵌产品规划、开发计划、执行跟踪、回顾分析、持续改进等众多互联网研发优秀实践,让研发管理轻松高效, 为企业提供基于敏捷开发方法的高效的任务协同工具。(2)iCode
iCode是一个代码管理工具。基于工作流。基于主干的工作流,整个团队维护一条主干分支,发布后恢复到一条主干的简明模式。基于分支的工作流,基于主干最新的代码,分支完成相应功能的开发、测试和发布,分支发布前,先同步主干的更新,上线后将分支合并回主干。
(3)iPipe
iPipe是一个交付平台。可以让开发、测试与运维人员在这个工具中直观的看到产品的状态与质量情况。四、持续交付方法与实践
(1)软件交付流程
(2)持续交付
持续交付可以让代码快速安全的部署到生产环境中,自动化过程,提供了一套更为完善的解决传统软件开发流程的方案。可以有效缩短提交代码和部署上线的时间,可以自动快速的提供反馈与修复缺陷,持续交付课让软件在整个生命周期内都处于可部署的状态,简化部署步骤。(3)敏捷开发与Devops
(4)持续交付的基础
持续集成和持续部署是持续交付的基础。持续集成包括代码编译,近代检查与单元测试任务。运用虚拟机技术与容器技术可实现快速将项目进行部署。(5)持续部署
应用容器技术进行持续部署。(6)流程控制
五、代码的艺术
(1)黑盒与白盒机制
(2)系统设计注意要点
首先,系统架构定义了系统的结构、行为与更多的视图,要素包括系统要完成的功能、系统如何组成以及功能在这些组成部分之间如何划分。其次,包括资源的限制,有计算的限制,存储的限制以及IO/网络的限制。需求是系统设计决策的来源,且清楚接口的重要性,接口主要包括模块对外的函数接口、平台对外的API、系统见通信的协议以及系统间存在依赖的数据。六、Mini-spider实践
(1)多线程编程
正确案例(将添加和判断写进一个函数):class UrlTable(object):
def init (self):
self.lock=threading.Lock()
self.table=()
def add(self, url):
"add url to table"
ret_val ='OK'
self.lock.acquire()
if url in self.table:
ret val ='ERROR'
else:
selftable[url]=True
self.lock.release()
return ret_val
错误案例(分别编写添加和判断函数):
class UrlTable(object):
def init (self):
self.lock=threading.Lock()
self.table=()
def add(self,url):
self.lock.acquire()
self.table[url]=True
self.lock.release()
def is_in_table(self, url):
self.lock.acquire()
ret_val=url in self.table
self.lock.release()
return ret_val
同时,模块划分和逻辑的复杂程度无关,即使是逻辑简单的代码,如果没有做好模块划分,也会变得难于维护。
(2)程序优雅推出
- task_down()机制- join()机制
(3)爬虫主逻辑代码
- 从队列中拿到任务
- 读取内容
- 存储数据
- 检查数据深度
- 如果数据深度不足,继续解析,并且放到队列中
- 结束任务
def zun(self):
while self.active:
# get url and depth from input queue
url, depth=self.input queue.get()
# read data from given url
data=webpage get(url)
if data is None:
self.input queuetask done()
continue
# save data to file
url filesave(self.output directory,ur1,data)
# check depth
if depth<selfmax depth:
# get new urls from data
new urls =html parseurl extract(data)
# add new url to input queue
for new url in new urls:
if selfurl table.add(newurl)=='OK':
self.input queue put((new url,depth+1))
# task done
self.input queue.task done()
七、代码检查规则背景和总体介绍
(1)代码检查场景&工具
(2)代码检查覆盖范围
- 编码规范(较快)
- 代码缺陷(较慢)
- 开源框架引用安全检查
- 可维护性
(3)规则等级处理
- Error
- Warning
- Advice
八、python语言案例详解
(1)代码风格规范
- 每行字符<=120字
- 函数长度<=120行
- 不能用分号做结尾
- 一行只能写一条语句
- 尽量避免冗余的括号
- 禁止使用Tab,统一使用4个空格进行缩进
- 类或全局函数隔两个空行,类方法隔一个空行
- 括号内不加空格,参数列表左括号前无空格,都好分号冒号前无空格,后一个空格
- 二元运算符前后各一个空格
- 参数默认值等号前后无空格
- 文件声明中注释中应有版权声明、功能用途介绍、修改人及联系方式
- docstring必须用三个双引号,对外接口必须使用docstring
- docstring必须包括功能简介,参数和返回值,如果可能抛出异常,必须特别注明
(2)代码引用规范
- 在导入库后再调用类或函数
- 每行只导入一个库,按标准库、第三方库、应用程序自有库的顺序导入
(3)代码定义规范
- 局部变量全小写字母,全局变量卸载文件头部,常量使用全大写字母
- 函数返回值小于等于三个
- 在导入库后再调用类或函数
- 类使用首字母大写的驼峰命名法
- _protected成员 __private成员
- 如果一个类无基类,必须继承自ovject类
(4)异常处理规范
- 禁止使用双参数形式或字符串形式的语法抛出异常
- 自定义异常名称为Error
- 除非重新抛出异常,禁止使用except语句捕获所有异常
- 使用as语法,不能使用逗号语法
(5)python惯例
- 让模块既可以被导入又可以被执行
- in运算符使用
- 不适用临时变量交换两个值
- 用序列构建字符串