设计模式的七大基本原则详述

    技术2022-07-14  83

    单一职责原则:每个类应该专注于做一件事情。

    里氏替换原则:超类存在的地方,子类是可以替换的。

    依赖倒置原则:实现尽量依赖抽象,不依赖具体实现。

    接口隔离原则:应当尽量为客户端提供小的单独的接口,而不是提供大的总的接口。

    迪米特法则:又叫最少知识原则,一个软件实体应当尽可能少的与其他实体发生相互作用。

    开闭原则:面向扩展开放,面向修改关闭。

    组合/聚合原则:尽量使用组合聚合来达到复用效果,尽量少使用继承。继承一定程度上是牺牲了封装性来达到复用的效果的,这样有时是得不偿失的。

    详细来分析这几大原则:

    单一职责原则较为简单,就是一个类尽量实现一个小的功能块,一个方法进行一个处理。

    依赖倒置原则:

    这个依赖较为宽泛,一般来讲就是具体依赖抽象。详细来说有子类依赖父类,子类依赖抽象类,实现类依赖接口等。

    依赖倒置就是说,编程时尽量使用接口或抽象类来代替实现类编程。换句话就是说,主代码要依赖接口或者抽象类编程,而不要依赖具体的实现类,这就是倒置。因为正常来说主代码应该依赖具体实现类的,现在反了。这样实现的好处就是可以让核心代码更加稳定,也就是在类层次组织上实现了高内聚。

    下面是一个实现案例:

    里氏替换原则:某种程度上说,其实和上面讲过的依赖倒置原则,讲的是一种事,只不过着重点不同罢了。

    就是说在编程中,所有使用的父类引用的地方都可以换成用他的子类实例,且不会有任何影响。但是这个过程反过来就不一定成立了。注意是不一定,而不是一定不(假如子类除了继承父类,没有自己的实现,那么也是可以将父类对象放到子类引用上的,做强制转换即可)。 里氏替换原则是实现开闭原则的重要方式之一。由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类对象来对对象进行定义,而在运行时再确定其子类类型。这种情况其实涉及到方法的静态绑定和动态绑定。

    静态绑定(前期绑定)是指:在程序运行前就已经知道方法是属于那个类的,在编译的时候就可以连接到指定的类中,定位到这个方法。 动态绑定(后期绑定)是指:在程序运行过程中,根据具体的实例对象才能具体确定是哪个方法。

    动态绑定是多态性得以实现的重要因素,它通过方法表来实现:每个类被加载到虚拟机时,在方法区保存元数据,其中,包括一个叫做 方法表(method table)的东西,表中记录了这个类定义的方法的指针,每个表项指向一个具体的方法代码。如果这个类重写了父类中的某个方法,则对应表项指向新的代码实现处。从父类继承来的方法位于子类定义的方法的前面。 下面看一个案例:

    接口隔离原则:

    使用多个专门的接口,而不使用单一的接口,每个接口应该承担一种相对独立的角色,不多不少。

    使用接口隔离原则时,必须满足单一职责原则,将一组相关的操作定义在一个接口中,且满足高内聚的前提下,接口中的方法越少越好。

    在应用时,一般可以用在为用户提供定制服务的方式。即为不同的客户端提供不同宽窄的接口,只提供用户需要的行为,而隐藏用户不需要的行为。如下图所示:

    合成复用原则(即组合聚合原则):

    合成复用原则就是指在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分。新对象通过委派调用已有对象的方法达到复用已有功能的目的。简言之,要尽量使用组合聚合关系,少用继承。

    在面向对象编程中,复用已有模块功能的方法有:组合与继承。 继承复用的特点:实现简单、易于扩展,破坏系统的封装性。从父类继承过来的实现是静态的,不可能通过运行时发生改变,没有足够的灵活性。

    组合聚合复用:耦合度相对较低,选择性地调用成员对象操作,可以在运行时动态进行。

    组合聚合可以使系统更加地灵活,类与类之间的耦合度降低,一个类的变化对其他类造成的影响相对较小。因此一般首先使用组合聚合来实现复用。其次才考虑继承。在使用继承时,需要严格遵循里氏替换原则,有效使用继承会有助于问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及复杂度。因此需要慎重使用继承复用。

    组合聚合的解释如下:

    组合关系:部分和整体之间具有相同的生命周期,当整体消亡后,部分也将消亡。就像大雁的翅膀和大雁是组合关系。代码实现时,部分类在整体类的构造函数中被构造,在析构函数中被析构(针对c++来说)。

    聚合关系:部分与整体之间并没有相同的生命周期,整体消亡后部分可以独立存在。就像大雁和雁群是聚合关系。代码实现时,整体类中传入一个部分类的指针,部分类已经在整体类外被构造,因而在整体类析构的时候,部分类并没有被析构。

    聚合和组合的区别在于:聚合关系是“has-a”关系,组合关系是“contains-a”关系;聚合关系表示整体与部分的关系比较弱,而组合比较强;聚合关系中代表部分事物的对象与代表聚合事物的对象的生存期无关,一旦删除了聚合对象不一定就删除了代表部分事物的对象。组合中一旦删除了组合对象,同时也就删除了代表部分事物的对象。 如下组合例子:

    如下聚合例子:

    迪米特法则:又叫做最少知识原则,原理就是一个类应该最少地组合聚合或者继承别的类。以此来保证框架的弱耦合性。迪米特法则其实是对两种代码复用方式的约束。

    从继承角度来说,迪米特法则要求类的继承层次要尽可能小,这样在子类中继承的父类数据就会相对简单,内聚性就强。

    从组合聚合角度来说,迪米特法则要求一个新类中应该少使用别的类实例,提高内聚性。

    如下示例:ClientInstance类本来是要使用A1,A2,A3,A4这几个功能类实例方法的,但是如果直接组合这几个实例对象,那么就显得耦合性很大。现在提供一个ControlManager类来管理这几个功能类,然后在ClientInstance中只需要调用ControlManager的实例,就可以达到目的。(本案例不考虑ControlManager的耦合性,因为此处仅是把它当做是一个管理类来说明这个迪米特法则。在实际的应用中,还是需要考虑的)

    综上所述设计模式的几大原则大都是围绕着高内聚弱耦合这样的思想进行处理的。

    单一职责原则便是高内聚的基本思想;组合聚合原则来降低使用继承的强耦合性;迪米特法则则又防止过度使用组合聚合带来的强耦合问题;接口隔离原则仍是体现高内聚思想;里氏替换原则、依赖倒置原则则助力实现高内聚弱耦合效果;开闭原则则是高内聚弱耦合的应用。

    Processed: 0.013, SQL: 9