2021.9.14 软件设计——设计原则

一、今日学习内容

1--------开闭原则:一个软件实体如类,模块和函数应该对扩展开放,修改关闭!

背景:一般情况下,在系统相对稳定的时候,一个Dao类都是通过实现一个抽象接口来完成一个Dao类,但是当我们对于某个Dao类的实现并不满足。我们需要在原来的Dao基础上新增方法或者模块。对于专属专用的接口自然比较容易,但是对于公共的接口或者职能清晰的接口,每一次的改变都是需要对系统的稳定和架构进行分析,对我们并不是那么随便可以改的!

>>>>开闭原则则是指:在这种背景下,为了不破坏系统的稳定性,遵循其理对其进行扩展,而拒绝修改!

运用:

 

解析:ICourse与javaCourse是系统设计之初正常业务下的接口与实现关系,其作为初始系统的考虑已经是丰满而足够的,现在因为业务的扩展不再满足,我们需要对当前的方法进行扩展!一般情况下,我们一般是不再去修改其代码,而是对当期实现类进行扩展,根据需要对原来方法的代码进行重载(重载而非重写)

扩展:对类进行扩展后,我们的调用也是使用接口引用指向扩展类对象的方法 这样做的原理是利用运行时多态的特性。当父类引用指向子类对象的时候,被声明的是父类对象,被编译的却是子类对象,子类在被实例化的时候会将父类也一同编译!因此当使用父类引用去调用静态时,静态变量是属于父类的,当调用方法的时候,因为父类引用是属于父类的,调用不了子类的方法,只能调用父类的方法。唯有当该父类声明强转为子类对象后,则可以调用到子类的方法,如下:


解析:接口类引用 指向 子类声明。调用的静资源是接口类的实现类。

---------------------------------------------------------------------------------------------------------------------------------------------------------------------

2-----依赖倒置原则:上层模块不应该依赖下层模块 ,二者都应该依赖其抽象!

背景:依赖倒置的情况比较特殊,但是效果的改变是最劲爆的,属于针对接口编程的经典! 一般而言,针对一个用户的能力,如学习,我们可以定义一个用户类,并通过赋予这个用户类一个成员方法来实现这种行为。随着用户类的不断发展,用户类每拥有一个方法我们则需要修改或者扩展用户类一次,最后的结果必然导致系统类爆炸。

如果用户扩展的这种能力是属于某种类型的实现之一,如学习与学习数学,学语文,学英语之间的关系,那么我们可以通过针对学习这个类型进行抽象,让用户类去引用这个类型接口。也即是用户类通过有参构造器或者setXXX(学习接口类型 xxx)这样的方式来指向接口,而我们针对不同的学习类型对学习接口类型进行实现,利用多态的特性进行引用,从而把用户与学习种类解耦,实现跟学习类型这个接口进行低耦合。

 

解析:本图的图例需要忽略掉ICourse和User之间的直联关系(这个由于个人将多个版本集中在一起演化而没删除的关系),通过图例我们可以看到User类只与ICourse有聚合关系,而子课程在实现接口类后,可以跟随业务需要而自由扩展!User类在需要的时候,直接传入声明则可以对应调用!如下:

 

---------------------------------------------------------------------------------------------------------------------------------------------------------------------

3-----单一职责原则:不要存在多于一个导致类变更的原因

背景:单一职责是指在类层面,方法层面,接口层面的职能都是单一的。在类中,职责一和职责二发生改变都会影响到同一个class类,那么久有可能造成当职责过多的时候,牵一发而动全身,系统耦合度太高而不便于维护!

接口层面:职责单一是指接口都应该归属于同一种类型的接口,不可过于少也不可以过于多地划分接口,而是根据职责来分!而且在完成后可以根据依赖倒置原则来执行修改引用

方法层面:方法和功能服务单一且清晰

在开发中,尽可能地维护好接口和方法的单一职责,而对于类的单一职责则看情况分析,以不引起类爆炸为准!

 

---------------------------------------------------------------------------------------------------------------------------------------------------------------------

4-----接口隔离原则:用多个职能专一的接口,而不是使用将多种职能的方法都集中在一起的接口!一个类对一个类的依赖应该建立在最小的接口上。

注意:接口隔离原则理论上是对接口进行细化,但是过于细化的接口必然会导致代码过于复杂,因此在接口创建过程中即要防止接口声明过多,也要防止接口里面方法体太多从而导致某些类出现空实现!

