《软件开发的201个原则》
Posted OOQ
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《软件开发的201个原则》相关的知识,希望对你有一定的参考价值。
目录
前言
《软件开发的201个原则》主要面向以下三类目标读者:
- 软件工程师和管理者。你可以弄清什么对软件项目是好的,什么是不好的。
- 软件工程方向的学生。熟悉了解这些原则,能够让你的视野有所提升,不再局限于初步的编码层面,培养软件工程思维方式和基本专业素养。如果将来从事软件相关职业,那么一定会对你的工作有帮助。
- 软件研究人员。(可能会有帮助。读者没有经历这种角色,待体会)
作为一名从事软件开发工作的读者,深刻体会到《软件开发原则》书中的系列原则,在我开发过程中处处可见。如果遵循这些原则,那么对你的开发是十分有效的。在项目开发过程格遵守开发流程,让一切异常都有迹可循,有法可解,工作效率显著提示。
一、一般原则
- 质量第一
- 质量在每个人眼中都不同
- 开发效率和质量密不可分
- 高质量软件是可以实现的
- 不要试图通过改进软件实现高质量
- 低可靠性比低效率更糟糕
- 尽早把产品交给客户
- 与客户用户沟通
- 促进开发者与客户的目标一致
- 做好抛弃的准备
- 开发正确的原型
- 构建合适功能的原型
- 要快速地开发一次性原型
- 渐进地扩展系统
- 看到越多,需要越多
- 开发过程中的变化是不可避免的
- 只要可能,购买而非开发
- 让软件只需简短的用户手册
- 每个复杂问题都有一个解决方案
- 记录你的假设
- 不同阶段使用不同语言
- 技术优先于工具
- 使用工具,但要务实
- 把工具交给优秀的工程师
- CASE实现目标就停止
- 工具是昂贵的
- “知道何时”和“知道如何”同样重要
- 了解形式化方式
- 和组织荣辱与共
- 跟风要小心
- 不要忽视技术
- 使用文档标准
- 文档要有术语表
- 软件文档都要有索引
- 对相同的概念用相同的名字
- 研究再转化,不可行
- 要承担责任
二、需求工程原则
- 低质量的需求分析,导致低质量的成本估算
- 先确定问题,再写需求
- 立即确定需求
- 立即修复需求规格说明中的错误
- 原型可降低选择用户界面的风险
- 记录需求为什么被引入
- 确认子集
- 评审需求
- 避免在需求分析时进行系统设计
- 使用正确的方法
- 使用多角度的需求视图
- 合理地组织需求
- 给需求排列优先级
- 书写要简洁
- 给每个需求单独编号
- 减少需求中的歧义
- 对自然语言辅助增强,而非替换
- 在更形式化的模型前,先写自然语言
- 保持需求规格说明的可读性
- 明确规定可靠性
- 应明确环境超出预期时的系统行为
- 自毁的待定项
- 将需求保存到数据库
三、设计原则
- 从需求到设计的转换并不容易
- 将设计追溯到需求
- 评估备选方案
- 没有文档的设计不是设计
- 封装
- 不要重复造轮子
- 保持简单
- 避免大量的特殊案例
- 缩小智力距离
- 将设计置于知识控制之下
- 保持概念一致
- 概念性错误比语法性错误更严重
- 使用耦合和内聚
- 为变化而设计
- 为维护而设计
- 为防备出现错误而设计
- 在软件中植入通用性
- 在软件中植入灵活性
- 使用高效的算法
- 模块规格说明只提供用户需要的所有信息
- 设计是多维的
- 优秀的设计出自优秀的设计师
- 理解你的应用场景
- 无须太多投资,即可实现复用
- “错进错出”是不正确的
- 软件可靠性可以通过冗余来实现
四、编码原则
- 避免使用特殊技巧
- 避免使用全局变量
- 编写可自上而下阅读的程序
- 避免副作用
- 使用有意义的命名
- 程序首先是写给人看的
- 使用最优的数据结构
- 先确保正确,再提升性能
- 在写完代码之前写注释
- 先写文档后写代码
- 手动运行每个组件
- 代码审查
- 可以使用非结构化语言
- 结构化代码未必是好代码
- 不要嵌套太深
- 使用合适的语言
- 编程语言不是借口
- 编程语言的知识没那么重要
- 格式化你的代码
- 不要太早编码
五、测试原则
- 依据需求跟踪测试
- 在测试之前早做测试
- 不要测试自己开发的软件
- 不要为自己的软件做测试计划
- 测试只能揭示测试的存在
- 虽然大量错误可证明软件毫无价值,但是零错误并不能说明软件的价值
- 成功的测试应该发现错误
- 半数的错误出现在15%的模块中
- 使用黑盒测试和白盒测试
- 测试用例应包含期望的结果
- 测试不正确的输入
- 测试压力必不可少
- 大爆炸理论不适用
- 使用McCabe复杂度指标
- 使用有效的测试完成度标准
- 达成有效的测试覆盖
- 不要在单元测试之前集成
- 测量你的软件
- 分析错误的原因
- 对“错”不对人
六、管理原则
- 好的管理比好的技术更重要
- 使用恰当的方法
- 不要相信你读到的一切
- 理解客户的优先级
- 人是成功的关键
- 几个好手要强过很多生手
- 倾听你的员工
- 信任你的员工
- 期望优秀
- 沟通技巧是必要的
- 端茶送水
- 人们的动机是不同的
- 让办公室保持安静(博主觉得应该保持适度安静)
- 人和时间是不可互换的
- 软件工程师之间存在巨大差异
- 可以优化任何想要优化的
- 隐蔽地收集数据
- 每行代码的成本是没用的
- 衡量开发效率没有完美的方法
- 剪裁成本估算方法
- 不要设定不切实际的截止时间
- 避免不可能
- 评估之前要先了解
- 收集生产力数据
- 不要忘记团队效率
- LOC/PM与语言无关
- 相信排期
- 精确的成本估算并不是万无一失的
- 定期重新评估排期
- 轻微的低谷不总是坏事
- 分配合适的资源
- 制定详细的项目计划
- 及时更新你的计划
- 避免驻波
- 知晓十大风险
- 预先了解风险
- 使用适当的流程模型
- 方法无法挽救你
- 没有奇迹般提升效率的秘密
- 了解进度的含义
- 按差异管理
- 不要过度使用你的硬件
- 对硬件的演化要乐观
- 对软件的进化要悲观
- 认为灾难是不可能的想法往往导致灾难
- 做项目总结
七、产品保证原则
- 产品保证并不是奢侈品
- 尽早建立软件配置管理过程
- 使软件配置管理适应软件过程
- 组织SCM独立于项目管理
- 轮换人员到产品保证组织
- 给所有中间产品一个名称和版本
- 控制基准
- 保存所有内容
- 跟踪每一个变更
- 不要绕过变更控制
- 对变更请求进行分级和排期
- 在大型开发项目中使用确认和验证
八、演变原则
- 软件会持续变化
- 软件的熵增加
- 如果没有坏,就不要修理它
- 解决问题,而不是症状
- 先变更需求
- 发布之前的错误也会在发布之后出现
- 一个程序越老,维护起来越困难
- 语言影响可维护性
- 有时重新开始会更好
- 首先翻新最差的
- 维护阶段比开发阶段产生的错误更多
- 每次变更后都要进行回归测试
- “变更很容易”的想法,会使变更更容易出错
- 对非结构化代码进行结构化改造,并不一定会使它更好
- 在优化前先进性性能分析
- 保持熟悉
- 系统的存在促进了演变
总结
以上原则有的在求学阶段接了解过一点,当时觉得“软件工程”这门课程,是及其乏味的。当我从事软件开发工作后,发现软件工程是最有用的。举个例子:在我进入职业生涯第一个项目的启动会上,我的项目经理就问了一个问题并指定。“当你遇到Bug时,你应该如何解决?”,按照软件工程思维,我内心的答案是:“记录Bug,找到Bug相关者,分析Bug,解决Bug”。气氛稍微沉默了一会,经理指定我回答······对我的回答,经理还是比较满意的,她问我怎么知道的。没等我回应。“是在新人教育时学到的吧”。我只能说:“是的”(其实,是软件工程课程上学到的)。
关于此书的好处,我相信在我今后的职业生涯中,定会产生积极而深远的影响。
设计模式-软件设计的7个原则
设计模式-软件设计的7个原则
概述
在软件开发时为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,通常要遵守一定的设计原则:
- 开闭原则
- 里式替换原则
- 依赖倒置原则
- 单一职责原则
- 接口隔离原则
- 迪米特原则
- 合成复用原则
1. 开闭原则
软件实体应当对扩展开放,对修改关闭。 开闭原则是软件设计的终极目标,对扩展开放可以使软件具有一定的灵活性,同时对修改关闭又可以保证软件的稳定性。使用开闭原则设计的软件有如下优势:
- 测试方便。由于开闭原则对修改关闭,因此软件实体是拥有稳定性的,测试时只需要对扩展代码进行测试即可;
- 更好地提高代码复用性。开闭原则通常采用抽象接口的方式来组织代码结构,抽象的编程本身就是对代码的复用性提高有很大的帮助;
- 提高软件的维护性和扩展性。由于开闭原则对扩展开放,因此当软件需要升级时,可以很容易地通过扩展来实现新功能,开发效率更高,代码也更易于维护。
在面向对象开发中,实现开闭原则可以通过继承父类和实现接口两种方式。 在开闭原则中,一个类只应该因为错误而修改,新加入的功能都不应该修改原始代码。
重构前的代码
enum Color: String
case unknown
case black
case white
case gray
case blue
case red
class Style
var backgroudColor = Color.black
var textColor = Color.white
func apply()
print("皮肤 - 背景色: \\(self.backgroudColor), 文字颜色: \\(self.textColor)")
let baseStyle = Style()
baseStyle.apply()
此时,如果有一个新需求增加一个背景色为灰色,文字颜色为蓝并且按钮颜色为红的主题,如何修改?并且需要遵守开闭原则
继承
class Custom1Style: Style
var buttonColor = Color.red
override init()
super.init()
backgroudColor = .gray
textColor = .blue
override func apply()
print("皮肤 - 背景色: \\(self.backgroudColor), 文字颜色: \\(self.textColor), 按钮颜色: \\(self.buttonColor)")
let custom1Style = Custom1Style()
custom1Style.apply()
通过继承方式实现开闭原则并不彻底,通过接口可以更好的实现开闭原则。
接口
protocol StyleInterface
var backgroudColor: Color get
var textColor: Color get
var buttonColor: Color get
func apply()
extension StyleInterface
var buttonColor: Color
get
return .unknown
class BaseStyle: StyleInterface
var backgroudColor: Color = .black
var textColor: Color = .white
func apply()
print("皮肤 - 背景色: \\(self.backgroudColor), 文字颜色: \\(self.textColor)")
class Custom2Style: StyleInterface
var backgroudColor: Color = .gray
var textColor: Color = .blue
var buttonColor: Color = .red
func apply()
print("皮肤 - 背景色: \\(self.backgroudColor), 文字颜色: \\(self.textColor), 按钮颜色: \\(self.buttonColor)")
let baseStyle2 = BaseStyle()
let custom2Style = Custom2Style()
baseStyle2.apply()
custom2Style.apply()
StyleInterface 协议定义了与主题相关的属性和方法,方法需要扩展多个主题时,需要对接口进行不同的实现即可。
2. 里式替换原则
继承必须保证超类所拥有的性质在子类中依然成立。即:在进行类的继承时,要保证子类不对父类的属性或方法进行重写,只是扩展父类的功能。 如果在设计时发现子类不得不重写父类的方法,则表明类的组织结构有问题,需要重新设计类的继承关系,比如将被重写的方法从父类抽离,仅在需要的子类声明。
3. 单一职责原则
一个类只应该承担一项责任,在实际设计中,可以以是否只有一个引起类变化的原因作为准则如果不止一个原因会引起类的变化,则需要对类重新进行拆分。 如果一个类或对象承担了太多的责任,则其中一个责任的变化可以带来对其他责任的影响,且不利于代码的复用性,容易造成代码的冗余,遵守单一职责设计的程序有以下几个特点:
- 降低类的复杂度,一个类承担单一的职责,逻辑清晰,提高内聚,降低耦合
- 提高代码可读性和可复用性
- 增强代码可维护性和可扩展性
- 类的变更是必然的,功能的增加必然会产生类的变更,单一职责可以使变更带来的影响最小。
4. 接口隔离原则
将庞大的接口定义拆分为更小的和更具体的接口,其“隔离”的主要是指对接口依赖的隔离。例如 UITableView
的 UITableViewDataSource
和 UITableViewDelegate
。定义的各个接口各司其职,尽量少耦合其他业务逻辑。
5. 依赖倒置原则
高层模块不应该依赖底层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。与 开闭原则 的核心思路相同,都是要尽量减少对已有代码的修改,同时又易于进行扩展。优势:
- 由于都对接口进行依赖,减少了类之间的耦合
- 封闭了对类实现的修改,增强了程序的稳定性
- 核心是面向接口开发,减少了并行开发的依赖于风险
- 提高代码可读性和可维护性
重构前的代码: 如下代码即上层依赖下层:
class FoodStore
func sell(count: Int)
print("食品商店卖了\\(count)食物")
class Customer
func buy(store: FoodStore, count: Int)
print("购物--")
store.sell(count: count)
let customer = Customer()
customer.buy(store: FoodStore(), count: 4)
当有新的商店出现时就需要更改上层的 Customer
类。 使用依赖倒置的原则进行重构,使 Customer
只对抽象的接口进行依赖。
protocol Store
func sell(count: Int)
class FoodStore: Store
func sell(count: Int)
print("食品商店卖了\\(count)食物")
class ClothStore: Store
func sell(count: Int)
print("服装商店卖了\\(count)服装")
class Customer
func buy(store: Store, count: Int)
print("购物--")
store.sell(count: count)
let customer = Customer()
customer.buy(store: FoodStore(), count: 4)
customer.buy(store: ClothStore(), count: 2)
重构后的 Customer
不再依赖具体的 Store
,扩展也不需要更改其内部实现。
6. 迪米特原则
又叫 “最小知识原则”。核心为一个类或对象尽可能少地与其他实体发生交互作用。通常,我们不会对单独的类使用迪米特原则,这样做的解耦效果并不明显,但是如果是模块之间的交互通过一个中介类来统一处理,那就可以大大减少模块间的耦合程度,例如在iOS组件化开发中的路由器,可以将模块之间的耦合通过路由进行隔离,降低模块间的耦合。
7. 合成复用原则
在设计类的复用时,要尽量先使用组合或聚合的方式设计,尽量少使用继承。合成复用原则通过组合和聚合的方式实现复用,实现上通常使用属性、参数的方式引入其他实体进行通信。 重构前的代码:
class Teacher
var name: String
init(_ name: String)
self.name = name
func teach()
print("讲课")
class MathTeacher: Teacher
override func teach()
print("\\(name)讲数学课")
class EnglishTeacher: Teacher
override func teach()
print("\\(name)讲英语课")
let james = MathTeacher("james")
james.teach()
let davis = EnglishTeacher("davis")
davis.teach()
根据合成复用原则,不使用继承,把学科封装为Teacher的一个属性。
class Suject
var name: String
init(_ name: String)
self.name = name
class Teacher
var name: String
var subject: Suject
init(_ name: String, subject: String)
self.name = name
self.subject = Suject(subject)
func teach()
print("\\(name)讲\\(subject.name)课")
let james = Teacher("james", subject: "数学")
james.teach()
let davis = Teacher("davis", subject: "英语")
davis.teach()
总结
- 开闭原则是核心,在设计软件时保持扩展的开放性和修改的封闭性
- 里式替换原则要求在继承时不要破坏父类的实现
- 单一职责原则要求类的功能要单一
- 接口隔离原则要求接口的设计要精简
- 依赖倒置原则要求面向抽象编程,即面向接口编程
- 迪米特原则提供一种降低系统耦合性的方式
- 合成复用原则要求组织类的关系时谨慎使用继承
以上是关于《软件开发的201个原则》的主要内容,如果未能解决你的问题,请参考以下文章