降低耦合

本文介绍了程序耦合的概念及其在编程中的重要性。通过减少耦合可以提高代码的可读性和可维护性。文章提出了几个实用的方法来降低耦合度,包括函数单一职责原则、减少全局变量使用等。

 

按照软件工程学粗浅的理解,耦合就是程序中模块与模块间的关系的总和。上面的“模块”一词是一个模糊的概念,可以是一个变量、一个函数、一个类,或者是一个package(在大型软件项目中)。

显然,如果这样定义耦合的话,我们编的任何程序都有它的存在,而且它显然是不可避免的——你的程序用到了一个函数,它要调用其它函数,或被其它函数所调用,或读和写某个变量——在我们粗浅的定义里,这些都是耦合了。

但耦合不仅是函数A调用函数B所以A与B之间存在耦合这么简单。事实上,这已经是比较轻的一种耦合。它带来的后果仅仅是:如果函数B改名了、被移除了、改变功能了就会影响到A的功能的实现,这种情况在我们的信息学竞赛的编程过程中并不多见,而且很有可能在编译期就被检查和改正。我想说的是更深一点的东西: 比如,函数A和函数B共同使用某个全局的变量或数组,那么它们对它就必须有某种哪怕是最显而易见的约定,否则就会出问题。

为何要降低耦合?很简单,我们希望我们的程序尽量清晰,而不是一团蛛网或浆糊;这样利于阅读和思考,也利于修改和优化。比如说若函数A依赖于函数B的实现中某个细微的对于全局变量的副作用,那么我们可以说A和B之间的耦合很深;这可能导致我们简单地修改了B之后发现A不能正常工作 了;这可能花费我们大量的时间来寻找原因,毕竟“由于B的副作用被消除导致A的崩溃”这种事情太晦涩了,这不是我们希望看到的。

接下来说如何降低耦合:

1.一个函数只做一件事。我看到过的一些OI程序似乎在“吝啬”地使用函数。也就是说持“能少一个函数就少一个函数”的态度。然而为了减少耦合,我们应该把庞大的、笨重的东西拆成小部件,让每一个小部件和尽量少的外部的东西耦合。另外,单个函数的行数多了会导致出错这也是广泛的共识。

2.不要过早在意细节优化。我知道,有些OI程序中有很巧妙的东西,比如说在排序的同时算前缀和之类。可是,真的需要这样做 吗?为了少写几次for或者是为了还不清楚的“效率”?效率不是想当然的东西,只有Profile能告诉你真正的效率如何瓶颈在哪里。先用最清楚的逻辑描述出程序的框架,如果真的有时间效率问题那一般来说是算法复杂度的问题,在程序编好且正确之后再考虑细节优化吧。这是耦合度增加的温床。

3.减少全局变量的数量。全局变量是耦合的多发地。在软件的开发过程中甚至有些专门对付它们的模式,例如“Singleton”。相信我,你能减少它们。在这一点上我不想说太多。

4.试图面向对象。我知道很难让人像我一样,每当要在程序中用到某种数据结构——不管是最简单的队列还是复杂些的并查集或者Suffix Tree——我都会编一个class,而且很可能是Template Class。我并不是炫耀,我只是想减少耦合,我只是想使程序更清晰些。“面向对象”这个话题会在本系列中单列一篇文章来做更详尽的分析。

我不知道你是否听说过UML、MVC等等这些在软件工程界很“时髦”的缩写词。我认为这些技术(尤其是MVC)存在的主要目的就是为了减少耦合。所以说,不要再写像涂了胶水的绳结一样的程序了,试图减少耦合,从今天开始。

 

PS:可以参考http://topic.youkuaiyun.com/u/20080608/00/64beea15-2ca4-4cbd-9773-b294be76031a.html?653340851

通过预留接口降低系统耦合程度是软件设计中一种重要的策略,以下是其方法和原理的介绍: ### 原理 在软件系统中,耦合是指模块之间的依赖关系。高耦合意味着一个模块的修改会对其他模块产生较大的影响,降低了系统的可维护性和可扩展性。而接口是一种抽象类型,它定义了一组方法的签名,但不包含方法的实现。通过使用接口,可以将模块之间的依赖从具体实现转移到抽象接口上,从而降低耦合程度。具体来说,当一个模块依赖于接口而不是具体的类时,它只关心接口所定义的行为,而不关心具体的实现细节。这样,当具体实现发生变化时,只要接口保持不变,依赖于该接口的模块就不需要进行修改,从而提高了系统的灵活性和可维护性。 ### 方法 - **定义接口**:首先需要明确系统中各个模块之间的交互点,并将这些交互点抽象成接口。接口应该只包含必要的方法,并且方法的定义应该清晰明确,避免包含过多的细节。例如,在一个电商系统中,订单模块和支付模块之间的交互可以通过定义一个`PaymentService`接口来实现,该接口可以包含`pay`方法用于处理支付操作。 ```java public interface PaymentService { void pay(Order order); } ``` - **实现接口**:具体的类实现接口所定义的方法。这些类可以根据具体的业务需求提供不同的实现。例如,可以有`AlipayService`和`WechatPayService`两个类来实现`PaymentService`接口,分别提供支付宝和微信支付的具体实现。 ```java public class AlipayService implements PaymentService { @Override public void pay(Order order) { // 实现支付宝支付逻辑 } } public class WechatPayService implements PaymentService { @Override public void pay(Order order) { // 实现微信支付逻辑 } } ``` - **依赖注入**:在使用接口的模块中,通过依赖注入的方式将具体的实现类注入到模块中。依赖注入可以通过构造函数、Setter方法或注解等方式实现。例如,在订单模块中,可以通过构造函数注入`PaymentService`接口的实现类。 ```java public class OrderService { private PaymentService paymentService; public OrderService(PaymentService paymentService) { this.paymentService = paymentService; } public void createOrder(Order order) { // 处理订单创建逻辑 paymentService.pay(order); } } ``` ### 优点 - **提高可维护性**:当系统中的某个模块需要修改时,只需要修改该模块的具体实现类,而不会影响到依赖于该接口的其他模块。 - **增强可扩展性**:可以轻松地添加新的实现类来扩展系统的功能,而不需要修改现有的代码。 - **便于测试**:可以使用模拟对象来替代具体的实现类进行单元测试,提高测试的效率和准确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值