软件设计的七大原则

生活中,最使人疲惫的往往不是道路的遥远,而是心中的郁闷;最使人痛苦的往往不是生活的不幸,而是希望的破灭;最使人颓废的往往不是前途的坎坷,而是自信的丧失;最使人绝望的往往不是挫折的打击,而是心灵的死亡。所以我们要有自己的梦想,让梦想的星光指引着我们走出落漠,走出惆怅,带着我们走进自己的理想。

导读:本篇文章讲解 软件设计的七大原则,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源: 原文

软件设计原则概述

学习设计原则是学习设计模式的基础。

实际开发过程中,并不一定要求所有代码都遵循设计原则,只需要在适当的场景遵循设计原则,就可以帮助开发者设计出更加优雅的代码结构。

软件设计原则一共有七个:

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

依赖倒置原则:通过抽象使各个类或者模块不相互影响,实现松耦合。

单一职责原则:一个类、接口、方法只做一件事。

接口隔离原则:尽量保证接口的纯洁性,客户端不应该依赖不需要的接口。

迪米特法则:又叫最少知道原则,一个类对其所依赖的类知道得越少越好。

里氏替换原则:子类可以扩展父类的功能但不能改变父类原有的功能。

合成复用原则:尽量使用对象组合、聚合,而不使用继承关系达到代码复用的目的。

具体详细参考: 软件设计原则思维导图
在这里插入图片描述

1.开闭原则

开闭原则(Open-Closed Principle, OCP)是指一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。

实现开闭原则的核心思想就是面向抽象编程。强调用抽象构建框架,用实现扩展细节,提高软件系统的可复用性及可维护性。

假设动物都有跑的行为

public interface Animal {
    void run();
}

public class Dog implements Animal{
    @Override
    public void run() {
        System.out.println("Dog is running");
    }
}

若此时要给Dog添加Run的修饰,在不修改原有代码前提前下,可根据开闭原则,用拓展的方式实现

public class NewDog extends Dog{
  public void newRun(){
        System.out.println("NewDog is happy running");
    }
}

2.依赖倒置原则

依赖倒置原则(Dependence Inversion Principle,DIP)是程序要依赖于抽象接口,不要依赖于具体实现。

高层模块不应该依赖底层模块,二者都应该依赖其抽象。

通过依赖倒置,可以减少类与类之间的耦合性,提高系统的稳定性,提高代码的可读性和可维护性,并能够降低修改程序所造成的风险。

汽车工厂生产BM和BC

public class CarFactory {
   void generateBM(){
        System.out.println("CarFactory is generateBM");
    }
    
    void generateBC(){
        System.out.println("CarFactory is generateBC");
    }
}

 public static void main(String[] args) {
        CarFactory carFactory = new CarFactory();
        carFactory.generateBM();
        carFactory.generateBC();
    }

若此时汽车工厂还想生成AD,则除了在CarFactory类(低层)添加generateAD()方法,还需要在调用的地方(高层)调用。

解决方案:创建抽象接口,不同的车型实现相同接口

public interface IGenerate {
    void generate();
}

public class BM implements IGenerate{
    @Override
    public void generate() {
        System.out.println("CarFactory is generateBM");
    }
}

public class BC implements IGenerate{
    @Override
    public void generate() {
        System.out.println("CarFactory is generateBC");
    }
}

public class AD implements IGenerate{
    @Override
    public void generate() {
        System.out.println("CarFactory is generateAD");
    }
}

这是一种常用的方式,叫依赖注入。注入的方式还有构造器方式和 setter 方式

public class CarFactory {
    void generate(IGenerate generate){
        generate.generate();
    }
}

 public static void main(String[] args) {
        CarFactory carFactory = new CarFactory();
        carFactory.generate(new BM());
        carFactory.generate(new BC());
        carFactory.generate(new AD());
}

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

public class CarFactory2 {
    private static IGenerate generate;

    public CarFactory2(IGenerate iGenerate) {
        this.generate = iGenerate;
    }

    public void generate() {
        generate.generate();
    }

