面向对象的七大设计原则

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:原则是尽量使用合成、聚合的方式,而不是使用继承

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值