面向对象设计原则

面向对象设计原则-SOLID

单一职责原则(SRP)

定义(类功能职责要单一)

一个类应该有且仅有一个引起它变化的原因,否则应该被拆分。

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

该原则提出对象不应该承担太多职责,如果一个对象承担太多的职责,至少存在两个缺点:

  • 一个职责的变化可能会削弱或者抑制这个类实现其他职责的能力
  • 当客户端需要改对象的某一个职责时,不得不将其他不需要的职责全都包含进来,从而造成代码冗余或浪费。
优点

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

  • 降低类的复杂度。一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多
  • 提高类的可读性,复杂性降低,自然可读性会提高。
  • 提高系统的可维护性。可读性高,自然更容易维护。
  • 降低变更引起的风险。
实现方法

单一职责原则是最简单也是最难运用的原则,需要设计人员发现类的不同职责并将其分离,再封装到不同的类或模块中,而发现类的多重职责需要设计人员具有较强的分析设计能力和相关重构经验。

案例讲解

大学学生工作主要包括学生生活辅导和学生作业指导两个方面工作,其中生活辅导主要包括(班委建设、出勤统计、心理辅导、费用催交、班级管理)等工作,学业知道主要包括(专业引导、科研指导、学习总结)等工作,如果将这些工作交给一位老师负责,显然不合理,正确的做法是生活辅导由辅导员负责,学业指导由导师负责,如下:
在这里插入图片描述

开闭原则(OCP)

定义(对扩展开放,对修改关闭)

软件实体应当对扩展开放,对修改关闭

软件实体包含:模块类与接口方法

优点

开闭原则是软件具有一定的适用性和灵活性同时具备稳定性和延续性。具体作用如下:

  • 软件测试只需要对扩展代码进行测试即可
  • 提高代码可复用性。粒度越小复用性越大。
  • 提高软件可维护性。遵守开闭原则的软件,稳定性高和延续性强,从而易于扩展和维护。
实现方法

可以通过 抽象约束封装变化来实现开闭原则。即通过接口或者抽象类为软件实体定义一个相对稳定的抽象层,而将相同的可变因素封装在相同的具体实现类中。

因为抽象灵活性好,适用性广,只要抽象合理,可以基本保持软件建构的稳定。而软件中易变的细节可以从抽象派生来的实现类来进行扩展,当软件需要发生变化时,只需要根据需求重新派生一个实现类来扩展就可以了。

里氏替换原则(LSP)

定义(继承尽量不要重写父类的方法)

继承必须确保超类所拥有的性质在子类中仍然成立。

里氏替换原则主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承。是继承复用的基础,反映了基类与子类之间的关系。

优点
  • 实现开闭原则的重要方式之一
  • 克服了继承中重写父类造成的可复用性差的缺点。
  • 动作正确性的保证,即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性。
实现方法

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

接口隔离原则(ISP)

定义(为类建立它需要专用的接口,接口不要庞大臃肿)

要为各个类建立它们需要专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

优点

接口隔离原则是为了约束接口,降低类对接口的依赖性,遵循接口隔离原则优点如下:

  • 提高系统灵活性和稳定性。(将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散)
  • 提高系统高内聚低耦合。
  • 接口粒度大小定义合理,能够保证系统的稳定性;但是如果定义过小,则会造成接口数量过多使设计复杂化;如果定义多大,灵活性降低,无法提供定制服务,给项目带来无法预料的风险。
  • 多个专门的接口还能提现对象层次,通过接口继承,实现对总接口的定义。
  • 减少项目中的代码冗余。(过大的接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余代码)。
实现方法

具体应用接口隔离原则时,应根据以下几个规则来衡量

  • 接口尽量小,但要有限度。一个接口只服务于一个子模块或业务逻辑。
  • 为依赖接口的类定制服务,只提供调用者需要的方法,屏幕不需要的方法。
  • 了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,环境不同,接口拆分标准就不不同。
  • 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。
接口隔离原则与单一职责原则区别
  • 单一职责原则注重是职责,而接口隔离原则注重的是对接口依赖的隔离
  • 单一职责原则主要是约束类,针对程序中实现和细节,接口隔离原则主要约束接口,针对抽象和程序整体框架的构建。

依赖倒置原则(DIP)

定义(要面向接口编程,不要面向实现编程)

原始定义为:高层模块不应该依赖底层模块,两者都应该依赖其抽象(抽象不应该依赖细节,细节应该依赖抽象)。

软件设计中,细节具有多变性,而抽象层相对稳定,因此以抽象为基础搭建起来的架构要比以细节为基础搭建起来的架构要稳定得多,这里的抽象是指接口或抽象类,而细节是指具体的实现类。

使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给它们的实现类去完成。

优点
  • 降低类间的耦合性
  • 提高系统稳定性
  • 减少并行开发引起的风险
  • 提高代码的可读性和可维护性
实现方法

依赖倒置原则目的是通过面向接口编程来降低类间的耦合性,因此实际编程中遵循以下4点:

  • 每个类尽量提供接口或抽象类,或者两者都具备。
  • 变量的声明类型尽量是接口或者抽象类。
  • 任何类都不应该从具体类派生。
  • 使用继承时尽量遵循里氏替换原则。
代码实现

本程序反映 学生类 与交通工具关系,交通工具中有乘坐(take())方法,学生类通过goHome乘坐交通工具回家,以下定义了学生类通过乘坐公公共汽车回家:

//交通工具-公共汽车
public class Bus {
    public String take() {
        return "乘坐公共汽车...";
    }
}

//学生类
public class Student {
    public void goHome(Bus bus) {
        System.out.println("学生放学回家" + bus.take());
    }
}

但是,这种设计存在缺点,如果学生想换乘校车回家,就要将学生类的代码修改如下

//交通工具-校车
public class SchoolBus {
    public String take() {
        return "乘坐校车...";
    }
}

//学生类
public class Student {
    public void goHome(SchoolBus schoolBus) {
        System.out.println("学生放学回家" + schoolBus.take());
    }
}

学生每换一种交通工具,都要修改一次代码,这明显违背了开闭原则,存在以上缺点原因时设计时同具体的交通工具类绑定了,这违背了依赖倒置原则,解决方法是:“定义一个交通工具的共同接口,学生类面向该接口编程,其代码修改如下:

//交通工具车接口
public interface Car {
    public String take();
}

//交通工具车接口实现类-公共汽车
public class Bus implements Car {
    @Override
    public String take() {
        return "乘坐公共汽车...";
    }
}

//交通工具车接口实现类-校车
public class SchoolBus implements Car {
    @Override
    public String take() {
        return "乘坐校车...";
    }
}

//学生类
public class Student {
    //学生回家乘坐的交通工具接口(面向接口编程)
    public void goHome(Car car) {
        System.out.println("学生放学回家" + car.take());
    }
}

//测试
public class Test {
    public static void main(String[] args) {
        new Student().goHome(new Bus());
        new Student().goHome(new SchoolBus());
        new Student().goHome(new Taxi());
    }
}

//输出
学生放学回家乘坐公共汽车...
学生放学回家乘坐校车...
学生放学回家乘坐Taxi...

这样不管学生类乘坐什么交通工具,或者增加新的交通工具,都不要修改原有的代码了。

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值