介绍
元认知,metacognition,关于思考的思考,教育心理学上的专有名词。管理学习和认知的过程,提高学习的效率。
让你的大脑顺从你的方法
良好应用程序的基石
伟大软件的意义
伟大软件的三步骤
别为了解决旧问题而产生新问题。
为正在试图解决的问题用文字描述清楚,确保你的设计与应用程序想实现的功能一致。
对象类型(object type)
封装(encapsulation)让你将应用程序分成一组一组合乎逻辑的部件。
任何时候看到重复程序代码,就找个地方进行封装!
委托:一个对象将操作转交给另一个对象的动作,第二个对象代表第一个对象执行该操作。
OOA&D
要点
收集需求
需求:需求是单一的需要,详细说明特定产品或服务应该做的事。最常用在系统工程或软件工程的正式用语中。
It's a specific thing your system has to do to work correctly.
取得好需求的最佳方式就是了解系统应该做什么。
A use case describes what your system does to accomplish a particular customer goal.
用例:用例是捕捉新系统或软件变更的潜在需求的技术。每个用例提供一个或多个场景(scenario),传达系统如何与终端用户(end user)或其他系统交互以实现特定目标。
一个好的用例有三个基本部分
你的系统必须在真实世界里运作,所以要为事情出错的情况进行规划与测试。
要点
需求变更
需求总是在变。然而,假如有很好的用例,你通常能快速地改变你的软件以适应这些新需求。
从第一步到最后一步通过用例的完整路径称为场景。大部分的用例有一些不同的场景,但总是有相同的用户目标。
任何时候你改变了用例,你就必须回头检查你的需求。
有时候,需求的变更揭露出关于系统你所不知道的问题。变更是经常的,随着你每次的实现,系统总是随之改善。
要点
需求
分析
分析帮助你确保系统运作在真实世界的情境里。
以对客户、老板及自己合理的方式编写你的用例。分析及用例让你给客户、经理和其他开发者展示系统在真实世界的情境里如何运作。
委托保护你的对象免受软件中其他对象实现改变的干扰。
查看用例里的名词(与动词)以整理出类与方法的动作叫做文本分析(textual analysis)。
好的用例以容易理解的语言,清楚且准确地解释系统在做什么。有了良好、完整的用例,文本分析是整理出系统所需类的简单且快速的方式。
注意用例里的名词,即使它们不是系统里的类。想想你确实有的类如何支持用例所描述的行为。
从一个类到另一个类的实线称为关联(association),它表示一个类与另一个类相关,通过引用(reference)、扩展(extension)、继承(inheritance)等来实现。这条线从来源类(source class)通往目标类(target class)。数字是该关联的多重性(multiplicity),表示有多少个目标类型被存储在源类的属性里。
要点
良好的设计=灵活的软件
抽象类是实际的实现类的占位符(placeholder)。抽象类定义行为,而子类实现该行为。
每当你在两个或两个以上的地方找到共同行为时,小心地将该行为抽取到一个类里,然后以此共同类重用这项行为。
聚合以空心菱形实线表示,表明一个类实际上拥有但可能共有(own but may share)另一个类的对象,例如作者owns论文(而且可能只是共同作者)。而合成是比聚合还要强的关系,以实心菱形实线表示,表明另一个类的对象实际上是此类本身的一部分(a part of),并且通常不与系统其他部分共有,例如摘要与正文are parts of论文(论文是不会与别的论文共用正文和摘要的,如果论文被删除,摘要与正文也会被删除)。
要看看软件是否设计良好的最佳方式之一是试着改变它。
对接口编码,而不是对实现,让你的软件更容易被扩展。通过对接口编码,你的程序代码将使用该接口的所有子类,甚至是还没被创建的那些。
设计是反复进行的(iterative)……你必须愿意改变自己的设计以及你从其他程序设计师那里所继承的设计。
注意!自尊心扼杀良好的设计。别害怕查看自己的设计决策及改善它们,即使那意味着进度倒退。
当你有一组特性跨对象变化时,使用集合(collection),像Map,来动态存储那些特性。你将从你的类中移除许多方法,并且在新特性被加入应用程序时避免改变你的程序代码。
大多数的好设计都是通过分析坏设计而来的。不要害怕犯错及改变。
A cohesive class does one thing really well and does not try to do or be something else.
内聚力,cohesion。内聚力量度单一模块、类或对象内各元素之间的连接度(degree of connectivity)。软件的内聚力越高,应用程序中每个类的责任就定义得越好且越相关(well-detined and related)。每个类就具有特定一组紧密相关的动作要执行。
分析与设计
解决大问题
用解决小问题的相同方式解决大问题。
你可以将大问题分解成许多功能片段,接着就单独解决每个片段。
看待大问题的最佳方式就是化整为零,将它视为许多单独的功能片段(pieces of functionality)。你可以将那些片段的每一个创建为要解决的单独的问题,并且运用你已经知道的每一件事。
从客户处取得功能(feature),接着整理出实现这些功能所需要的需求。
只要可以,就尽量把细节往后拖延。
使用功能或需求列表来捕捉系统需要做的“大事”。
绘制用例图来显示你的系统是什么,无需深入不必要的细节。
领域分析让你检查你的设计,并且是以客户所用的语言。
领域分析。识别、收集、组织及表示领域相关信息的流程,根据的是现有系统与其开发历程的研究、领域专家捕捉到的知识、潜在的理论以及领域里新兴的技术。
别忘了真正的客户是谁。
领域分析帮你避免构建不属于你的责任范围内的系统部分。
OOA&D的威力
要点
解决大问题
架构
Architecture is your design structure, and highlights the most important parts of your app, and the relationships between those parts.
架构。架构是系统的组织结构,包含分解开来的各个部件、它们的连通性、交互机制以及你在系统设计中使用的指导原则与决策。
应用程序中真正重要的事情是架构上重要的事(architecturally significant),你应该先把焦点置于其上。
架构三问
系统的本质是指在最基本的层次上系统是什么。
一次把焦点放在一个功能上,减少项目的风险。不要为无助于减少风险的功能分心。
好的设计总是会减少风险。
有时候,编写伟大程序代码的最佳方式,是在允许的情况下将程序代码的编写往后顺延。
确定某项功能的意义及如何实现
1. 询问客户 -- 该功能的意义为何? --> 2. 共同性分析 -- 如何在我的系统中实现该功能? --> 3. 实现计划
客户不为伟大程序代码付给你钱,而是为伟大软件付给你钱。
要点
设计原则
模拟是避免做傻事的最佳方式。
设计原则是能被应用到设计或编写程序代码中的工具或技术,让程序代码更可维护、更具灵活性或者更易扩展。
使用已被证实的OO设计原则形成更可维护、更具灵活性以及更易扩展的软件。
开闭原则(OCP):类应该允许为扩展而开放(open for extension),禁止为修改而关闭(close for modification)。(对扩展开放,对修改关闭。)
OCP关系到灵活性,而不只是继承。
不自我重复原则(DRY):通过将共同之物抽取出来并置于单一地方来避免重复的程序代码。
DRY关系到让系统中每一个信息与行为的片段都保存在单一、合理的地方。
单一职责原则(SRP):系统里的每一个对象应该具有单一职责,所有对象的服务都应该聚焦在实现该职责上。
当你的每一个对象都只有一个改变的理由时,你已经正确地实现单一职责原则。
Liskov替换原则(LSP):子类型(subtype)必须能够替换其基类型(base type)。
LSP完全关系到设计良好的继承。当你从一个基类继承下来时,你必须能用你的子类替换该基类且不会把事情弄糟,否则你就错误地使用了继承。
Make it Stick
Liskov所提出的关于继承的原则——继承必须确保父类型所拥有的特质(属性与方法),对子类型仍然成立,也就是说,当子类型能替换其父类型时,才算具有正确合理的继关系。
委托(delegation)是将特定工作的责任委派给另一个类或方法。
假如你需要使用另一个类的功能性,但不想改变该功能性,考虑以委托代替继承。
组合(compositon)让你使用来自一组其他类的行为,并且可以在运行时切换该行为。
在组合中,由其他行为所组成的对象拥有(own)那些行为。当对象被摧毁时,其所有行为也被摧毁。组合中的行为不存在于组合本身以外。
聚合(aggregation)是当一个类被用作另一个类的一部分时,但仍然可以存在于该类之外。
假如你喜欢委托、组合与聚合胜过继承,你的软件通常会更灵活,更易维护、扩展与重用。
要点
OO原则
迭代与测试
伟大软件的编写是迭代进行的。先针对整体轮廓操作,接着迭代应用程序的每个片段,直到完成。
功能驱动开发(Feature Driven Development)挑出应用程序的特定功能,并且规划、分析及开发该功能,直到完成。
用例驱动开发(Use Case Driven Development)挑出通过用例的场景(scenario),并且编写程序代码以支持通过该用例的完整场景。
两种迭代(iterating)方式皆由良好的需求(requirement)所驱动。因为需求源自客户,两种方式都是聚焦在交付客户要的东西上。
功能驱动开发比较细粒化(granular),用例驱动开发比较“整体化”(big picture)。
你应该为所有你能想到的可能的使用状况测试你的软件。要有想象力!也别忘了测试软件不正确使用的状况。你将在早期捕捉住错误,让你的客户高兴高兴。
测试驱动开发聚焦于让类的行为正确。
设计决策总是一种取舍(tradeoff)。
良好的软件是通过迭代造就而成。分析、设计、再一次迭代,一次一次完成应用程序更小更小的部分。每当你迭代时,重新评估你的设计决策,假如它对你的设计合理,就别害怕改变。
测试案例解析
当你按契约编程(program by contract)时,你与软件的用户正同意该软件以特定方式行动。
防御性编程假设最坏的状况,并试着保护它自己(跟你)免于误用或受到坏数据的侵扰。
当你采取契约式编程时,你正在与客户的程序代码合作,以对你将如何处理有问题的状况达成协议。
当你采取防御性编程时,你正在确保客户获得“安全的”响应,不管客户要什么。
要点
开发方式
编程实践
OOA&D生命周期
你的功能列表完全关系到理解你的软件应该做什么。
用例图让你开始思考你的软件将如何被使用,而不需深入一堆不必要的细节。
系统的功能是系统所做的事,而且不是总直接反映在你的用例里,用例显示系统如何被使用。功能与用例一起工作,但它们不是相同的东西。
你的设计决策应该基于你的系统如何被使用以及良好的OO原则。
你应该只将客户需要与之交互的类暴露给客户。不与客户交互的类可以在对客户端程序代码影响最小的情况下被改变。
你的职责是在确认客户得到想要的功能性与确保程序代码具有灵活性且设计良好之间取得平衡。
有时候,特定问题的最佳程序代码已经被编写过了。假如某人已经拥有有效的解法,就别硬是要自己编写。
OOA&D关系到许多选择。绝对没有唯一正确的方式来解决问题,因此拥有的选择越多,就越有机会为每个问题找到好解法。
附录1
使用继承的时机是当一个对象的行为类似于另一个对象的行为时,而不只是因为IS-A关系成立。
反设计模式是设计模式的反面:它们是问题共同的“坏”解法。这些潜藏的危险应该被识别出来并且被避免。
CRC代表类(Class)、职责(Responsibility)与协作者(Collaborator)。
缺陷密度 = 程序代码中发现的缺陷 / (程序代码总行数 / 1000)
编写具有可读性的程序代码,让程序比较好维护及重用,对你与其他开发者都一样。
重构改变程序代码的内部结构,而不影响程序代码的行为。
附录2
继承让你基于其他类来构建类,避免重复及反复出现的程序代码。
封装保护程序代码里的信息免于被不正确地使用。
封装是将类的实现隐藏起来,好让它容易使用与改变。封装让类以黑盒(black box)的方式提供服务给它的用户,但不开放该程序代码让其他人改变或者以错误的方式使用它。封装是遵循开闭原则(OCP)的关键技术。
封装:将编程元素(programming elements)包含在较大、较抽象的实体内的过程,也被称为信息隐藏(information hiding)或关注点分离(separation of concerns)。
封装将数据与应用程序的行为分离开来。于是你可以掌控每一个部分如何被应用程序的其他部分使用。
要点
词汇
继承 inheritance
多态 polymorphism
封装 encapsulation
循环遍历 loop through
主要路径 main path
替换路径 alternate path
可选路径 optional path
聚合 aggregation
合成 composition
泛化 generalization
复原力 resilient
耦合度 coupling
低耦合 loosely coupled
内聚力 cohesion
内聚性 cohesive
高内聚 highly cohesive
同级评审 peer review
双封装 double-encapsulation
更容易变更 Ease-of-Change
交互依赖 interdependent
整体轮廓 big picture
共同性 commonality
变化性 variability
外挂包 add-on pack
用例图 use case diagram
蓝图 blueprint
领域分析 domain analysis
开闭原则 OCP, Open-Closed Principle
不自我重复原则 DRY, Don't Repeat Yourself Principle
单一职责原则 SRP, Single Responsibility Principle
Liskov替换原则 LSP, Liskov Substitution Principle
组合对象 composing/composite object
主要对象 main object
被组合对象 composed object
功能驱动开发 Feature Driven Development
用例驱动开发 Use Case Driven Development
分析瘫痪 analysis paralysis
原子性 atomic
初步设计 prelimiary design
现成可用的代码 ready-bake code
反设计模式 anti-pattern
度量 metrics
抽象化度量 abstraction metric
顺序图 sequence diagram
生命线 liftline
状态机图 state machine diagram
警戒表达式 guard expression