一起学习设计模式-设计原则

2021-05-25 17:52:40  晓掌柜  版权声明:本文为站长原创文章,转载请写明出处


一、前言

    上篇文章我们讲述了设计模式的一些基本概念;一起学设计模式-基本概念。那今天我们学习设计模式的另一个重要组成部分:

    设计原则。


二、什么是设计原则

        顾名思义,设计原则就是设计模式索要遵循的基本原则,我们知道设计模式是行业前辈们智慧的结晶,是在进行软件系统设计时不断

    累计经验、吸取教训、深刻反思得出的准则。就目前来说比较公认的设计原则包括:开闭原则、单一职责、里氏替换、依赖倒转、接口隔离、

    合成复用和迪米特法则。下面我们就以上设计原则做一一的讲解。

    2.1、单一职责(SRP)

        从命名我们就可以看出,所谓单一职责就是:一个类、模块、方法只做一件事情!但是这个单一职责是一个有些模糊的概念,

    我们在开发中的一些操作是没有严格准守这一原则的,比如属性和行为的分离我们往往会写在一起等。但是我们尽量还是要保持单一职责因为至少

    我们在后续拓展和维护时不会牵一发而动全身,造成修改一处功能后导致了其他模块出现故障的情况出现。

        eg:

            有一个账单修改的接口,负责出账单、收款、核销三个阶段的数据变更。实际场景是每个状态下的业务逻辑是有出入的,出账单时关注账单金额信息、

        收款时关注收款金额和凭证、核销时关注核销金额和流水证明。当我们把这三部分糅合在一起时,一旦任何一个环节有业务变更,你的修改都是影响

        到了接口对另外模块的职责,修改时费时费力不说,还极易导致其他模块发生故障。

            针对上述情况我们使用单一职责就能很好的解决这个问题,每个阶段的业务处理都有专门的对应接口进行处理,逻辑清晰明了,任何一处的变更也不会对

        另外的模块产生较大影响。


    2.2、开闭原则(OCP)

        所谓开闭原则就是:对拓展开放,对修改关闭。本意是保证软件系统的良好拓展性,是的我们在业务变更时可以在不修改原有代码的情况下做出友好的拓展。

        eg:

            我们同样以账单为例,现在业务需求,需要根据不同的账单阶段导出对应账单文件。核销时需要流水图片而出账单时则不需要。

        按照我们通用的习惯可能是在代码中做好if else分支处理。这显然是违背了开闭原则的。正确的做法应该是,新建一个账单模板的工厂类,

        接收账单状态然后创建对应阶段下的账单模板信息。 

   

    2.3、里氏替换(LSP)

        相较于前两个,这是一个比较难以从字面上直接得出信息的一个设计原则。其定义也比较复杂:所有引用基类的地方都必须能透明地使用其子类对象,

    即在软件中将基类替换成子类对象时也能使软件正常运行,反之则不行。(子类可以替代父类,而程序逻辑能保持不变!

    ① 之类方法必须在父类中生命、或子类必须实现父类的所有方法。

    ② 尽量把父类设计成抽象类或者接口,在其子类中集成父类或实现父类接口,并实现父类中声明的方法。运行时使用子类替换父类。


    2.4、依赖倒转(DIP)

        简单概况就是:抽象不应该依赖于细节,而是细节应该依赖于抽象。换言之,要针对接口编程而不是针对实现编程。

        高层模块不应该依赖于底层模块!两则都应该依赖于抽象。

        eg:

            我们同样以导出单据文件为例:单据的业务信息处理为高层模块、导出文件为低层模块。我们想要导出一个账单,那么基本上要有以下操作:

        账单信息的格式化、账单文件的导出(账单类,导出类,然后在账单类中进行文件导出)。

            我们思考以下这个场景:现在处理账单导出还需要有请假单的导出。发现是不能实现的,因为我们的导出模板和文件导出已经绑定在一起了,

        除非我们是不是又要新增一个请假单的类,然后再进行文件导出。

            为了降低单据模板信息和文件导出耦合度过高的情况,我们引入一个文件导出的抽象接口,然后分别让账单和请假单去实现这个接口。

        这样的话我们无论是请假单还是账单就都可以进行导出了。

        示例代码如下:

interface FileExport{

    public String getContent();

}

class Bill Implemments FileExport{

    public String getContent(){

        Syetem.out.println("账单导出");

    }

}

class Leave implenments FileExport{
public String getContent(){
System.out.println("请假单导出");
}
}

class BuinessHandle {

    public void export(FileExport fileExport){  
System.out.println("业务导出"); System.out.println(fileExport.getContent());
} }
public class Client{
public static void main(String[] args){
BuinessHandle buinessHandle = new BuinessHandle();
buinessHandle.export(new Bill());
}
}

   

    2.5、接口隔离原则(ISP)

        使用多个专门的接口,而不是使用一个大而全的接口(是不是和单一职责有些类似)。接口如果定义的过于臃肿,只要接口中存在的方法,

    不管依赖方是否需要都必须实现它,这是不符合接口隔离原则的。

        当一个接口过大时,我们应该把他拆分成多个小的接口。每个接口应承担相对独立的角色,不应该做的事情不做,应该做的事情都要做。

    同时,凡是都有一个度,接口设计过小则会使数量增多,复杂度增加。切记,我们不能手拿锤子就满眼都是钉子!


    2.6、合成复用原则(CRP)

        尽量去使用对象组合,而不是继承来达到复用的目的。简单来说复用时要尽量使用组合关系,减少继承的使用


    2.7、迪米特法则(LOD)

        软件中的一个模块要修改时,要尽量少地影响到其他的模块。


三、后记

        软件设计最大的难题就是应对需求的持续迭代,每一次复杂的变动都可能带来不可预期的结果。所以我们更应该遵循设计原则,尽量规避在持续的业务变更中

    是的自己的软件编程一坨屎山!

        另外本文只是对七大设计原则做了一些文字方面的表述,设计模式及原则是一个看不见摸不着的东西,他是一个准则,也是一个好的软件甚至是我们在开发时的设计标准。

    本文暂定为初版,后续会补充具体的业务场景和示例代码。如果本文中有任何不妥之处还请多多斧正,另欢迎多多交流!

    更多精彩,请持续关注:guangmuhua.com


最新评论: