1.什么是好的架构
好的系统架构展示了架构系统的完整性。也就是说,它来自于一组设计规则,这组规则有助于减少复杂性,并可以用于指导详细设计和系统验证。
(1)具备客户要求的功能
(2)能够在要求的工期之内安全的构建;
(3)性能足够好;
(4)是可靠的
(5)是可用的,并且使用的时候不会造成伤害
(6)安全的
(7)成本是可以接受的
(8)符合法规标准
(9)将超越前人及其竞争者
2. 架构中的设计原则
在使用面向对象的思想进行系统设计的时候,前人总结出了7条原则:单一职责原则、开闭原则、里氏替换原则、依赖注入原则、接口分离原则、迪米特原则和优先使用组合而不是继承原则
2.1 单一职责原则(Single Responsibility Principle,SRP)
单一职责原则的核心思想就是:系统中的每一个对象都应该只有一个单独的职责,而所有对象所关注的就是自身职责的完成。
单一职责原则就是“高内聚、低耦合”;每个类应该只有一个职责,对外只能提供一种功能,而引起类变化的原因应该只有一个。在设计模式中,所有的设计模式都应该遵循这一原则;
SRP的好处:可以消除耦合,减小因为需求变化引起的代码僵化的问题;
注意:
- 一个合理的类,应该有一个引起它变化的原因,即单一职责;
- 在没有变化征兆的情况下应用SRP或者其他的原则是不明智的;
- 在需求实际发生变化的时候,就应该应用SRP等原则来重构代码;
- 使用测试驱动开发会迫使我们设计出现劣质趋势之前分离不合理的代码;
- 如果测试不能迫使职责分离,僵化性和脆弱性就会越来越明显,那就应该用Façade或者Proxy模式对代码重构;
2.2 开闭原则(Open for Extension,Closed for Modification,OCP)
开闭原则的核心思想:一个对象对扩展开放,对修改关闭;
意思就是:对类的改动是通过增加代码进行的,而不是改动现有的代码。也就是说,软件开发人员一旦写出了可以运行的代码,就不应该去改变它,而是要保证它能够一直运行下去。
想要做到这一点就需要借助于抽象和多态,即能把可能变化的内容抽象出来,从而使抽象的部分是相对稳定的,而具体的实现层则是可以改变和扩展的。
根据开闭原则,我们改变一个软件时(比如扩展其他的功能),应该通过扩展的方式来实现软件的改变,而不应该靠修改原有的代码来实现改变;
最终目的就是为了提高程序的复用性、可维护性等要求;
2.3 里氏替换原则(Liskov Substitution Principle,LSP)
里氏替换原则的核心思想:在任何父类出现的地方都可以使用它的子类来替代。
简单来讲意思就是:同一个继承体系中的对象应该有共同的行为特征。里氏替换原则关注的是怎样良好的使用继承,也就是说不要滥用继承,它是继承复用的基石。
在里氏替换原则中,所有引用父类的地方必须能够透明的使用其子类对象。也就是说,只要父类出现的地方,子类就能够出现,而且替换为子类不会产生任何的错误或者异常;
但是反过来,子类出现的地方,使用父类对象就可能出现问题了;
这个原则是为良好的继承定义了一个规范,4层含义:
(1)子类必须完全实现父类的方法;
(2)子类可以有自己的特性;
(3)覆盖或者实现父类的方法时,输入参数可以被放大;
(4)覆写或者实现父类的方法时输出结果可以被缩小;
总结就是父类可以出现的地方子类就可以出现,而且替换为子类对象不会产生任何的错误或者异常,使用者也无需知道是父类还是子类。但是反过来就不行了,有子类出现的地方,父类就未必适用。因为毕竟子类的范围要大于或者等于父类的范围;
2.4 依赖注入原则(Dependence Inversion Principle,DIP)
依赖注入的原则的核心思想就是:要依赖于抽象,不要依赖于具体的实现。
直接的好处就是将注入的范围增大,以后新添加的功能可以轻松地改动;
意思就是:在应用程序中,所有的类如果使用或者依赖与其他的类,则都应该依赖与这些其他类的抽象类,而不是这些其他类的具体实现类。抽象层次应该不依赖于具体实现的细节,这样才能保证系统的可复用性和可维护性。为了实现这一原则,就要求开发人员在编程的时候针对接口编程,而不针对实现编程;
依赖注入原则有如下三点说明:
(1)高层模块不应该依赖底层模块,两者都应该依赖于抽象(抽象类或者接口);
(2)抽象(抽象类或者接口)不应该依赖于细节(具体实现类);
(3)细节(具体实现类)应该依赖抽象;
依赖注入原则的本质其实就是通过抽象(抽象类或者接口)使各个类或者模块之间彼此独立,不相互影响,实现模块之间的松耦合。
这个原则是6个设计原则中最难实现的,如果没有实现这个原则,意味着开闭原则也就无法实现了;
依赖注入原则的实现方式:
(1)通过构造函数传递依赖对象;
例如在构造函数中需要传递的参数是抽象类或者接口的方式实现;
(2)通过setter方法传递依赖对象;
即设置的setXXX方法中的参数为抽象类或者接口,来实现传递依赖对象;
(3)接口声明实现依赖对象;
【目的】利用依赖注入的原则可以实现各个类或者模块之间的松耦合;
2.5 接口分离原则(Interface Segregation Principle,ISP)
接口分离原则的核心思想:不应该强迫客户程序依赖它们不需要使用的方法。
实质就是:一个接口不需要提供太多的行为,一个接口应该只提供一种对外的功能,不应该把所有的操作都封装到一个接口中;
这里的接口指的不仅仅是通过interface关键字定义的接口,接口分为如下两种:
(1)对象接口(Object Interface)
Java中声明的一个类,通过new关键字产生的一个实例,它是对一个类型的事务的描述,这也是一个接口;
例如:Phone phone = new Phone();//这里类Phone就是实例phone的一个接口;
(2)类接口(Class Interface)
这种接口就是通过关键字interface定义的接口;
接口分离原则要求的是在一个模块中应该只依赖它需要的接口,以保证接口的小纯洁。而且需要保证接口应该尽量的小,即接口设计的时候应该让接口尽量的细化,不要定义过于臃肿的接口。
接口分离原则和单一职责原则有些相似,不同之处在于:单一职责原则要求的是类和接口职责单一,注重的是职责,是业务逻辑上的划分。而接口分离原则要求的是接口的方法尽量的少,针对一个模块尽量的有用;
在使用接口分离原则的时候,需要有一些规范:
(1)接口尽量小:为了保证一个接口只服务于一个子模块或者业务逻辑;
(2)接口高内聚:接口高内聚是对内高度依赖,对外尽量隔离。即一个接口内部声明的方法相互之间都与某一个子模块相关,而且这个子模块必须的;
(3)接口设计是有限度的:如果完全的遵循接口分离原则的化,会出现接口设计力度会越来越小,接口的数量会剧增,系统的复杂度一下子增加了。
2.6 迪米特原则(Law of Demeter,LOD)
迪米特原则的核心思想就是:一个对象应当对其他的对象尽可能的少了解。意思就是降低各个对象之间的耦合关系,提高系统的可维护性。在模块之间,应该只通过接口来通信,而不理会模块的内部工作原理,它可以使各个模块耦合程度降到最低,促进软件的复用;
迪米特原则的核心观念就是:类间解耦、弱耦合。
【使用迪米特原则注意事项】
- 在类的划分上,应该创建有弱耦合的类;
- 在类的结构设计上,每一个类都应该尽量降低成员的访问权限;
- 在类的设计上,只要有可能,一个类应该设计成不变类;
- 在对其他的类的引用上,一个对象对其他的对象的引用应该降到最低;
- 尽量降低类的访问权限
- 谨慎使用序列化功能;
- 不要暴露类成员,而应该提供相应的访问器(属性);
2.7 优先使用组合而不是继承原则