软件六大设计原则

软件设计原则

概述:

软件开发过程中,为了提高系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程序员应该尽量遵循7大原则来开发程序,从而提高软件开发效率,节约软件开发成本和维护成本。

7大设计原则,侧重点不同

开闭原则是总纲,对扩展开放,对修改关闭。

里氏替换原则,不要破坏类的继承。

依赖倒置原则,面向接口编程。

单一职责,类要单一。

接口隔离原则,告诉我们设计接口的时候要精简单一。

迪米特法则,降低代码耦合度。

合成复用法则,优先使用组合或者聚合关系复用,少用继承关系复用

开闭原则

开闭原则的含义是:当应用的需求改变时,在不修改软件实体的源代码或者二进制代码的前提下,可以扩展模块的功能,使其满足新的需求。

对软件测试的影响软件遵守开闭原则的话,软件测试时只需要对扩展的代码进行测试就可以了,因为原有的测试代码仍然能够正常运行。

可以提高代码的可复用性粒度越小,被复用的可能性就越大;在面向对象的程序设计中,根据原子和抽象编程可以提高代码的可复用性。

可以提高软件的可维护性遵守开闭原则的软件,其稳定性高和延续性强,从而易于扩展和维护。

栗子:

/**
 * 抽象绘制图形类
 */
abstract class Shape {
    abstract void draw();
}

 

public class Circle extends Shape {
    void draw() {
        System.out.println("绘制圆形");
    }
}

public class Rectangle extends Shape{
    void draw() {
        System.out.println("绘制三角形");
    }
}

public class GraphicEditor {
    public void drawShape(Shape shape) {
        shape.draw();
    }
}

测试类:

public class TestMain {
    public static void main(String[] args) {
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Rectangle());
        graphicEditor.drawShape(new Circle());
        //方便扩展 对修改关闭 对扩展开
    }
}

业务抽象,例多平台发送消息(微信,QQ,邮件,新浪邮箱,阿里短信平台...

里氏替换原则

里氏替换原则是实现开闭原则的重要方式之一。

它克服了继承中重写父类造成的可复用性变差的缺点。

它是动作正确性的保证。即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性。

子类可以扩展父类的功能,但是不能改变父类原有的功能。

 

问题描述:

public class A {
    public int func1(int a, int b) {
        return a - b;
    }
}

class B extends A {
    @Override
    public int func1(int a, int b) {
        return a + b;
    }

    public int func2(int a, int b) {
        return func1(a, b) + 100;
    }
}

class C {
    public static void main(String[] args) {
        B b = new B();
        System.out.println("100-50=" + b.func1(100, 50));
        System.out.println("100-80=" + b.func1(100, 80));
        System.out.println("100+20+100=" + b.func2(100, 20));
    }
}

输出结果:

100-50=150

100-80=180

100+20+100=220

 

里氏替换原则是应该避免的原则。增加代码问题风险。

依赖倒置原则

依赖倒置原则的主要作用如下。

依赖倒置原则可以降低类间的耦合性。

依赖倒置原则可以提高系统的稳定性。

依赖倒置原则可以减少并行开发引起的风险。

依赖倒置原则可以提高代码的可读性和可维护性。

问题设计示例:

public class Driver {
    public void driver(Ben ben) {
        ben.run();
    }
}

class Ben {
    public void run() {
        System.out.println("奔驰汽车");
    }
}

class TestMain {
    public static void main(String[] args) {
        Driver driver = new Driver();
        driver.driver(new Ben());
    }
}

设想一下如果这时多了奥迪,丰田,大众,红旗... 代码之间耦合度过高 违反依赖倒置原则

这里不应该是 一个司机依赖车。反过来会更好 车依赖于司机。

public interface IDriver {
    void driver(ICar iCar);
}

public interface ICar {
    void run();
}

public class Driver implements IDriver {

    public void driver(ICar iCar) {
        iCar.run();
    }
}

public class Car implements ICar {
    public void run() {
        System.out.println("汽车运行");
    }
}

public class TestMain
{
    public static void main(String[] args) {
        Driver driver = new Driver();
        driver.driver(new Car());
    }
}

 

深入理解

 

依赖倒置原则的本质就是通过抽象(抽象类或接口)使各个类或模块实现彼此独立,不互相影响,实现模块间的松耦合。在项目中使用这个规则需要以下原则;

 

    每个类尽量都要有接口或抽象类,或者抽象类和接口都有: 依赖倒置原则的基本要求,有抽象才能依赖倒置

    变量的表面类型尽量是接口或者抽象类

    任何类都不应该从具体类派生

    尽量不要重写基类已经写好的方法(里式替换原则)

    结合里式替换原则来使用: 结合里式替换原则和依赖倒置原则我们可以得出一个通俗的规则,接口负责定义public属性和方法,并且声明与其他对象的依赖关系,抽象类负责公共构造部分的实现,实现类准确的实现业务逻辑,同时在适当的时候对父类进行细化。

 

一句话:依赖倒置原则的核心就是面向抽象(抽象类或者接口)编程

单一职责原则

单一职责原则的核心就是控制类的粒度大小、将对象解耦、提高其内聚性。如果遵循单一职责原则将有以下优点。

降低类的复杂度。一个类只负责一项职责,其逻辑肯定要比负责多项职责简单得多。

提高类的可读性。复杂性降低,自然其可读性会提高。

提高系统的可维护性。可读性提高,那自然更容易维护了。

变更引起的风险降低。变更是必然的,如果单一职责原则遵守得好,当修改一个功能时,可以显著降低对其他功能的影响。

注意:单一职责同样也适用于方法。一个方法应该尽可能做好一件事情。如果一个方法处理的事情太多,其颗粒度会变得很粗,不利于重用。

public interface IPhone{
    //拨通电话
    public void dial(String phoneNumber);
    //通话
    public void chat(Object o);
    //挂断电话
    public void hangup();
}

相对于类或者方法:对象制作一件事。

接口隔离原则

将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。

接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。

如果接口的粒度大小定义合理,能够保证系统的稳定性;但是,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。

使用多个专门的接口还能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义。

能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码。

 

接口细分,例如一个水果接口,不要把所有水果全部放到一个接口。

分为:生产水果 销售水果 邮寄水果 榨汁水果接口...

迪米特法则

迪米特法则要求限制软件实体之间通信的宽度和深度,正确使用迪米特法则将有以下两个优点。

降低了类之间的耦合度,提高了模块的相对独立性。

由于亲合度降低,从而提高了类的可复用率和系统的扩展性。

合成复用法则

通常类的复用分为继承复用和合成复用两种,继承复用虽然有简单和易实现的优点,但它也存在以下缺点。

继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为白箱复用。

子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。

它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。

 

采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有以下优点。

它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为黑箱复用。

新旧类之间的耦合度低。这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口。

复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。

 

posted @ 2022-08-23 15:39  苏子墨  阅读(264)  评论(0编辑  收藏  举报