面向对象六大设计原则
1.单一职责原则 (Single Responsibility Principle)
单一职责原则,简称SRP,定义是应该有且仅有一个类引起类的变更,即:一个类只负责一个职责。
优点:
类的复杂性降低,实现什么职责都有明确的定义;
逻辑变得简单,类的可读性提高了,而且,因为逻辑简单,代码的可维护性也提高了;
变更的风险降低,因为只会在单一的类中的修改。
2.里氏替换原则 (Liskov Substitution Principle)
单一职责原则,简称LSP。定义如下:
复杂定义:如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有对象o1都替换成o2的时候,程序P的行为都没有发生变化,那么类型T2是类型T1的子类型。
简单定义:所有引用基类的地方必须能透明地使用其子类的对象。
通俗地讲,就是:子类可以扩展父类的功能,但不能改变父类原有的功能。
面向对象的三大特征是封装、继承和多态。其中的继承,当子类继承父类时,虽然可以复用父类的代码,但是父类的属性和方法对子类都是透明的,子类可以随意修改父类的成员。若需求变更,子类对父类的方法进行了一些复写的时候,其他的子类可能就需要随之改变,这在一定程度上就违反了封装的原则,解决的方案就是引入里氏替换原则。
里氏替换原则为良好的继承定义了一个规范,它包含了4层含义:
子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法。
子类可以有自己的个性,可以有自己的属性和方法。
子类覆盖或重载父类的方法时输入参数可以被放大。
子类覆盖或重载父类的方法时输出结果可以被缩小,也就是说返回值要小于或等于父类的方法返回值。
3.依赖倒置原则 (Dependence Inversion Principle)
依赖倒置原则,简称DIP,定义是:
高层模块不应该依赖底层模块,两者都应该依赖其抽象;
抽象不应该依赖细节;
细节应该依赖抽象。
不可分割的原子逻辑就是底层模块,原子逻辑的再组装就是高层模块。
在Java中,抽象是指接口或抽象类,两者都不能被实例化;而细节是实现接口或继承抽象类产生的类,也就是可以被实例化的实现类。依赖倒置原则是指模块间的依赖是通过抽象来发生的,实现类之间不发生直接的依赖关系,其依赖关系是通过接口是来实现的,这就是俗称的面向接口编程。所以说依赖倒置原则的核心就是要我们面向接口编程,理解了面向接口编程,也就理解了依赖倒置。
4.接口隔离原则 (Interface Segregation Principle)
接口隔离原则,简称ISP,定义是:
客户端不应该依赖它不需要的接口
也就是说客户端需要什么接口就提供什么接口,把不需要的接口剔除掉,这就需要对接口进行细化,保证接口的纯洁性。换成另一种说法就是,类间的依赖关系应该建立在最小的接口上,也就是建立单一的接口。
建立单一接口,这里需要和单一职责区分。单一职责原则要求的是类和接口职责单一,注重的是职责,一个职责的接口是可以有多个方法的,而接口隔离原则要求的是接口的方法尽量少,模块尽量单一,如果需要提供给客户端很多的模块,那么就要相应的定义多个接口,不要把所有的模块功能都定义在一个接口中,那样会显得很臃肿。
5.迪米特原则 (Law of Demeter)
迪米特原则,简称LoD,也被称为最少知识原则,它描述的规则是:
一个对象应该对其他对象有最少的了解
也就是说,一个类应该对自己需要耦合或调用的类知道的最少,类与类之间的关系越密切,耦合度越大,那么类的变化对其耦合的类的影响也会越大,这也是我们面向设计的核心原则:低耦合,高内聚。
迪米特法则还有一个解释:只与直接的朋友通信。
什么是直接的朋友呢?每个对象都必然与其他对象有耦合关系,两个对象的耦合就成为朋友关系,这种关系的类型很多,例如组合、聚合、依赖等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。
6.开闭原则 (Open Closed Principle)
开闭原则,简称OCP,是Java世界里最基础的设计原则。定义是:
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
简而言之,在编程过程中,需求会不断变化,我们的软件实体应该通过扩展来方式来实现变化,而不是通过修改已有的代码实现变化。所以,我们的程序需要有易于扩展、易于维护、易于升级等特性。
遵循开闭原则的最好手段是抽象,用抽象构建框架,用实现扩展细节。因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节,我们用从抽象派生的实现类来进行扩展,当软件需要发生变化时,我们只需要根据需求重新派生一个实现类来扩展就可以了。当然前提是我们的抽象要合理,要对需求的变更有前瞻性和预见性才行。但从开闭原则的角度说,更好的方式是面向接口编程,这样可以更好的扩展性,同时也可降低耦合,可提高系统的稳定性。
————————————————
版权声明:本文为CSDN博主「樱花语」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zyy_hbcz/article/details/104909965
程序场景
模拟一个生活场景:员工乘坐交通工具(自行车、公交车、地铁)回家。
抽象类的基本概念
- 抽象思维,是人类思维达到高级阶段产生的一种能力,例如,当小孩子思维尚未成熟时,他们只能掌握具体概念,他们在学习代词“你、我、他”时往往遇到困难,因为代词具有较高的抽象性。
- 抽象类就是为了继承而存在的,如果你定义了一个抽象类,却不去继承它,那么等于白白创建了这个抽象类,因为你不能用它来做任何事情。
- 对于一个父类,如果它的某个方法在父类中实现出来没有任何意义,必须根据子类的实际需求来进行不同的实现,那么就可以将这个方法声明为abstract方法,此时这个类也就成为abstract类了。
抽象类
抽象类的定义语法:访问修饰符 abstract class ClassName { }
抽象类和普通类的区别
抽象类要用abstract修饰
普通类可以实例化,抽象类不能实例化
如果一个类继承于一个抽象类,则子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。
抽象方法
访问修饰符 abstract class ClassName {
访问修饰符 abstract 返回值类型 方法名(参数列表…);
}
抽象方法和普通方法的区别
普通方法必须要有方法体,抽象方法不能有方法体(大括号也没有)
抽象方法要用abstract修饰
抽象方法必须存在于抽象类中
抽象类和抽象方法
抽象类与抽象方法的使用
抽象类中可以没有抽象方法,但包含了抽象方法的类必须被定义为抽象类
如果子类没有实现父类的所有抽象方法,子类必须被定义为抽象类
没有抽象构造方法,也没有抽象静态方法
抽象类中可以有非抽象的构造方法,创建子类的实例时可以调用
员工乘坐交通工具回家案例:
生活中的接口
在生活中,接口是一套规范,只要是满足这个规范的设备,就可以将它们组装在一起,从而实现该设备的功能。
接口
在软件中,接口同样是一种规范或标准,它们可以约束类的行为,是一些方法特征的集合,但是没有方法的实现。
定义接口的语法:
[访问修饰符] interface 接口名 extends 父接口1,父接口2,…{
//常量定义
//方法定义
}
类实现接口的语法:
[访问修饰符] class 类名 extends 父类名 implements 接口1,接口2,…{
//类成员
}
接口的特性
- 接口的命名规则与类相同。如果修饰符是public,则该接口在整个项目中可见;如果省略修饰符,则该接口只在当前包可见。
- 接口中可以定义常量,不能定义变量。接口中的属性都会自动用public static final 修饰,即接口中的属性都是全局静态常量。
- 接口中方法自动用public abstract修饰,接口中所有方法都是抽象方法。
- 接口不能实例化,接口中不能有构造方法。
- 接口直接可以通过extends实现继承关系,一个接口可以继承多个接口,但是接口不能继承类。
- 接口的实现类必须实现接口的全部方法,否则必须定义为抽象类。
USB接口案例
接口表示一种能力
“做这项工作需要一个木匠”
木匠并不是指某一个人,而代表一种能力,招聘木匠就是招聘具备该能力的人
接口表示一种能力
体现在接口的方法上,一个类实现了某个接口,就表示这个类具备了某种能力
接口表示一种约定
生活中,我们使用的两相电源插座,规定了
两个接头间的额定电压
两个接头间的距离
接头的形状
接口表示一种约定
有些接口只有名称
程序场景
问题:要求实现打印机打印功能。打印机的墨盒可能是彩色的,也可能是黑白的,所用的纸张可以有多种类型,如A4、B5等,并且墨盒和纸张都不是打印机厂商提供的。
打印机厂商如何避免自己的打印机与市场上的墨盒、纸张不符呢?
使用接口实现打印机打印功能
思路分析
制定墨盒、纸张的约定或标准
其他厂商按照墨盒、纸张的标准生产墨盒、纸张
然后打印机厂商按照约定对墨盒、纸张开发打印机,无论最后使用的是哪个厂商提供的墨盒或纸张,只要符合统一的约定,打印机都可以打印。
面向接口编程
面向接口编程就是先把客户的业务逻辑线提取出来,作为接口,业务具体实现通过该接口的实现类来完成。当客户需求变化时,只需编写该业务逻辑的新的实现类,替换该接口的实现类就可以完成需求,不需要改写现有代码,减少对系统的影响。
面向接口编程的优点
接口体现了约定和实现相分离的原则
面向接口编程就意味着:开发系统时,主体架构使用接口,接口构成系统的骨架,这样就可以通过更换实现接口的类来实现更换需求。
面向接口编程的优点:
- 降低代码间的耦合性
- 易于程序的扩展,提高了程序的可扩性性
- 提高了程序的可维护性
面试题
抽象类和接口的联系和区别?
相同点:
代表系统的抽象层
都不能被实例化
都能包含抽象方法:用于描述系统提供的服务,不必提供具体实现不同点:
在抽象类中可以为部分方法提供默认实现,而接口中只能包含抽象方法
抽象类便于复用,接口便于代码维护
一个类只能继承一个直接的父类,但可以实现多个接口
总结