1面向对象的七大原则
学习视频链接:https://www.bilibili.com/video/BV1G4411c7N4
1.1单一职责原则
单一职责原则:
降低类的复杂度,一个类只能负责一项职责
1.2接口隔离原则(ISP)
接口隔离原则:Interface Segregation Principle
一个类对另一个类的依赖建立在最小接口上
案例1.2:
分析案例:有一个接口Interface 里面有5个方法,其中有两个类,分别实现接口里面的方法,class01 实现接口里面的1 2 3 方法,class02 实现接口里面的1 4 5 方法,如果按照上面的实现方法,那么class01 和class 02 都继承实现了不该实现的方法。此上述方案,违背了接口隔离原则。…
代码如下:
package com.asutin.interfaceSegregation;
public class Segregation {
public static void main(String[] args) {
class01 class01 = new class01();
class01.method1();
class01.method2();
class01.method3();
System.out.println("-------------------------------------");
class02 class02 = new class02();
class02.method1();
class02.method4();
class02.method5();
}
}
//一个接口limi
interface Iop {
void method1();
void method2();
void method3();
void method4();
void method5();
}
//类1 实现接口
//分析原因: CLass01 继承了4 5 方法,本身就不需要这俩方法,但是继承了。违背了接口隔离原则
class class01 implements Iop {
@Override
public void method1() {
System.out.println("类1实现接口中方法01");
}
@Override
public void method2() {
System.out.println("类1实现接口中方法02");
}
@Override
public void method3() {
System.out.println("类1实现接口中方法03");
}
@Override
public void method4() {
System.out.println("类1实现接口中方法04");
}
@Override
public void method5() {
System.out.println("类1实现接口中方法05");
}
}
class class02 implements Iop {
@Override
public void method1() {
System.out.println("类2实现接口中方法01");
}
@Override
public void method2() {
System.out.println("类2实现接口中方法02");
}
@Override
public void method3() {
System.out.println("类3实现接口中方法03");
}
@Override
public void method4() {
System.out.println("类2实现接口中方法04");
}
@Override
public void method5() {
System.out.println("类2实现接口中方法05");
}
}
案例优化1.2:
分析上面的案例,发现了两个类都要实现同一个方法,那么可以将方法1直接放在最小接口里面,这样就能满足接口隔离原则
优化代码如下:
package com.asutin.interfaceSegregationImprove.interfaceSegregation;
public class Segregation {
public static void main(String[] args) {
class01 class01 = new class01();
class01.method1();
class01.method2();
class01.method3();
System.out.println("-------------------------------------");
class02 class02 = new class02();
class02.method1();
class02.method4();
class02.method5();
}
}
//一个接口limi
//解决方案:把公共的方法定义一个最基础的接口
interface base {
void method1();
}
interface Iop1 {
void method2();
void method3();
}
interface Iop2 {
void method4();
void method5();
}
//类1 实现接口
class class01 implements base, Iop1 {
@Override
public void method1() {
System.out.println("类1实现接口中方法01");
}
@Override
public void method2() {
System.out.println("类1实现接口中方法02");
}
@Override
public void method3() {
System.out.println("类1实现接口中方法03");
}
}
class class02 implements base, Iop2 {
@Override
public void method1() {
System.out.println("类2实现接口中方法01");
}
@Override
public void method4() {
System.out.println("类2实现接口中方法04");
}
@Override
public void method5() {
System.out.println("类2实现接口中方法05");
}
}
1.3依赖倒转原则(DIP)
依赖倒转原则(Dependence Inversion Principle):
- 高层模块不应该依赖底层模块,二者都应该依赖器抽象
- 抽象不应该依赖细节,细节应该抽象
- 依赖倒转的中心思想是面向接口编程
- 设计理念: 想对于细节的多边性,抽象的东西要稳定
- 使用接口和抽象类的目的是指定好规范
依赖关系传递的三种方式和应该用案例
1)接口传递
2)构造方法的传递
3)setter方式的传递
案例1.3:
定义一个电子邮件的类,里面有个返回消息的类,接下来有一个类接受消息类,里面还有接受消息的方法,在方法参数为电子邮件类
有邮件类方法返回消息
那么现有新的需求,添加一个新的接受微信、短信的信息的功能,那么这样只能定义上述的类,方法传递参数,违背了依赖到转原则
代码如下:
package com.asutin.inversion;
public class DependencyInversion {
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email());
}
}
class Email {
public String getMessage() {
return "电子邮件:BIAO.YANG@HAND-CHINA.COM";
}
}
//定义一个消息类
/*
* 分析问题:
* 如果获取的对象是一个 微信,短信 则需要新增加类,同时person 也要新增方法
* 解决思路:设计一个抽象的接口IReceiver, 使得Person和接口发生依赖
* Email 微信 短信 都属于接受消息的方位,可以同归实现IReceiver接口。
* 利用依赖倒装原则
*
* */
class Person {
public void receive(Email email) {
System.out.println(email.getMessage());
}
}
优化案例1.3
上述问题的解决思路:
定义一个接口,里面有一个用于接受消息的方法,接下来使用三个类来实现这个接口,分别是 邮件、微信、短信的类实现这个接口,并且重写类里面的方法,这样解决了传递多个类的问题,在返回消息类类中,直接通过接口传递,这样在实例化的时候,我们通过不同的new 对象,就可以达到这样的功能。
代码实现如下:
package com.asutin.inversionImprove;
public class DependencyInversion {
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email());
person.receive(new WeChat());
}
}
//定义一个接口里面有接受消息的类
//使用依赖倒置原则
interface IReceive {
public String getMessage();
}方法
//电子邮件类实现IReceive接口,实现接口里面的方法
class Email implements IReceive {
@Override
public String getMessage() {
return "电子邮件:BIAO.YANG@HAND-CHINA.COM";
}
}
//微信类实现IReceive接口,实现接口里面方法
class WeChat implements IReceive {
@Override
public String getMessage() {
return "微信号:YB3355405368";
}
}
//定义一个消息类
class Person {
//对接口的依赖[传递接口,在实例化的时候我们就可以申明要用哪个类的实例化。]
public void receive(IReceive iReceive) {
System.out.println(iReceive.getMessage());
}
}
三种实现方式
package com.asutin.inversionImplement;
public class DependencyInversion {
public static void main(String[] args) {
//实现接口传递依赖
// OpenAndClose openAndClose = new OpenAndClose();
// openAndClose.open(new JJ());
//实现构造器传递依赖[创建类的时候传递接口对象]
// OpenAndClass openAndClass = new OpenAndClass(new JJ());
// openAndClass.open();
//实现set方法传递参数
OpenAndClose openAndClose = new OpenAndClose();
openAndClose.setItv(new JJ());
openAndClose.open();
}
}
/*
//实现一
class JJ implements ITV {
@Override
public void display() {
System.out.println("JJ电视机启动中....");
}
}
interface ITV { //接口
public void display();
}
//通过接口传递方式实现
interface IOpenAndClose {
public void open(ITV itv);//接口抽象方法,接受接口
}
//实现接口
class OpenAndClose implements IOpenAndClose {
@Override
public void open(ITV itv) {
itv.display();
}
}
*/
/*
//实现
//通过构造方法依赖传递
class JJ implements ITV {
@Override
public void play() {
System.out.println("JJ电视机启动中.....");
}
}
interface ITV {
void play();
}
interface IOpenAndClose {
void open();
}
class OpenAndClass implements IOpenAndClose {
public ITV itv;
public OpenAndClass(ITV itv) {
this.itv = itv;
}
@Override
public void open() {
this.itv.play();
}
}
*/
//set方法传递
interface ITV {
void play();
}
interface IOpenAndClose {
void open();
}
class OpenAndClose implements IOpenAndClose {
public ITV itv;
public void setItv(ITV itv) {
this.itv = itv;
}
@Override
public void open() {
this.itv.play();
}
}
class JJ implements ITV {
@Override
public void play() {
System.out.println("JJ电视机启动中....");
}
}
总结:
- 底层模块尽量使用抽象类或者接口,或者两者都使用
- 变量的声明类型尽量是抽象类或接口,这样我们变量引用和实际对象间,存在一个缓冲层利于优化程序
- 继承使用遵循里氏替换原则
1.4里氏替换原则(LSP)
里氏替换原则(Liskov Substitution Principle)
所有引用基类的地方必须能透明的使用其子类的对象
【通俗的讲:在使用继承的时候,遵循里氏替换原则:在子类中尽量不要重写父类的方法】
案例1.4
A类中有一方法,用作求两数之差,有一个类B extends 类A ,并且重写了A类的方法,这样在B 类的对象调用的时候就会产生误解,
针对以上问题违背了里氏替换原则
代码如下:
package com.asutin.liskov;
public class Liskov {
public static void main(String[] args) {
A a = new A();
System.out.println("11-8=" + a.subtract(11, 8));
System.out.println("1-8=" + a.subtract(1, 8));
System.out.println("--------------B对象调用出错!--------------");
B b = new B();
System.out.println("11-8=" + b.subtract(11, 8));//求11-8 结果确是
// 11+8
System.out.println("1-8=" + b.subtract(1, 8));
System.out.println("========================================");
System.out.println("11+8+9=" + b.subtract1(11, 8));
System.out.println("1+8+9=" + b.subtract1(1, 8));
}
}
//定义求差的类
class A {
public int subtract(int a, int b) {
return a - b;
}
}
//定义B类继承A的方法,并且使用自己独特的方法
class B extends A {
public int subtract(int a, int b) {
return (a + b);
}
public int subtract1(int a, int b) {
return (a + b) + 10;
}
}
优化案例1.4
实现方法很多,看下面的实现。
package com.asutin.liskovImporve;
public class Liskov {
public static void main(String[] args) {
System.out.println("-------A类对象-----------");
A a = new A();
System.out.println("11-8=" + a.subtract(11, 8));
System.out.println("1-8=" + a.subtract(1, 8));
System.out.println("-------B类对象----------");
B b = new B();
System.out.println("11+8=" + b.subtract(11, 8));
System.out.println("1+8=" + b.subtract(1, 8));
System.out.println("11+8+9=" + b.subtract1(11, 8));
System.out.println("1+8+9=" + b.subtract1(1, 8));
System.out.println("B类对象使用A类里面的方法:");
System.out.println("11-8=" + b.subtract3(11, 8));
}
}
//定义一个最基础的类
//里氏替换原则,把继承类抽象一个最基础的类,这样子类之间要有联系,使用组合 聚合 依赖的方式建立关系
class Bases {
}
//定义求差的类
class A extends Bases {
public int subtract(int a, int b) {
return a - b;
}
}
//定义B类继承A的方法,并且使用自己独特的方法
class B extends Bases {
public int subtract(int a, int b) {
return (a + b);
}
public int subtract1(int a, int b) {
return (a + b) + 10;
}
//如果b使用a 中的减法的方法
private A a = new A();
public int subtract3(int a, int b) {
return this.a.subtract(a, b);
}
}
总结:如果子类要重写父类的方法,尽量选择一个最基本的类。
1.5开闭原则(OCP)
- 开闭原则(Open Closed Principle)是编程中最基础、最重要的设计原则。
- 对扩展开发【提供方】,对 修改关闭【使用方】
- 软件要变化,尽量通过扩展软件实体的行为来实现变化,而不是通过修改代码来实现
案例1.5
在使用方尽量不要修改代码。
package com.asutin.openandclose;
public class OpenAndClose {
public static void main(String[] args) {
GraphicEditor graphicEditor = new GraphicEditor();
graphicEditor.drawShape(new Rectangle());
graphicEditor.drawShape(new Circle());
//新增的方法
graphicEditor.drawShape(new Triangle());
}
}
//定义一个绘制图的类
class GraphicEditor {
public void drawShape(Shape s) {
if (s.m_type == 1) {
drawRectangle(s);
} else if (s.m_type == 2) {
drawCircle(s);
} else if (s.m_type == 3) { //新增代码
drawTriangle(s);
}
}
public void drawRectangle(Shape r) {
System.out.println("绘制矩形");
}
public void drawCircle(Shape r) {
System.out.println("绘制圆形");
}
//新增代码
public void drawTriangle(Shape r) {
System.out.println("绘制三角形");
}
}
//基类
class Shape {
int m_type;
}
class Rectangle extends Shape {
Rectangle() {
super.m_type = 1;
}
}
class Circle extends Shape {
Circle() {
super.m_type = 2;
}
}
//****************新增加一个三角形的类,要修改如上操作****************************//
class Triangle extends Shape {
Triangle() {
super.m_type = 3;
}
}
案例优化1.5
package com.asutin.OpenAndCloseImprove;
public class OpenAndClose {
public static void main(String[] args) {
GraphicEditor graphicEditor = new GraphicEditor();
graphicEditor.drawShape(new Rectangle());
graphicEditor.drawShape(new Circle());
graphicEditor.drawShape(new Triangle());
//新增的功能
graphicEditor.drawShape(new Other());
}
}
//定义一个绘制图的类
class GraphicEditor {
public void drawShape(Shape s) {
s.draw();
}
}
//解决方案 定义一个抽象类里面还有画图的方法
abstract class Shape {
int m_type;
public abstract void draw(); //抽象方法,子类继承的时候必须要实现
}
class Rectangle extends Shape {
Rectangle() {
super.m_type = 1;
}
@Override
public void draw() {
System.out.println("绘制矩形....");
}
}
class Circle extends Shape {
Circle() {
super.m_type = 2;
}
@Override
public void draw() {
System.out.println("绘制圆形....");
}
}
class Triangle extends Shape {
Triangle() {
super.m_type = 3;
}
@Override
public void draw() {
System.out.println("绘制三角形....");
}
}
//******* 新添加一个类********
class Other extends Shape {
Other() {
super.m_type = 4;
}
@Override
public void draw() {
System.out.println("其他图形的绘制....");
}
}
1.6 迪米特法则(DP)
- 一个对象对其他对象的了解是最少的【类与类关系密切,那么耦合性就越大】
- 迪米特法则(Demeter Principle ):最少指导原则======【至于直接朋友通信】
- 直接朋友:类对象出现在另一个类的 成员变量,方法参数,方法返回值中的类称为直接朋友,
- 陌生的类最好不要出现在另一个类的局部变量中(类方法中使用了另一个类)
案例1.6:
分析案例:有哪些缺陷
避免在类中出现非直接朋友,如果出现了非直接朋友,那么就会违背了迪米特法则。
案例中在学校管理类中出现了打印学院员工的方法,此方法是中包含了学院员工类,这样在类中出现了非直接朋友,违背了迪米特法则。
package com.asutin.demeter;
import java.util.ArrayList;
import java.util.List;
public class Demeter {
public static void main(String[] args) {
SchoolManager schoolManager = new SchoolManager();
schoolManager.print(new CollegeManager());
}
}
//定义学校总部员工类
class Employee {
private String id;
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
//定义学院员工类
class CollegeEmployee {
private String id;
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
//管理学院员工的管理类
class CollegeManager {
public List<CollegeEmployee> getAllEmployee() {
List<CollegeEmployee> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
CollegeEmployee emp = new CollegeEmployee();
emp.setId("学院员工id=" + i);
list.add(emp);
}
return list;
}
}
//学校管理类
class SchoolManager {
public List<Employee> getAllEmployee() {
List<Employee> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Employee ee = new Employee();
ee.setId("学校员工id=" + i);
list.add(ee);
}
return list;
}
//打印学校员工和学院员工的id
//分析 SchoolManager类的方法中包含的其他类,违背了迪米特法则【需求改进】
public void print(CollegeManager sub) {
System.out.println("---------学院员工的编号--------");
//这里的List集合是由传递的对象来调用的方法
List<CollegeEmployee> list1 = sub.getAllEmployee();
for (CollegeEmployee collegeEmployee : list1) {
System.out.println(collegeEmployee.getId());
}
System.out.println("---------学校员工的编号------");
//这里的List集合是本类的方法返回的集合
List<Employee> list2 = this.getAllEmployee();
for (Employee employee : list2) {
System.out.println(employee.getId());
}
}
}
案例优化1.6
package com.asutin.demeterImprove.demeter;
import java.util.ArrayList;
import java.util.List;
public class Demeter {
public static void main(String[] args) {
SchoolManager schoolManager = new SchoolManager();
schoolManager.print(new CollegeManager());
}
}
//定义学校总部员工类
class Employee {
private String id;
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
//定义学院员工类
class CollegeEmployee {
private String id;
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
//管理学院员工的管理类
class CollegeManager {
public List<CollegeEmployee> getAllEmployee() {
List<CollegeEmployee> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
CollegeEmployee emp = new CollegeEmployee();
emp.setId("学院员工id=" + i);
list.add(emp);
}
return list;
}
//新增到这个类中,此类的方法
public void print() {
System.out.println("---------学院员工的编号--------");
//这里的List集合是由传递的对象来调用的方法
List<CollegeEmployee> list1 = this.getAllEmployee();
for (CollegeEmployee collegeEmployee : list1) {
System.out.println(collegeEmployee.getId());
}
}
}
//学校管理类
class SchoolManager {
public List<Employee> getAllEmployee() {
List<Employee> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Employee ee = new Employee();
ee.setId("学校员工id=" + i);
list.add(ee);
}
return list;
}
//打印学校员工和学院员工的id
public void print(CollegeManager sub) {
// 使用传递的参数打印
System.out.println("=========更新后的运行========");
sub.print();
System.out.println("---------学校员工的编号------");
//这里的List集合是本类的方法返回的集合
List<Employee> list2 = this.getAllEmployee();
for (Employee employee : list2) {
System.out.println(employee.getId());
}
}
}
总结: 迪米特的核心是【降低类之间的耦合】 ,并不是完全没有依赖
1.7合成复用原则(CRP)
Composite Reuse Principle:原则是尽量使用合成、聚合的方式,而不是使用继承