    public static void main(String[] args) {
        CarFactory2 bm = new CarFactory2(new BM());
        bm.generate();
        CarFactory2 bc = new CarFactory2(new BC());
        bc.generate();
        CarFactory2 ad = new CarFactory2(new AD());
        ad.generate();

    }
}

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

public class CarFactory3 {
    private static IGenerate generate;

    public static void setGenerate(IGenerate generate) {
        CarFactory3.generate = generate;
    }

    public void generate() {
        generate.generate();
    }

    public static void main(String[] args) {
        BM bm = new BM();
        CarFactory3 bmFc = new CarFactory3();
        bmFc.setGenerate(bm);
        bmFc.generate();

        BC bc = new BC();
        CarFactory3 bcFc = new CarFactory3();
        bcFc.setGenerate(bc);
        bcFc.generate();

        AD ad = new AD();
        CarFactory3 adFc = new CarFactory3();
        adFc.setGenerate(ad);
        adFc.generate();

    }
}

3.单一职责原则

单一职责(Simple Responsibility Pinciple,SRP)是指不要存在多于一个导致类变更的原因。

比如一个 Class负责两个职责,一旦发生需求变更,修改其中一个职责的逻辑代码,有可能会导致另一个职责的功能发生故障。于是可以给两个职责分别用两个 Class 来实现,进行解耦,即使以后需求变更维护也互不影响。

这样做可以降低类的复杂度,提高类的可读性,提高系统的可维护性,降低变更引起的风险。

单一职责原则就是一个Class/Interface/Method只负责一项职责。

CarFactory类既要生成small Car又要生成big Car,承担了两种处理逻辑

public class CarFactory {
    void generate(Integer type) {
        if (type == 1) {
            System.out.println("generate small Car");
        } else {
            System.out.println("generate big Car");
        }
    }

    public static void main(String[] args) {
        CarFactory carFactory = new CarFactory();
        carFactory.generate(1);
        carFactory.generate(2);
    }
}

此时,若要生产不同类型Car,则需修改代码,造成不可控的风险。

解决方案:对职责进行分离解耦

public class SmallCar {
    void generate() {
        System.out.println("generate small Car");
    }
}

public class BigCar {
    void generate() {
        System.out.println("generate big Car");
    }
}


public static void main(String[] args) {
    SmallCar smallCar = new SmallCar();
    smallCar.generate();

    BigCar bigCar = new BigCar();
    bigCar.generate();
}

4.接口隔离原则

接口隔离原则(Interface Segregation Principle, ISP)是指使用多个专门的接口,而不使用单一的总接口,客户端不应该依赖它不需要的接口。

接口隔离原则符合常说的高内聚低耦合的设计思想,从而使得类具有很好的可读性、可扩展性和可维护性。

注意点:

1.一个类对一类的依赖应该建立在最小的接口之上

2.建立单一接口,不要建立庞大臃肿的接口

3.尽量细化接口,接口中的方法尽量少(要适度)

Bird不具备run的行为,Dog不具备fly的行为

public interface IAnimal {
    void eat();

    void fly();

    void run();
}

public class Bird implements IAnimal {
    @Override
    public void eat() {}
    @Override
    public void fly() {}
    @Override
    public void run() {}
}


public class Dog implements IAnimal {
    @Override
    public void eat() {
    }

    @Override
    public void fly() {
    }

    @Override
    public void run() {
    }
}

此时,应该针对不同动物行为来设计不同的接口

public interface IEatAnimal {
	void eat();
}

public interface IFlyAnimal {
	void fly();
}

public interface IRunAnimal {
	void run();
}

不同的动物选择不同的接口实现,从而达到接口隔离原则,符合高内聚低耦合的设计思想

public class Dog implements IRunAnimal,IEatAnimal {
	@Override
	public void eat() {}
	@Override
	public void run() {}
}


public class Bird implements IFlyAnimal,IEatAnimal {
	@Override
	public void eat() {}
	@Override
	public void fly() {}
}

5.迪米特法则

