• 软件设计的7大原则


    开闭原则、依赖倒置原则、单一职责原则、接口隔离原则、迪米特原则、里氏替换原则、合成复用原则。

    开闭原则

    • what:一个软件实体如类、模块和函数应该对扩展开发对修改关闭
    • why:提高软件系统的可复用性及可维护性

    • how: 版本更新尽量不更改源代码,但是可以添加新功能;

    • example: 弹性工作时间,每天工作8小时不变,早点来早点走,晚点来晚点走

    面向抽象编程,继承、多态机制

    开闭原则

    左图表示我们有一个订单接口,现在要在其基础上扩展一个功能,能够实现打折的功能。如何才能实现打折功能呢?

    1. 在 IOrder 接口中添加一个 getDiscountPrice(),并在其实现类中实现这个方法。这样可行么?如果这样做就需要在所有实现这个方法的类中,添加 getDiscountPrice()。
    2. 修改 getPrice() 的实现,这样原价去哪里获取呢?也不是很好
    3. 我们可以写一个类 ShoeDiscoutOrder 继承 ShoeOrder 然后 @Override getPrice() 来实现打折功能,而原价则使用 getOriginPrice(),并在其中使用 super.getPrice() 调用父类方法。

    这是开闭原则的简单应用:扩展是开启的,但是对接口和基类的修改是关闭的。

    依赖倒置原则

    • what: 高层模块不应该依赖低层模块,二者都应该依赖其抽象
    • why: 减少雷剑的耦合性,提高系统稳定性,提高代码可读性和可维护性,降低修改程序所造成的风险
    • how:抽象不应该依赖细节;细节应该依赖抽象。针对接口编程,不要针对实现编程。

    • example:对比扩展类中的方法编程,以及面向接口编程的区别。

    现在有一个 course 类,类中有上各种课程的方法,我在应用层调用各种上课的方法,就需要在 course 中新增相应的方法。此时就造成了一种局面:高层模块依赖于低层模块。代码是这样的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    // 应用层(高层)调用低层的方法
    public class Test {
    	public static void main(String[] args) {
           Geely geely = new Geely();
           geely.studyJavaCourse();
           geely.studyFECourse();
       }
    }
    
    // 低层想要扩展就要加方法
    public class Geely {
    	public void studyJava(){
      	// 业务代码
      }
      
      public void studyPython(){
      	// 业务代码
      }
      
      // 更多课程都要添加新的方法
    }
    

    我们希望高层模块不依赖低层模块,并且让它们依赖其抽象。具体来说是什么意思呢?就是说我有一个接口实现了 stydyCouse() 这一个方法,具体什么课,怎么学都由该接口的实现类负责。我从高层传入对象,就可以调用相应的类。

    依赖倒置原则实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    // 高层调用低层,只要注入具体的实现类
    public class Test {
        public static void main(String[] args) {
            Geely geely = new Geely();
            geely.setiCourse(new JavaCourse());
            geely.studyImoocCourse();
    
            geely.setiCourse(new FECourse());
            geely.studyImoocCourse();
    
        }
    }
    
    // 低层调用方法无需变动
    public class Geely {
        private ICourse iCourse;
    
        public void setiCourse(ICourse iCourse) {
            this.iCourse = iCourse;
        }
    
        public void studyImoocCourse(){
            iCourse.studyCourse();
        }
    }
    

    依赖倒置的核心是面向接口编程

    单一职责原则

    • what: 不要存在多余一个导致类变更的原则
    • why: 一个类有多个职责会导致它变更,修改某一个职责可能导致其他职责出错
    • how: 一个类/接口/方法只负责一个职责

    • example:类、接口和方法级别的单一职责实现。

    类级别

    现在有一个 bird 类,有的鸟靠飞,有的鸟靠走。我们可能会用一个逻辑判断来实现。如果说遵循单一职责原则,就应该将其拆成两个类 FlyBirdWalkBird ,然后由应用层来判断是哪种鸟。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    public class Bird {
        public void mainMoveMode(String birdName){
            if("鸵鸟".equals(birdName)){
                System.out.println(birdName+"用脚走");
            }else{
                System.out.println(birdName+"用翅膀飞");
            }
        }
    }
    

    接口级别

    比如说有个 ICourse 接口,接口中有若干方法,一些方法负责获取课程信息,一些方法负责管理可能。比如说,有个方法是退订这门课,那就不能获取课程信息了对不对。总之,这样一个接口现在负责了两种职责,我们就应该将其拆成两组接口。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    public interface ICourse {
      // 职责一:负责获取课程信息
        String getCourseName();
        byte[] getCourseVideo();
    
      // 职责二:负责管理课程
        void studyCourse();
        void refundCourse();
    }
    

    单一职责原则的实现

    方法级别

    方法中做逻辑判断,负责多个任务,实际上是可以拆分开的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    private void updateUsername(String userName){
    	userName = "geely";
    }
    private void updateUserAddress(String address){
    	address = "beijing";
    }
    
    // 多个职责,条件判断,拆!
    private void updateUserInfo(String userName,
             String address,boolean bool){
            if(bool){
                //todo something1
            }else{
                //todo something2
            }
    
    
            userName = "geely";
            address = "beijing";
    }
    

    接口隔离原则

    • what: 用多个专门的接口,而不使用单一的总接口,客户端不应该依赖它不需要的接口
    • why: 符合高内聚低耦合的设计思想,从而使得类具有很好的可读性、可扩展性和可维护性
    • how:

      • 一类对一个类的依赖应该建立在最小的接口上
      • 建立单一接口,不要建立庞大臃肿的接口
      • 尽量细化接口,接口中的方法尽量少
      • 注意适度原则,一定要适度
    • example: 一个关于动物的接口承载太多的方法,将其拆分的例子

    同样是动物但是这个动物能做的事儿,其他动物可能不能做。如果动物都实现同样的接口,那么有些方法的实现就要空着了。所以要拆分。

    1
    2
    3
    4
    5
    
    public interface IAnimalAction {
        void eat();
        void fly();
        void swim();
    }
    

    拆分前,拆分后如下所示:

    接口隔离原则演示

    接口隔离原则看着简单,但是把握好接口隔离的粒度还是需要仔细考量的。

    迪米特原则

    • what: 一个对象应该对其他对象保持最少的了解,又叫最少知道原则
    • why: 降低类之间的耦合
    • how: 尽量降低类和类之间的耦合
    • example:

    Boss 类只需要和 TeamLeader 打交道,而不需要和 course 打交道。

    耦合

    修改之后如下所示:

    修改之后

    迪米特原则的关键就是梳理出这个类应该和哪些类打交道,不应该和哪些类打交道,做到尽可能合理。

    里氏替换原则

    合成复用原则

  • 相关阅读:
    java生成验证码
    springmvc笔记(来自慕课网)
    angularJs编写多指令的情况
    四年前端开发的迷茫.
    angularJs的ui-router总结
    grunt构建前端自动化的开发环境
    socket传送文件
    socket--粘包
    socket--接受大数据
    动态导入模块
  • 原文地址:https://www.cnblogs.com/shuiyj/p/13185176.html
Copyright © 2020-2023  润新知