软件设计原则和设计模式的理解

1、常见的原则有开闭原则(OCP)、单一职责原则(SRP)、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特法则。总的原则:低耦合,高内聚。

2、原则有适用范围,原则之间会冲突。

      --比如买东西的原则是物美价廉,但这两个原则有时会冲突,所以设计者有时需要找到平衡点。

      --兵法常说十则围之,五则攻之。但是,实际上有的战斗或战役,根据实际情况,也有以少胜多,或者人多仍然撤退。这就是说原则是有适用范围的。

3、开闭原则:总结出这个原则的人,语文没学好。真实的意思就是说增加需求或需求变化时,最好不要修改原来的代码,而是通过扩展的方法来实现。比如配置文件,比如新增一个实现类等等。如果代码是C代码的框架或库,如果修改了原有的代码流程或接口,会导致所有相关代码重新编译,波及很大,导致测试工作量极大,导致补丁文件极大,导致框架或库的使用者/调用者必须更新版本。如果是自己维护的应用层的代码,影响相对来说小一些,但显然还是不要修改原有代码好一些。当然,如果原有代码无法在本次发生变化的这个方面进行扩展,那只能违背这个原则,修改原有代码了。


4、摘抄一段网友对设计原则的理解

开闭原则是面向对象设计中最基础的设计原则,它指导我们如何建立稳定灵活的系统。开闭原则可能是设计模式六项原则中定义最模糊的一个了,它只告诉我们对扩展开放,对修改关闭,可是到底如何才能做到对扩展开放,对修改关闭,并没有明确的告诉我们。以前,如果有人告诉我“你进行设计的时候一定要遵守开闭原则”,我会觉的他什么都没说,但貌似又什么都说了。因为开闭原则真的太虚了。

         在仔细思考以及仔细阅读很多设计模式的文章后,终于对开闭原则有了一点认识。其实,我们遵循设计模式前面5大原则,以及使用23种设计模式的目的就是遵循开闭原则。也就是说,只要我们对前面5项原则遵守的好了,设计出的软件自然是符合开闭原则的,这个开闭原则更像是前面五项原则遵守程度的“平均得分”,前面5项原则遵守的好,平均分自然就高,说明软件设计开闭原则遵守的好;如果前面5项原则遵守的不好,则说明开闭原则遵守的不好。

         其实笔者认为,开闭原则无非就是想表达这样一层意思:用抽象构建框架,用实现扩展细节。因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节,我们用从抽象派生的实现类来进行扩展,当软件需要发生变化时,我们只需要根据需求重新派生一个实现类来扩展就可以了。当然前提是我们的抽象要合理,要对需求的变更有前瞻性和预见性才行。

         说到这里,再回想一下前面说的5项原则,恰恰是告诉我们用抽象构建框架,用实现扩展细节的注意事项而已:单一职责原则告诉我们实现类要职责单一;里氏替换原则告诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向接口编程;接口隔离原则告诉我们在设计接口的时候要精简单一;迪米特法则告诉我们要降低耦合。而开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭。

         最后说明一下如何去遵守这六个原则。对这六个原则的遵守并不是是和否的问题,而是多和少的问题,也就是说,我们一般不会说有没有遵守,而是说遵守程度的多少。任何事都是过犹不及,设计模式的六个设计原则也是一样,制定这六个原则的目的并不是要我们刻板的遵守他们,而需要根据实际情况灵活运用。对他们的遵守程度只要在一个合理的范围内,就算是良好的设计。我们用一幅图来说明一下。

图中的每一条维度各代表一项原则,我们依据对这项原则的遵守程度在维度上画一个点,则如果对这项原则遵守的合理的话,这个点应该落在红色的同心圆内部;如果遵守的差,点将会在小圆内部;如果过度遵守,点将会落在大圆外部。一个良好的设计体现在图中,应该是六个顶点都在同心圆中的六边形。

在上图中,设计1、设计2属于良好的设计,他们对六项原则的遵守程度都在合理的范围内;设计3、设计4设计虽然有些不足,但也基本可以接受;设计5则严重不足,对各项原则都没有很好的遵守;而设计6则遵守过渡了,设计5和设计6都是迫切需要重构的设计。

