浏览 6947 次
锁定老帖子 主题:关于接口、依赖、耦合,我的一些想法
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2012-09-18
一、对模块的理解 模块可以在逻辑和物理2个层面上进行划分 对于比较小的工程,可能会把所有的模块都放在一个工程里。这样的话,不同的模块仅仅是在逻辑上有区别,在物理上还是一致的,因此也就不存在由于依赖而无法编译的问题 对于比较大的工程,除了在逻辑上划分之外,不同的模块往往还会放在不同的工程中,这时理顺模块之间的依赖关系,就尤为重要。因为如果存在依赖缺失,或者循环依赖等问题的话,整个项目就无法编译 二、对依赖和耦合的理解 首先我认为,在开发层面,设计是为了解决职责分配、依赖和耦合的问题,本文仅讨论依赖和耦合 只要2个模块需要协同工作来完成一个功能,这2个模块就一定存在依赖和耦合。如果2个模块完全独立,没有任何依赖,实际上这2个模块就没有关系,没必要放到一起说 有些场景,2个模块在代码层面没有任何依赖,而是通过数据库、文件、JMS等其它方式来集成,那么可以认为这2个模块不存在“编译依赖”,但是依赖依然存在。假如模块A不再遵守约定的JMS消息格式,那么模块B实际上也无法继续正常使用 因此,模块之间只有紧耦合和松耦合的区别,不存在完全不耦合的模块,除非它们毫无关系。我们设计的目的,也就是使2个模块之间的依赖尽可能地减少,达到松耦合 某种意义上,我觉得耦合和依赖是同一个概念 画2个方框,分别代表模块A和模块B,有一种比较直观的方法,来判断这2个模块的耦合程度和依赖情况:如果模块A里大量依赖了模块B中的代码,那么可以简单地认为,这2个模块是紧耦合的;如果模块B仅通过若干个清晰的接口暴露给模块A,那么就认为,这2个模块是松耦合的 当然这种判断方法是否正确,只是我的个人意见 比如说,存在一个医院就诊系统,模块A是患者,模块B是医院行政部门。那么就存在2种设计。一种是患者直接依赖模块B中的挂号、诊室、药房、收银。另一种是采用Facade设计模式,患者仅仅依赖模块B中的“接待”。 采用前者,模块A就至少依赖了模块B的4个类,或者说,模块A了解模块B的实现细节。采用后者的话,模块A仅仅依赖模块B的1个类,或者说,模块A对模块B的实现细节是一无所知的。这2种设计放在一起比较,很明显是后者优于前者,而且后者的依赖关系也更加清晰 三、对接口的理解 首先,我认为不能狭义地把接口理解为interface关键字。如前文所说,有些模块或者子系统之间,是通过JMS来集成的。不管双方内部是如何实现,最终通过JMS Message来传递消息。这种情况下,就认为JMS消息是模块间的接口 关于接口,有很多说法,比如“不要针对实现编程,要针对接口编程”、“没有接口,就没有设计”等等。对于这些说法,每个人都有不同的理解,我个人对于接口有以下看法: 1、有利于多模块协同开发 接口可以认为是模块之间的契约。在开发之初,首先把接口确定下来。然后各模块就可以各自开发,只要保证遵守接口的契约即可。 比如说,2个模块通过JMS集成,那么只要把消息格式确定下来,2个模块的开发就相互不影响了 再比如说,模块A依赖模块B的InterfaceB接口,那么只要InterfaceB确定下来,模块A就可以自行进行内部的开发,不用去关心模块B的实现情况。同时也解决了编译依赖的问题,因为模块A除了InterfaceB之外,不依赖模块B的任何代码 2、有利于清晰模块间的依赖 前面说过,协同工作的模块之间肯定存在依赖关系。但是到底怎么依赖,就很有讲究,不同的依赖方式,肯定会造成松耦合和紧耦合的区别 以经典的创建对象为例,从直接new实例,到工厂,到现在的依赖注入,耦合程度不断降低 以下代码都在模块A中,假设模块A用到了模块B的Car的实例 Benz car = new Benz(); 这种方式显然是最差的,因为依赖的是具体的实现类,如果后面换了一种车型,不但客户端(模块A)需要修改,而且依赖关系完全是不稳定的。现在依赖Benz类,后面可能就依赖BMW类 Car car = new Benz(); 这种方式稍微好了一点,有了接口的定义,至少调用方法是稳定的了,但是还是没有解决依赖不稳定的问题 Car car = CarFactory.getInstance("Benz"); 这种方式就比前一种更进了一步,不但调用方法稳定,而且依赖也是稳定的,现在不管模块B有多少种Car的实现类,模块A都仅仅依赖于Car接口和CarFactory类。但是依然是由客户端来负责创建,当模块B变更了实现类,客户端的代码依然需要修改 @Autowired private Car car; 这就是现在的依赖注入,除了具备上述工厂模式的优点之外,连创建实例的职责,也转移到了依赖注入框架,模块A和模块B的依赖达到了最低(当然如上文所说,依赖仍然存在) 依赖接口,而不是依赖实现类,这种依赖比较稳定,而且范围也比较明确 3、模块内部比较稳定的类之间,不需要接口 有一种做法,就是很死板地规定一个实现类对应一个接口,哪怕在模块内部,也通过接口来调用,这种方式我倒是持保留意见,暂时没看到有什么好处 4、在三层架构中,依赖接口编程 这个和上一条有点类似,大部分项目里,在DAO层,都会有一组DAO接口对应一组DAO实现,但是实际上,替换DAO的实现类倒是几乎不会发生 但是因为这层接口,可以作为2层之间的界限,所以我个人觉得还是有意义的,可以保留 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2012-09-19
最后修改:2012-09-19
我认为Java里的interface关键字 造成我们对接口认识上的误差;
广义的接口: 就是一个约定 一套契约;比如git命令、http协议、服务器之间传输的数据、数据库数据、组件行为等等都是;“面向接口编程,而非实现” 我觉得改成“面向使用编程,而非具体”更好,我们关注的是接口完成什么功能、怎么用(输入什么数据、得到什么结果),不关心具体如何实现的。 Java里的interface关键字,我的理解 1、这个关键词的目的是强制开发人员 写接口(即写契约);这样的好处是即使开发人员水平不是很高也能定义出还不错的接口; 2、实现抽象(定义行为集合、延迟实现)和多态(动态绑定); 3、类也是接口,如果别人使用它;只要是具有交互性的就是接口; 4、我觉得是Java语言设计也是有问题,我们大部分情况下不需要单独提取一个接口,定义接口就是一个重复的工作,如果有一套类似于Javadoc的机制自动帮我们从类中抽取接口及描述不是更好,我们现在做的工作不是再重复吗? 即Java里的interface关键字定义接口的好处: 降低开发人员编码要求、延迟实现、实现多态; |
|
返回顶楼 | |
发表时间:2012-09-19
你说的模块之间的接口我的理解是面向业务的,面向服务的,方便系统集成的,对于不同模块的开发人员来说只关心各自的东西
程序里的interface关键字我的理解是抽象业务共性、方便代码扩展、降低对象之间的依赖性以及耦合度 |
|
返回顶楼 | |
发表时间:2012-09-19
我觉得关键在抽象,一开始不需要把系统搞得超级复杂,随着功能的增加,对新增功能的没有变化的部分进行抽象,这个时候可能就需要抽象类和接口了
|
|
返回顶楼 | |
发表时间:2012-09-19
最后修改:2012-09-19
个人认为,所谓开关原理,高耦合,低内聚,这些都是软件设计中所追求的一种思想,是一种标准。
我赞成楼主文中所提到的模块间的划分原则,从逻辑和物理的角度去考虑,同时补充一点,独立出一个模块或整合多个模块,也需要从模块的performance上考虑。我们为整个系统布置时更要考虑个体模块本身的效率问题,不能仅仅从模块的相关性、依赖性去考虑。 楼主说的三层架构,面向接口编程,我个人是不喜欢,也不赞成,感觉现在开发都是这种定视思维了,让框架引导了如何编程。 面向接口编程,关键是面向应用,面向服务,定义一个interface出来就面向接口了?业务意义上当然不是,这仅仅是代码语言层面的实现手法而已。 |
|
返回顶楼 | |
发表时间:2012-09-19
每个人理解不一样
|
|
返回顶楼 | |
发表时间:2012-09-19
lvjun106 写道 楼主说的三层架构,面向接口编程,我个人是不喜欢,也不赞成,感觉现在开发都是这种定视思维了,让框架引导了如何编程。 面向接口编程,关键是面向应用,面向服务,定义一个interface出来就面向接口了?业务意义上当然不是,这仅仅是代码语言层面的实现手法而已。
三层架构是不错的,只是用的人不能完全理解而已。 我个人认为,新系统应该把OOD作为强制的需求,而不仅仅是业务功能才是需求,否则系统越做越烂。 |
|
返回顶楼 | |
发表时间:2012-09-19
对于寻找相对恒久的抽象,将变数完全纳入设计的理想设计者们始终只能渐进但永远不能完全达到.
系统的空间和时间跨度越是庞大,这点越是明显. |
|
返回顶楼 | |
发表时间:2012-09-20
java是强类型,单继承的,没有接口不知道会是什么样子
|
|
返回顶楼 | |
发表时间:2012-09-20
个人认为,所谓开关原理,高耦合,低内聚,这些都是软件设计中所追求的一种思想,是一种标准。quote]
求解高耦合,低内聚?我们不是一直追求着低耦合,高内聚吗? 其实有点没错,在我们的脑海中用下interface就是用了面向接口编程的思想可谓是根深蒂固,从来没有想过真正地开发是面向应用的 |
|
返回顶楼 | |