迪米特原则(Law of Demeter LoD)是指一个对象应该对其他对象保持最少的了解,又叫最少知道原则(Least Knowledge Principle,LKP),尽量降低类与类之间的耦合。主要强调只和朋友交流,不和陌生人说话。

朋友的定义:

出现在成员变量、方法的输入、输出参数中的类都可以称之为成员朋友类

陌生人的定义:

出现在方法体内部的类不属于朋友类

举例:校长需要知道学生的信息,就需要由老师进行统计报告

Principal类做了Teacher类做的事情,与Student 产生关联关系

class Student {
    public String name;
    public int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

class Teacher {
  /**
   * 报告学生信息
   */
    void report(List<Student> studentLists) {
        studentLists.stream().forEach(student -> {
            System.out.println("name :" + student.name + " age : " + student.age);
        });
    }
}

class Principal {
    void statistics(Teacher teacher) {
        ArrayList<Student> students = new ArrayList<>();
        students.add(new Student("张三", 18));
        students.add(new Student("李四", 19));

        teacher.report(students);
    }

    public static void main(String[] args) {
        Principal principal = new Principal();
        Teacher teacher = new Teacher();
        principal.statistics(teacher);
    }
}

Principal类只关心Teacher类反馈的结果,不需要与Student产生关系

class Student {
    public String name;
    public int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}


class Teacher {
  /**
   * 报告学生信息
   */
    void report() {
      ArrayList<Student> students = new ArrayList<>();
      students.add(new Student("张三", 18));
      students.add(new Student("李四", 19));

      students.stream().forEach(student -> {
            System.out.println("name :" + student.name + " age : " + student.age);
        });
    }
}

class Principal {
    void statistics(Teacher teacher) {
        teacher.report();
    }

    public static void main(String[] args) {
        Principal principal = new Principal();
        Teacher teacher = new Teacher();
        principal.statistics(teacher);
    }
}

6.里氏替换原则

里氏替换原则(Liskov Substitution Principle,LSP)是指一个软件实体如果适用一个父类的话,那一定是适用于其子类,所有引用父类的地方必须能透明地使用其子类的对象,子类对象能够替换父类对象,而程序逻辑不变。

子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。

注意点:

1.子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法

2.子类中可以增加自己特有的方法

3.当子类的方法重载父类的方法时,方法的前置条件(即方法的输入/入参)要比父类方法的输入参数更宽松

4.当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的输出/返回值)要比父类更严格或相等

假设动物都有跑的行为,后来需要对该行为进行修饰,进行了重新父类方法

public interface Animal {
    void run();
}

public class Dog implements Animal{
    @Override
    public void run() {
        System.out.println("Dog is running");
    }
}
public class NewDog extends Dog{
  public void run(){
        System.out.println("NewDog is happy running");
    }
}

正确的做法是增加方法而不是覆盖父类的方法

public class NewDog extends Dog{
  public void newRun(){
        System.out.println("NewDog is happy running");
    }
}

7.合成复用原则

合成复用原则(Composite/Aggregate Reuse Principle,CARP)是指尽量使用对象组合(has-a)或聚合(contanis-a),而不是继承关系达到软件复用的目的。

可以使系统更加灵活,降低类与类之间的耦合度,一个类的变化对其他类造成的影响相对较少。

继承又叫白箱复用,相当于把所有的实现细节暴露给子类

组合/聚合也称为黑箱复用,对类以外的对象是无法获取到实现细节的

使用继承

public class DBConnection {
    public String getConnection(){
        return "connection";
    }
}

public class MyLDao extends DBConnection {
    public void save(Object obj) {
        String connection = super.getConnection();
        System.out.println("获取连接:" + connection + " ,进行保存");
    }
}

改造为合成复用原则。使用构造注入,也可以使用Setter注入

public class DBConnection {
    public String getConnection(){
        return "connection";
    }
}

public class MyLDao {
    private DBConnection dbConnection;

    public MyLDao (DBConnection dbConnection) {
        this.dbConnection = dbConnection;
    }