5、单一职责原则

     职责可大可小,有时很难说清楚。比如电话这个对象,有拨号功能,有通话功能,是否需要把这两个功能都独立成一个类?

     比如一个GUI组件,有编辑功能、过滤功能、排序功能、显示功能,其它操作功能(比如点击、双击、mouseover、keydown,keyup,回车、Esc,获取焦点、失去焦点),那些需要独立出一个类?

     需求发生变化的点,需要独立出去。实现起来比较复杂的东西,需要独立出去,不要让一个类的职责太多,函数太多,代码太多。

    我在实现一个设备面板显示的类时,把创建设备(一个设备由很多个子设备、孙设备、孙孙设备等组成)、缩放(像百度地图一样,可以以任意点为原点,用滚轮进行缩放)、布局(子设备如何摆放在界面上,要有层次,每一层要有次序,有间隔,不要覆盖。要美观。有关联关系的要尽量摆在一起,不要乱放)每个都作为一个独立类。

6、里氏替换原则:子类可以替换父类

    举个更通俗易懂的例子:基类——人,有个方法——生娃。子类可能是,亚洲人,非洲人,欧洲人,然后我让他们生娃,他们都能生出娃,而不是别的小动物或者无机物,这就满足了里氏替换原则(LSP)。

举个不满足的加深理解:基类——人,有个方法——生娃。子类有超人,机器人,蜘蛛人。看似都具有人的基本特征,但是其中机器人让他生娃,是无法做到的,所以不满足里氏替换,所以机器人不能作为人的基类去定义。


7、依赖倒置

依赖抽象,不要依赖具体。虽然你用的是具体的实现。

比如操作数据库,应该定义通用的数据源,并进行操作。而不是具体的某种数据源(JNDI方式创建DataSource、Apache提供的简单连接池创建数据源、C3P0方式创建数据源、Proxool方式创建数据源、BoneCP方式创建数据源)。

最好是让创建者把具体的数据源注入进来,这样如果某个项目更换数据源,数据库操作模块都不需要修改代码。--这就是依赖注入。

说道依赖注入,就不得不说控制反转。spring很流行,其核心思想就是控制反转,依赖注入。

控制反转,就是说你需要用某个实例时,不要像以前那样去new一个对象。而是让框架提供给你这个实例。这样的好处是你看不到具体实现类。

可以用依赖查找、依赖注入、依赖拖拽实现控制反转。

依赖查找,比如通过jndi查找依赖的东西。依赖注入,比如通过构造函数可以注入依赖的东西,也可以通过set函数注入。依赖拖拽,比如可以通过bean工厂获取依赖的东西。

8、迪米特法则:一个类应该对其他类保持最少的了解。

10、门面模式、代理模式

以前接手的一个模块,对外提供接口时,将自己的一些实现细节暴露了,导致调用方很复杂,而且自己的实现一旦发生变化,还要求调用者进行修改和兼容老版本。

后来增加了一个接口人子模块,统一对外提供接口。这就是门面模式。

我们的系统以前只支持TCP接口,后来又有需求要支持SNMP接口,再后来还要支持http接口。于是,又增加了snmpProxy, httpProxy模块。调用者调用snmpProxy的接口后,snmpProxy又调用接口人模块。这就是代理模式。

门面模式是提供统一接口,屏蔽内部实现。代理模式是要实现某种通用特性,有面向切面的意思。

不过,这个例子中,如果认为snmpProxy是适配器模式,好像更合适一些,适配不同的协议。代理模式,像远程代理,ftp代理似乎更典型一些。


11、调停者模式

我们有一个比较大的系统,系统有好几个模块,模块是分布式部署的,也就是说按照业务规模,可以把所有模块都部署在一台服务器上,也可以把负载最重的某个模块独立部署在一台服务器上,或者每个模块都独立部署。另外,每个模块还是可以多实例的,也就是说,业务规模很大时,一个模块可以跑多个实例,每个实例独立部署在一台服务器上。 这样,模块之间互相访问就比较困难了,你不知道别人在哪个机器上,也不知道别人现在有几个实例,自己应该调用哪个实例的接口。

于是,调停者模式就大显身手了。他了解每个模块的部署情况,也了解每个实例分担的范围(比如实例1管理苏州的业务,实例2管理杭州的业务)。大家都把消息发送给调停者,由调停者派发消息并返回接口。 这个模块被我们成为消息总线。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值