现实:虽然不想说明,但是此接口是往往被默认使用最多的情况,目前的三层架构都是以"职能"为模块做接口,无形中符合了此设计!

 

上面三个接口都是从下面的集成接口里面拆分的,根据动物类型拆分而成!

---------------------------------------------------------------------------------------------------------------------------------------------------------------------

5------迪米特原则:一个对象应该对其他对象保持最少的了解。一个对象只需要保持对其朋友对象的关注就好,其他的交给中介类!

背景:无论什么类,当一个类中加载过多类的引用或者对象声明,那么这个类将会与其被引用的类产生高度的耦合,非常不利于代码的维护和扩展。因此在一个类对另外一个类进行引用的时候,如非必要,尽可能不要引入其类。就比如少用继承多用聚合或者组合也是这个道理!

注:迪米特主要指定了一个边界,提醒我们,重点关注的有以下:

一个类的成员变量,入参或出参所涉及到的对象都是该类的朋友类,在方法体里面的类则不算其朋友类。因此我们需要尽可能保持不要与其他非朋友类对话!

 

---------------------------------------------------------------------------------------------------------------------------------------------------------------------

6-----里氏替换:如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象哦
都替换成o2时,程序P的行为没有发生改变,那么类型T2位T1的子类型!

扩展:一个软件实体如果使用一个父类,那一定适用其子类,所有引用父类的地方必须能够透明底使用其子类的对象,子类能够替换父类对线下而程序逻辑不变!

里氏替换是对开闭原则的扩展,他主要是制定了当我们队父类进行扩展时候的一些原则,同时这些原则也约束了继承的泛滥!

里式替换原则支持通过对现有类,现有接口的扩展来满足新增的业务需求,而不支持直接去修改基础类。但是里式替换原则却不太支持对父类进行重写,更多的要求对其进行重载,因此在这方面的约束也颇多:

-----子类可以扩展父类的功能,但是不能改变父类的功能!

-----对于抽象类(存在空白方法体的类),子类可以实现父类的抽象,但是不能覆盖父类的非抽象方法

-----一个 子类可以增加自己特有的方法

这三条规则的结合,可以看出里式替换相对来说是支持重载的。并且有对其提出了诸多要求:

当子类的方法重载父类的方法时候,方法的前置条件,也即方法的输入/入参要比父类方法的输入参数更加宽松!如果子类的参数与父类相同,那么就会变成重写,如果入参类型小于父类,那么父类方法永远不会被执行到!
当子类的方法实现父类的方法时(重写/重载/实现抽象方法),方法的后置条件(方法的输出/返回值)要比父类更加严格或者相等,可以理解为范围!
注:这也是我们在面试的时候说到重写和重载各自条件的来源!

---------------------------------------------------------------------------------------------------------------------------------------------------------------------

7-----合成/复用原则:尽量使用对象组合,而不是继承来达到复用的目的

在面向设计过程中对于关联关系首先要通过组合/聚合关系来实现复用功能,其次才是通过继承和实现关系来实现复用。因为继承对类间关系的绑定,会造成高度耦合,而组合聚合则可以使得系统更加灵活,从而降低类间的耦合度。

继承复用会破坏类间的封装性,这种容易将父类的内部细节暴露给子类的行为,称为白箱操作。并且对于继承而言,子类其从父类继承而来的方法基本是基于静态实现,一旦父类发生改变,子类也需要随着变动。类与类间没有足够的灵活性可言!

注入:将已有的对象纳入到新对象中,使之成为新对象的一部分,新对象可以调用成员对象的功能,对成员对象内部细节却不可见!甚至可以利用依赖倒置或者多态动态调用,这种行为被称为"黑箱操作"。在实现复用的时候应该多用关联而少用继承,关联可以理解为内部对象调用等,如将原本为继承的代码修改为setter方法注入(依赖倒置原则)

扩展:

组合:组合是指将多个对象组合在一起,具有相同的生命周期,主类维护了对子类的引用等信息,当任何一个子类出现问题或者子类遗失,主类都会受到影响!

聚合:聚合是一种弱的关联关系,不具备相同的生命周期,主类或许是通过set方法维护了对子类的调用,当子类遗失的时候,主类并不回受到影响!(类似于依赖倒置原则中的用户类和课程子类)

二、遇到的问题

   对于js方面的只是还有很多困惑

三、明日计划

   明天继续学习相关知识

posted @ 2021-09-14 16:56  小仙女W  阅读(69)  评论(0编辑  收藏  举报