    public void save(Object obj) {
        String connection = dbConnection.getConnection();
        System.out.println("获取连接:" + connection + " ,进行保存");
    }
}

    public static void main(String[] args) {
        MyLDao myLDao = new MyLDao(new DBConnection());
        myLDao.save(new Object());
    }
-----------------------------------------------------------------------------

public class MyLDao {
    private DBConnection dbConnection;

    public void setDbConnection(DBConnection dbConnection) {
        this.dbConnection = dbConnection;
    }

    public void save(Object obj) {
        String connection = dbConnection.getConnection();
        System.out.println("获取连接:" + connection + " ,进行保存");
    }
}

    public static void main(String[] args) {
        MyLDao myLDao = new MyLDao();
        myLDao.setDbConnection(new DBConnection());
        myLDao.save(new Object());
    }

假如业务变化,要求使用Oracle,可以创建新的Oracle数据库连接继承原有连接,原有代码无须进行修改,而且还可以很灵活地增加新的数据库连接方式。

public class OracleDBConnection extends DBConnection {
    public String getConnection() {
        return "Oracle Connection";
    }
}

public class MyLDao {
    private DBConnection dbConnection;

    public void setDbConnection(DBConnection dbConnection) {
        this.dbConnection = dbConnection;
    }

    public void save(Object obj) {
        String connection = dbConnection.getConnection();
        System.out.println("获取连接:" + connection + " ,进行保存");
    }
}

    public static void main(String[] args) {
        MyLDao myLDao = new MyLDao();
        myLDao.setDbConnection(new OracleDBConnection());
        myLDao.save(new Object());
    }

其实DBConnection还不是一种抽象,不便于系统扩展,在设计之初可将DBConnection变成抽象类,若有新的需求只需要实现具体逻辑。

public abstract class DBConnection {
	public abstract String getConnection();
}

public class MySQLConnection extends DBConnection {
	@Override
	public String getConnection() {
		return "MySQL";
	}
}

public class OracleConnection extends DBConnection {
	@Override
	public String getConnection() {
		return "Oracle";
	}
}



public class MyLDao {
    private DBConnection dbConnection;

    public void setDbConnection(DBConnection dbConnection) {
        this.dbConnection = dbConnection;
    }

    public void save(Object obj) {
        String connection = dbConnection.getConnection();
        System.out.println("获取连接:" + connection + " ,进行保存");
    }
}

    public static void main(String[] args) {
        MyLDao myLDao = new MyLDao();
        myLDao.setDbConnection(new OracleConnection());
        myLDao.save(new Object());
    }

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/136917.html

(0)
飞熊的头像飞熊bm
0 0

相关推荐

  • Leaflet中使用Leaflet.Path.Transform插件实现旋转图形 后端漫谈

    Leaflet中使用Leaflet.Path.Transform插件实现旋转图形

    0 0212
    飞熊的头像 飞熊
    2023年4月20日
  • Java操作Kafka API以及Spring Boot集成Kafka 后端漫谈

    Java操作Kafka API以及Spring Boot集成Kafka

    0 0206
    飞熊的头像 飞熊
    2023年4月22日
  • 第六章 封装和继承 ② 代码 后端漫谈

    第六章 封装和继承 ② 代码

    0 0309
    seven_的头像 seven_
    2023年2月23日
  • 猿创征文 | Shell编程【上篇】 后端漫谈

    猿创征文 | Shell编程【上篇】

    0 0329
    seven_的头像 seven_
    2023年3月1日
  • MySQL5.7忘记root密码并重置密码 后端漫谈

    MySQL5.7忘记root密码并重置密码

    0 0162
    飞熊的头像 飞熊
    2023年12月12日
  • Java 8新特性之Optional 后端漫谈

    Java 8新特性之Optional

    0 0190
    seven_的头像 seven_
    2023年2月28日
  • 采用多种方式实现项目的查询多级缓存(六) 后端漫谈

    采用多种方式实现项目的查询多级缓存(六)

    0 0162
    飞熊的头像 飞熊
    2023年4月26日
  • Three.js中实现点击按钮添加删除旋转立方体 后端漫谈

    Three.js中实现点击按钮添加删除旋转立方体

    0 0230
    飞熊的头像 飞熊
    2023年4月20日
  • C# 提取PDF中指定文本、图片的坐标 后端漫谈

    C# 提取PDF中指定文本、图片的坐标

    0 0237
    小半的头像 小半
    2024年2月21日
  • JWT的详细使用 后端漫谈

    JWT的详细使用

    0 0141
    飞熊的头像 飞熊
    2023年4月22日
  • 【保姆级】如何创建一个Cucumber Test 后端漫谈

    【保姆级】如何创建一个Cucumber Test

    0 0133
    飞熊的头像 飞熊
    2023年4月19日
  • 两台服务器5分钟快速搭建3主3从的redis集群 后端漫谈

    两台服务器5分钟快速搭建3主3从的redis集群

    0 0208
    飞熊的头像 飞熊
    2023年12月12日

