最近阅读学习了《设计模式解析》一书,这本书主要解释了设计模式带来了什么好处、解决了什么问题,以及如何更好的使用它们。感觉其中有很多要点都非常的有道理,值得记录下来,在开发的时候时不时拿出来翻看,温故知新,做一个优雅的开发者。
-
修改一处代码,不应该影响其他地方,如果影响了,说明它们应该放在一起来接受变化
-
应当尝试预期所有可能发生的变化,并相应的构建代码
-
对象应该自己负责自己,而且应该清楚的定义责任
-
抽象类定义了一组类可以做什么,可以充当其他类的占位符
-
多态:当我通过抽象引用概念性地要求对象做什么时,将得到不同的行为,具体行为取决于派生对象的具体类型。
-
分析陷阱:避免过早过多的关注细节
-
按接口编程:
尽量用聚合代替继承,找出变化并封装之 -
Facade(外观) 模式简化了接口,而Adapter模式则将一个已有的接口转换成另一个接口。
-
对象
传统看法:具有方法的数据
新看法:具有责任的实体 -
封装
传统看法:数据隐藏
新看法:可以隐藏:实现细节、派生类、设计细节、实例化规则
继承可能会带来的问题
弱内聚:五边形、六边形等都作为shape的子类,那么边线绘制的逻辑会存在于每一个类中
减少了复用的可能性:难道每次想要复用五边形的绘制代码,都要继承它?
- 发现变化并将其封装:
考虑你的设计中哪些地方可能发生变化,这种方式与关注会导致重新设计的原因相反。它不是考虑什么会迫使你的设计改变,而是考虑你怎样才能够在不重新设计的情况下进行改变。
设计两步法
- 抽象类(共性) 需要用什么接口来处理这个类的所有责任
- 派生类(可变性) 对于这个给定的特定实现,应该怎么样根据给定的规约来实现它?
- 规约视角和概念视角的关系在于:规约标识了用来处理此概念所有情况所需的接口
- 规约视角和实现视角的关系在于:对于给定的规约,怎样实现这个特定情况
- 每次当我们感觉需要为什么东西加注释的时候,相反我们会编写一个方法
可测试的代码是优秀代码的主要品质
敏捷编程中非常独具特色的实践之一,就是在编写代码之前就编写测试,这有几个目的:
- 最后能得到一组自动化测试。
- 必须按方法的接口而非实现来设计,这样能得到封装更好,耦合更松散的方法。
- 关注测试会使你注意将概念分成多个可测试的部分,这样能够获得强内聚和松耦合。
-
灾难往往是由短期未甄最优的决策,长期累积而引起的。
-
针对接口进行编程,而不要针对实现编程。
-
优先使用对象组合,而不是类继承。
对象应该只对自己负责
设计者设计出的接口,没有必要考虑抽象类所有可能的派生类,这可能导致另一种分析瘫痪,只需要支持那些实际要构造的派生类即可。
- 模式并不总能提供十全十美的解决方案。但是因为模式是众多设计人员多年的集体经验结晶,所以它通常优于你我自己在有限的时间所能提出的解决方案。
-
switch语句可能说明需要抽象
-
每个部分都因其存在于更大的整体背景中而被赋予了特定的形式
-
设计应该从问题的一个简单陈述开始,然后通过在这个陈述中加入信息,使它更加详细(也更加复杂),这种信息应该采取模式的形式。
-
模式定义了问题域中实体之间的关系。
一条规则(设计模式),实现一次
模式应该按顺序每次只运用一个,首先应用那些要为其他模式创造背景的模式
开发步骤
- 找出模式:
找到问题中存在的模式,用这些模式来思考问题。请记住,模式的用途是定义实体之间的关系- 从背景模式开始:
找出为其他模式创造了背景的模式。这些模式应该作为设计的起点。然后从背景转向内部,观察其余的模式和任何其他可能已经发现的模式,从中选出为其余模式定义背景的模式,重复这一过程- 改进设计:
改进过程中始终考虑模式所蕴含的背景- 实现:
实现应该融入模式所要求的细节这是建筑学中的理论,在软件设计中一个模式可能无法成为另一个模式的背景,但我们能做到在已经出现的概念的背景中添加新概念进行设计。
-
先考虑系统中需要什么,然后再去关注如何创建它们(先考虑需要什么对象再考虑如何创建(工厂))
-
一个从基类创建的类应该支持基类的所有行为(主旨是让所有的派生类都可以彼此互换的,实现为一个空方法也是可以的)
抽象类与接口
- 抽象类允许有公共的状态或者行为
- 在不需要的时候不要使用抽象类,因为只有从一个类派生的机会
- 抽象类可以看成是一种聚集相关实体的方式,其关注点是如何设计这些具体的实体,从而可以以同样的方式使用它们。
- 通过考虑服务对象,看如何抽象它们,使用对象才不会与任何特定于实现的细节相耦合。
- 接口看上去更符合依赖倒置原则,看上去更简单,但并不代表其优于抽象类。
- 当需要定义公开的状态、行为时,也可能定义一个接口,然后用抽象类实现该接口
- 抽象类能够确定默认行为,使实现类更加简单
- 现实世界中问题往往不会秩序井然或者循规蹈矩地出现。除了那些最简单的问题,似乎总会有一些没有什么规律的异常和变化。它们是一些异常的不良特性,会破坏我们精心设计的模型。
Decorator(装饰者)模式
动态地给一个对象添加一些额外的职责,就增加功能来说,Decorator比生成子类更灵活。
Decorator模式的约束因素
- 考虑存在几种可选的功能
- 这些装饰对象可能遵循也可能不遵循所有规则
- 需要某种方式以所需的不同顺序调用这些装饰对象,但是又不能加重客户对象的负担
- 不希望应用程序必须承担知道使用哪些装饰对象的职责
Template Method模式
定义一个操作中算法的骨架,而将一些步骤延迟到子类中,不改变算法的结构而重定义它的步骤。
各种工厂模式
开发分为以下两步方法:
- 定义对象和它们的协作方式
- 编写为相应情况实例化对象并在对象共享时管理已有对象的工厂
对象要么构造其他对象,要么使用其他对象,绝不要两者兼顾
工厂封装了在什么环境下创建什么对象的规则,这样当系统的其他部分使用对象时,就可以不考虑具体的实现。
工厂方法模式
定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法模式使一个类的实例化延迟到其子类。
总结
- 对象是具有明确定义的责任的事物。
- 对象对自己负责。
- 封装指的是任何形式的隐藏。其中包括:数据隐藏、实现隐藏、类隐藏、设计隐藏、实例化隐藏。
- 使用共性和可变性分析抽象出行为和数据中的变化。
- 按接口设计。
- 将继承看成一种将变化概念化的方法,而不是创建已有对象的特殊情形。
- 将变化放入一个类中,并与该类中的其他变化解耦。
- 力求松耦合。
- 力求强内聚。
- 将使用一个对象的代码与创建该对象的代码分离。
- 在应用“一次且仅一次”规则时要绝对小心。
- 通过“按意图编程”,使用反映意图的名字,确保代码的可读性。
- 在编程之前就考虑代码的可测试性。
在模式的学习过程中,寻找以下约束因素和概念会有所帮助
- 这个模式隐藏了什么实现?这样我们就可以修改它
- 这个模式中有什么共性?这有助于你找到共性
- 这个模式中对象的责任是什么?这可以更容易地按责任进行分解
- 这些对象之间有什么关系?这将提供这些对象的约束因素的信息
- 这个模式本身怎样成为从背景设计的微观示例?这使我们能够更好地理解为什么这个模式是优秀设计
网友评论