为什么我停止使用Spring?

这是来自 JOHANNES BRODWALL 的一篇博文,他曾经在Dzone上发表谦卑的架构师一文引发了争论,在这篇博文中他正式声明停止使用Spring。下面是原文大意翻译:

我对Spring和依赖注入框架的意见引起争论,我是挪威Spring最早的使用者之一,我们开发一个庞大系统,我们最终不得不考虑一些事情,如XML配置的重用机制,Spring的@Autowire和组件扫描功能帮我们解决这个问题,但是作为回报,降低了我们质疑整个源码的能力,将我们开发人员隔离在一个个小小的孤岛上,这些框架往往以复杂性为文化,工具 文档和其他不必要的层等待。

后来我尝试不使用依赖注入框架来构建应用程序,让我决定什么使用new,什么时候有一个setter或构造器参数,什么类型能被作为依赖,哪个类型创建会与底层耦合。我发现了一些DI容器能改进设计的好处,同时,我去除了容器,我的解决方案代码变得越小了,易于浏览和理解,容易测试,我陷入了两难底部。

我发现,使用容器的成本非常高,它提高了复杂性和规模,降低了凝聚,对于我来说,小的系统价值远远大于创建一个松耦合后又松耦合的系统。

凝聚性和松耦合是对立,模块A重用模板B,模块A和B耦合了,B的变化会影响A,因此,重用和松耦合是对立,这时我偏重松耦合。当有冲突时,我将连贯性凝聚性看高于松耦合,而松耦合高于重用。Spring文化好像与这个正好相反(凝聚性最差)。

个人顶这位架构师。spring的标签让我很不习惯,代码零散,看起来难受,东跳西跳的看代码。注入比标签好点。我不能控制代码,而是交给别人去控制,让我很不放心(我不是有强烈的控制欲吧?)。
大家都叫高内聚松耦合,为啥不叫全内聚零耦合,有些事情过犹不及。

[该贴被masterice于2013-12-15 09:41修改过]


2013-12-15 09:35 "@
masterice"的内容
高内聚松耦合,为啥不叫全内聚零耦合 ...

我也基本同意:凝聚>松耦合>重用。

以前为了重用而重用,导致紧耦合,后来为了松耦合而松耦合,导致一地鸡毛,其实如果抓住内聚这个核心,纲举目张,其他两个问题就解决。

打个比喻,为松耦合而松耦合,类似我们拿了一把刀,把系统像切黄瓜一样切,切得很薄,这是为了切而切,没有尊重被切物自身规律。想象一下,如果是水,你怎么用刀切呢?而这时,通过增强事物内部凝聚力,将水放到冰格中冰冻,这将会增强水分子之间引力也就是凝聚力,水自然变成一个个碎冰块,也就是松耦合了。

顺其自然,巧妙利用自然之道才能与事物和谐相处,而不是任由强大的内心粗暴迸发,从而对事物形成冲击力后任加蹂躏。

不太明白你们说的这部分内容,但很好奇,想求老大进一步用代码的方式来阐述一下这个问题,最好是一个简单易懂的示例,谢谢!
[该贴被sinaID99267于2013-12-16 17:01修改过]


2013-12-16 17:00 "@
sinaID99267"的内容
想求老大进一步用代码的方式来阐述一下这个问题,最好是一个简单易懂的示例,谢谢! ...

这个问题其实是SOA架构本身的问题,因为Spring是SOA架构中的一个实现,所以也带有这个问题。

SOA架构的问题来自于其面向服务自身,用服务来对业务切分,这样,可能破坏业务自身的聚合性。也就是说,业务逻辑会散落在多个服务之中,想修改代码,要修改多个服务。

服务的抽象主要是从重用角度抽象的,如有很多客户都要下订单,那么我们就抽象一个订单服务提供给这些客户;然后又有很多客户要签合同,那么我们就抽象一个合同服务于这些客户;这些服务都是从对外接口的角度粗粒度设计,或者只是纯粹从业务流程来考虑的,并没有从业务内部领域逻辑角度考虑,有人说,这两者不是一样吗?有时一样,有时不一样。比如某个单位无论给订单客户还是合同客户,都有一个统一的最低底线价格,按照面向服务的设计,这个底线价格会各自写到订单服务和合同服务中。

有人说,那订单服务与合同服务共同重用一个实体名叫价格策略。这种重用其实引发了依赖,紧耦合,破坏了松耦合。

比如订单服务代码:


public class OrderService{

public int getPrice(PricePlocy pricePlocy){
..
}
}

合同服务:


public class ContractService{

public int getPrice(PricePlocy pricePlocy){
..
}
}

这两个服务都依赖PricePlocy,就算PricePlocy是个接口,如果接口发生变化,这两个服务的代码就发生变化。

那么有人说,将PricePlocy独立出来成为一个PricePlocyService,三个服务通过ESB消息总线交互。那这样服务的粒度就太细了,如果再发生PricePlocy2,难道你再搞个PricePlocy2Service吗?

这里就发生了重用与松耦合矛盾的情况,重用的角度是从为客户服务这个外部因素考虑的,而不是从业务内部去发现领域规律。

如果从内部去考虑,发现内部聚合,自然外界接口就松耦合,比如订单服务和合同服务,背后有一个价格制定策略,这个是核心,是DNA,我们可能将定价算法作为一个聚合根,订单和合同只是定价这个实体的外部两个服务接口而已。

面向服务和面向领域的区别就是一个从外部考虑,一个从内部考虑,考虑方向完全不同。两者可以互补。如果片面从外部考虑,导致各个服务对领域的强行肢解,流程变动方便了,但是业务逻辑的一贯性凝聚性被破坏了。


面向服务和面向领域的区别就是一个从外部考虑,一个从内部考虑,考虑方向完全不同。两者可以互补。如果片面从外部考虑,导致各个服务对领域的强行肢解,流程变动方便了,但是业务逻辑的一贯性凝聚性被破坏了。

说的很清楚。

2013-12-16 20:57 "@banq"的内容
我们可能将定价算法作为一个聚合根,订单和合同只是定价这个实体的外部两个服务接口而已。 ...

那么上面的代码变成什么样子了?

接口发生变化,服务要重写。。。

这个才是核心

接口是不能变化的,至少设计接口时的本意是这样的

并非spring围绕接口做文章不对,而是一般通常对领域需求理解不透彻,很难提炼出合适的接口,导致接口可能会变化。

接口变化了,等同于api变化,等同于基类变化,通常也是由于需求发生变化。。。后果当然严重了。。。