先提耦合和内聚。
耦合度:以下是耦合度的排序,衡量自己的设计属于哪个耦合度
内容耦合:不同模块,存在共享部分,直接相互引用或者共享命令
公共耦合:公共区域数据由多个模块使用,包括对模块无用的数据
外部耦合:仅共享外部声明的对模块有用的数据
控制耦合:通过传递参数调用其他模块,导致被调用方内聚度达到逻辑强度
特征耦合:模块传递公共区域没有的数据结构,但未使用全部数据
数据耦合:仅通过传递参数,得到返回数据,并且数据全部被使用,可以将被调用模块视为黑箱
内聚强度:以下是内聚强度的排序,衡量自己的设计属于哪个内聚强度
巧合强度:整合重复的命令群
逻辑强度:整合某种抽象功能
时间强度:整合同时间触发的功能
流程强度:整合处理特定问题的
通信强度:流程强度,并且内部功能相互传递信息,或者引用相同数据
信息强度:整合处理特定数据的模块
功能强度:整合处理特定功能的模块
开发中追求高内聚低耦合,高内聚模块复用性好,低耦合模块修改不容易造成连锁反应,并且可以将模块功能的修改等操作仅仅只考虑模块内部,实际中可以有意识的看自己的代码属于哪个程度,并且尽量的采取优化手段。培养一个好的习惯
根据设计原理中的同构原理,项目要力求规范,并且时刻保持着规范。
规范不仅仅只是指代码的设计,项目资源,文件夹,代码存放位置,甚至于场景中物体,都要有意识的保持整洁,清晰,简单,要注重结构性。
说一下类中的内容排版,仅供参考。这也是规范的一种情况。
私有变量—不存在set属性,或者外部不会对其造成修改,不加下划线。反之比如有set属性加下划线
属性—外部访问只能通过属性。
构造方法
公共方法—与模块外有交互,谨慎程度较高。
私有方法—仅仅考虑模块内部,谨慎程度次之。
静态方法—静态方法不包含变量,每个静态方法满足单一原则,看代码的时候只看名称和返回值,不考虑内部。所以放在最后,它是最不需要关心的。
顺带提及一下,Region,我们会使用它将一堆代码包含在一起,但是我会更建议使用部分类,分离实现与字段属性,与内部的策略。然后在更小的类中优化。
在UNIX思想中有模块化原则,在设计模式六大原则中有单一职责SRP,实际上它们都是在做一件事,让代码满足正交性(代码像两条直角边,修改任何一边,不会影响另一条边)。
举个例子,使用全局数据会最大化代码间的耦合度,共享该数据的成分关联会变得紧密,这就是违背了正交性,满足正交性可以减少阅读代码时候的外部干扰,使得代码阅读性提高。
实际中,要注意相互频繁调用的模块,相互频繁调用说明原本应该被放在一起的代码,被放在了不同的地方。
这里的效应局部化,从小到大都应该满足该原则,往小了说就是某个函数只做一件事,往大了说,对模块而言,一个模块只对更高级别的抽象者负责。
!!!但是这里可能存在一个误区,实际中我们追求的有时候可能并不是一个模块只做一件事,而是模块应该只对某一类行为者负责。仅仅只有该类行为者会需要更改它。
UNIX简单原则(+设计原理中的简单性原理)-代码要简单,理由很简单,简单的代码可读性高,要避免滥用复杂的代码。
UNIX简约原则-代码块要小,简单来说就是不要堆砌代码。
UNIX健壮性原则-函数内容透明,简单,每个模块或者函数完成的任务能简单说明,根据设计原理中的清晰原理,需要保证逻辑的清晰性,甚至附有注释,文档等辅助说明。二者结合。
UNIX表达性原则-数据比逻辑更好控制,故而建议复杂的部分交给数据,用数据表达信息。
尽量使用声明式表达(描述问题的定义,问题的性质和解决问题满足的条件),声明式表达可以忽略内部的逻辑,没有流程方面的限制,增强代码的可读性。
设计原理中的安全原理:根据软件架构中的分治(大问题分割成小问题),以及SRP,我们可以将代码划分成更小的函数,因为小,所以安全处理变得更为简单。尽可能的采用全面的方式做好安全处理,例如变量不可能为空,但是还是在函数内进行为空检查。
类的设计要符合ADT。
从局部化往上就到了层次中,既然有层次,那首先要提的就是软件架构中的抽象,抽象这边有两个观点,一个叫”舍象“,一个叫”一般化“。舍象用于提取复杂对象的抽象更高的层级,一般化用于提取多个共同特征的对象的抽象更高的层级。这边还存在一个三次原则,三次用到某个功能时,就着手“抽象化”。从而进行软件架构中的封装(相互关联的数据和逻辑合为一个模块,共同担负一个抽象概念)。
抽象可以通过两种方式出错,一:包含并非真正重要的细节,使得抽象变得不惜要的复杂。二:忽略了真正重要的细节,导致模糊不清。
这里还包含另外一个点.
策略与实现分离,策略模块(策略模块依赖于软件的前提条件),实现模块(实现模块不依赖于软件的前提条件),通常策略决定做什么,机制决定如何做。再直接点,机制就是“我能”,策略就是“我想要”。
那么有了层次,根据设计原理中的层次原理,层次上需要讲究结构层次,例如主从,前后,本末。或者金字塔原理等。
引申依赖关系上要参考-OCP开闭原则(如果A组件不想被B组件上发生的修改所影响,那么就应该让B组件依赖于A组件),DIP依赖反转(模块之间交互应该依赖抽象,而非实现),LSP里式替换(在代码中多使用抽象接口,尽量避免使用多变的具体实现类),
提及一下接口和抽象的应用场景的区别:
如果对象存在多个功能相近且关系紧密的版本,则使用抽象类。
如果对象关系不紧密,但是若干功能拥有共同的声明,则使用接口。
抽象类适合于提供丰富功能的场合,接口则更倾向于提供单一的一组功能。
不同的抽象层级存在一定关系,抽象层级的内容严格控制,不越界。
根据软件架构中的关注点分离原则,把各个关注点有关的代码集中起来成为独立的模块,例如MVC。
根据设计原理中的对称原理,上下,左右,结构要讲究对称性,结构会比较清晰,易于理解。
设计原理中的线性原理,处理流程尽量走直线,因为功能如果是由多个线性结合实现,那它的结构就会非常简单。这点要求有意识的重构代码,重构复杂的部分。
将模块分组,参考软件架构中的打包,自下而上地设计包。
简单工厂模式:创建的对象不多并且不会增加,工厂不需要抽象,具体的简单工厂可以处理。
抽象工厂模式:创建的对象是一系列相互关联或相互依赖的产品族,例如服装工厂,但是我每次只要一个其中一个牌子的一件衣服。
原型模式:对象之间相同或者相似。或者对象的创建过程比较麻烦,但复制比较简单的时候。
建造者模式:创建的对象较为复杂,产品的各个部分变化频繁,但将它们组合在一起的算法却相对稳定。
代理模式:保护目标对象,增强目标对象,无法或不想直接引用某个对象。
适配器模式:旧系统满足新系统功能,但是接口和新系统的接口不一致
桥接模式:一个类存在两个独立变化的维度,且这两个维度都需要进行扩展
装饰模式:对象的功能要求可以动态添加删除,需要排列组合一组基本功能。
外观模式:复杂系统的子系统很多,需要设计一个简单的接口供外界访问。
享元模式:存在大量相同或相似的对象,耗费大量的内存资源。
组合模式:需要表示对象整体与部分的层次结构
模板方法:算法的步骤固定,其中个别易变,提取公共方法,抽象可变方法
策略模式:动态选择算法中的一种,出现多种行为以多个条件语句出现,不同类只区别在表现行为不同
命令模式:解耦请求和接收者,随机请求或者经常删除和增加命令
职责链模式:存在多个对象链式处理一个请求,对象可选处理和移交下一个对象,或者处理部分,剩余请求移交下一个对象。
状态模式:状态决定行为,运行时可以根据状态改变行为,以及状态转换条件过于复杂。简化逻辑
观察者模式:对象存在一对多的关系。抽象模型存在两个方面独立的封装,可以独立的改变和复用。
中介者模式:对象存在复杂的网状结构关系,依赖关系混乱难以复用。创建运行多个类的对象,但是不想生成新的子类。
迭代器模式:提高多种遍历方式,遍历不同数据结构提供统一的接口。
访问者模式:对象结构稳定,操作算法经常变化。提供操作对象的多种不同且不相关的操作,避免这些变化影响对象的结构。对象结构包含多种类型的对象,抽象依赖于具体类型的操作。
备忘录模式:保存和恢复数据,存档
解释器模式:会引起效率,性能以及维护问题,一般不使用。