发表回复

登录后才能评论

扫我!扫我!扫码!

软件设计的七大原则

站长精选

  • SpringBoot 自定义启动画面

    SpringBoot 自定义启动画面

    2024年6月21日

  • Java8 异步非阻塞做法:CompletableFuture 两万字详解!

    Java8 异步非阻塞做法:CompletableFuture 两万字详解!

    2022年12月1日

  • SpringBoot 应用 Docker 化:从 Maven 构建到 Docker 部署的完整指南

    SpringBoot 应用 Docker 化:从 Maven 构建到 Docker 部署的完整指南

    2023年11月27日

  • 在同事面前炫一把,用 Docker 搭建更酷的本地开发环境

    在同事面前炫一把,用 Docker 搭建更酷的本地开发环境

    2023年5月5日

  • 3万字 SpringBoot 日志全解析:快速掌握!

    3万字 SpringBoot 日志全解析:快速掌握!

    2024年10月4日

  • Java 中如何优雅地根治 null 值引起的 Bug 问题

    Java 中如何优雅地根治 null 值引起的 Bug 问题

    2024年1月11日

  • 换掉 VS Code!这个最新开源的编辑器用起来无比丝滑!

    换掉 VS Code!这个最新开源的编辑器用起来无比丝滑!

    2023年4月25日

  • MyBatis-Plus联表查询的短板,终于有一款工具补齐了

    MyBatis-Plus联表查询的短板,终于有一款工具补齐了

    2022年12月12日

  • 盘点 MySQL 缓存优化方案

    盘点 MySQL 缓存优化方案

    2024年8月18日

  • 史上最全的整合第三方授权登录的工具类库,JustAuth 搞定一切!

    史上最全的整合第三方授权登录的工具类库,JustAuth 搞定一切!

    2023年12月9日

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!

玻璃钢生产厂家福建仿铜玻璃钢雕塑优势玻璃钢花盆展示视频玻璃钢雕塑烤漆图片不锈钢校园玻璃钢雕塑多钱玻璃钢文学作品雕塑湛江广场玻璃钢雕塑上海玻璃钢雕塑哪里有商场雕塑放玻璃钢还是泡沫银川西宁玻璃钢雕塑价格云南玻璃钢商场美陈雕塑玻璃钢雕塑工艺参数玻璃钢造型雕塑性价比哪个好云浮玻璃钢动物雕塑供应商玻璃钢民族卡通雕塑福建玻璃钢雕塑性能运城泡沫校园玻璃钢雕塑厂家济南水果玻璃钢雕塑定做价格哪里有玻璃钢人物雕塑定做厂家宁波玻璃钢抽象动物雕塑厂家加工广西玻璃钢雕塑厂家公司江苏步行街玻璃钢雕塑多少钱浙江大型商场美陈供应商中牟玻璃钢雕塑费用中牟玻璃钢花盆花器商场美陈软装图片三门峡玻璃钢卡通雕塑厂家电话玻璃钢雕塑独具创意泰州设计玻璃钢雕塑价位黑龙江火烈鸟玻璃钢雕塑定制徐汇区模压玻璃钢花盆香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化