您的位置:首页 > 编程语言 > Java开发

Java设计模式之七大原则

2020-03-08 13:32 399 查看

Java设计模式之七大原则

  • 总结
  • 设计模式作用

    • 代码重用性
    • 可读性
    • 可扩展性
    • 可靠性 (即:当我们增加新的功能后,对原来没有影响 )
    • 使程序呈现 高内聚 ,低耦合

    常用七大原则

    单一职责原则(Single Responsibility Principle)

    • 参考链接

      https://mp.weixin.qq.com/s/yPcjk-e1aKT_jIDWnW7wqQ

    • 概念
      对类来说的,即一个类应该只负责一项职责

    • 适用范围

      如类A负责两个不同职责:职责1,职责2。 当职责1需求变更而改变A时,可能造成职责2执行错误, 所以需要将类A的粒度分解为 A1,A2

      单一职责原则适用的范围有接口、方法、类。按大家的说法,接口和方法必须保证单一职责,类就不必保证,前提是类中方法数量足够少,可以在方法级别保持单一职责原则

    • 案例代码

    接口隔离原则(Interface Segregation Principle)

    • 参考链接

      https://mp.weixin.qq.com/s/g9nVW3WJIfe9f-bu6ZYSAw

    • 概念

      客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上

    • 适用范围

      类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D,如果接口Interface1对于类A和类C来说都不是最小接口,那么类B和类D必须去实现他们不需要的方法

      将接口Interface1拆分独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。

    • 案例代码

    依赖倒置原则(Dependence Inversion Principle)

    • 参考链接

      https://mp.weixin.qq.com/s/fb9X_HN7Gb_xj0JAh2Do9w

    • 概念

        高层模块不应该依赖低层模块,两者都应该依赖其抽象(模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的)

      1. 抽象不应该依赖细节(接口或抽象类不依赖于实现类)

      2. 细节应该依赖抽象(实现类依赖接口或抽象类)

      3. 依赖倒置的中心思想是面向接口编程

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

    • 适用范围

      Spring框架中的DI(依赖注入)思想

    • 依赖传递方式

        接口传递

        interface IOpenAndClose {//开关的接口
        public void open(ITV tv); //抽象方法,接收接口
        }
        
        interface ITV { //ITV接口
        public void play();
        }
        
        class ChangHong implements ITV {
        
        @Override
        public void play() {
        // TODO Auto-generated method stub
        System.out.println("长虹电视机,打开");
        }
        
        }
        // 实现接口
        class OpenAndClose implements IOpenAndClose{
        public void open(ITV tv){
        tv.play();
        }
        }
      1. 构造方法传递

        interface IOpenAndClose {
        public void open(); //抽象方法
        }
        
        interface ITV { //ITV接口
        public void play();
        }
        
        class OpenAndClose implements IOpenAndClose{
        
        public ITV tv; //成员
        public OpenAndClose(ITV tv){ //构造器
        this.tv = tv;
        }
        public void open(){
        this.tv.play();
        }
        }
      2. setter方式传递

        interface IOpenAndClose {
        public void open(); // 抽象方法
        
        public void setTv(ITV tv);
        }
        
        interface ITV { // ITV接口
        public void play();
        }
        
        class OpenAndClose implements IOpenAndClose {
        private ITV tv;
        
        public void setTv(ITV tv) {
        this.tv = tv;
        }
        
        public void open() {
        this.tv.play();
        }
        }
        
        class ChangHong implements ITV {
        
        @Override
        public void play() {
        // TODO Auto-generated method stub
        System.out.println("长虹电视机,打开");
        }
        
        }
    • 案例代码

        老板抽象类及类

        public abstract class Boss {
        
        private Staff staff;
        
        public Boss(Staff staff) {
        this.staff = staff;
        }
        
        public abstract void support();
        
        public abstract void askHelp(Boss boss);
        
        public Staff getStaff() {
        return staff;
        }
        
        public void setStaff(Staff staff) {
        this.staff = staff;
        }
        }
        
        public class BossImpl extends Boss {
        
        public BossImpl(Staff staff) {
        super(staff);
        }
        
        @Override
        public void support() {
        this.getStaff().service();
        }
        
        @Override
        public void askHelp(Boss boss) {
        boss.support();
        }
        }
      1. 员工抽象类及类

        public abstract class Staff {
        
        private String name;
        
        public abstract void service();
        
        public abstract void askHelp(Boss boss);
        
        public String getName() {
        return name;
        }
        
        public void setName(String name) {
        this.name = name;
        }
        }
        
        public class StaffImpl extends Staff{
        
        public StaffImpl(String name) {
        this.setName(name);
        }
        
        @Override
        public void service() {
        
        System.out.println(this.getName() + "提供服务");
        }
        
        @Override
        public void askHelp(Boss boss) {
        boss.support();
        }
        }
      2. 测试方法

        public class DependenceInversionTest {
        
        public static void main(String[] args) {
        Staff staffA = new StaffImpl("A员工");
        Staff staffB = new StaffImpl("B员工");
        
        Boss bossA = new BossImpl(staffA);
        Boss bossB = new BossImpl(staffA);
        
        //A老板向B老板求助
        bossA.askHelp(bossB);
        
        //B员工向A老板求支援
        staffB.askHelp(bossA);
        
        //A老板辞退了A员工,换成了C员工
        Staff staffC = new StaffImpl("C员工");
        bossA.setStaff(staffC);
        
        //B员工向A老板求支援
        staffB.askHelp(bossA);
        }
        }
    • 注意事项

        低层模块尽量都要有抽象类或接口,或两者都有,程序稳定性更好

      1. 变量都声明类型尽量是抽象类或接口,这样变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化

      2. 继承时遵循里式替换原则

    里式替换原则(Liskov Substitution Principle)

    • 参考链接

      https://mp.weixin.qq.com/s/qjkPRhYWBEax8A59wZY3Lw

    • 概念

        如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。(假如 A 能胜任 B 干的所有事情,那 B 就是 A 的父亲,也就是儿子要会父亲的所有能活,儿子活得再烂也要有父亲的水平。)

      1. 所有引用基类的地方必须能透明地使用其子类的对象

    • 适用范围

      当需要重新父类(抽象类)原有非抽象方法时,将原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖、聚合、组合等关替代

    • 案例代码

    • 注意事项

        在使用继承时,遵循里式替换原则,在子类中尽量不要重写父类方法

      1. 里式替换原则中,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合、组合、依赖来解决问题

    开闭原则ocp(Open Closed Principle)

    • 参考链接
      https://mp.weixin.qq.com/s/szBP4A1TFQL1RVZvXEknRA

    • 概念

      软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的

    • 适用范围

      编程中遵循其他原则,以及使用设计模式的目的就是遵循开闭原则

    • 案例代码

        改造前

        public class Ocp {
        
        public static void main(String[] args) {
        //使用看看存在的问题
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Rectangle());
        graphicEditor.drawShape(new Circle());
        graphicEditor.drawShape(new Triangle());
        }
        
        }
        
        //这是一个用于绘图的类 [使用方]
        class GraphicEditor {
        //接收Shape对象,然后根据type,来绘制不同的图形
        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(" 绘制三角形 ");
        }
        }
        
        //Shape类,基类
        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. 改造后

        //改进思路:把创建Shape类做成抽象类,并提供了一个抽象的draw方法,让子类去实现即可,当有新的图形种类时,只需要让新的图形类继承Shape,并实现darw方法即可,使用方的代码不需要修改
        public class Ocp {
        
        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 OtherGraphic());
        }
        
        }
        
        //这是一个用于绘图的类 [使用方]
        class GraphicEditor {
        //接收Shape对象,调用draw方法
        public void drawShape(Shape s) {
        s.draw();
        }
        
        }
        
        //Shape类,基类
        abstract class Shape {
        int m_type;
        
        public abstract void draw();//抽象方法
        }
        
        class Rectangle extends Shape {
        Rectangle() {
        super.m_type = 1;
        }
        
        @Override
        public void draw() {
        // TODO Auto-generated method stub
        System.out.println(" 绘制矩形 ");
        }
        }
        
        class Circle extends Shape {
        Circle() {
        super.m_type = 2;
        }
        @Override
        public void draw() {
        // TODO Auto-generated method stub
        System.out.println(" 绘制圆形 ");
        }
        }
        
        //新增画三角形
        class Triangle extends Shape {
        Triangle() {
        super.m_type = 3;
        }
        @Override
        public void draw() {
        // TODO Auto-generated method stub
        System.out.println(" 绘制三角形 ");
        }
        }
        
        //新增一个图形
        class OtherGraphic extends Shape {
        OtherGraphic() {
        super.m_type = 4;
        }
        
        @Override
        public void draw() {
        // TODO Auto-generated method stub
        System.out.println(" 绘制其它图形 ");
        }
        }

    迪米特法则(Law of Demeter)

    • 参考链接

      https://mp.weixin.qq.com/s/Dxhdzfvfv0mI1_lDhrG8kw

    • 概念

        每个单元对于其他的单元只能拥有有限的知识:只是与当前单元紧密联系的单元

      1. 每个单元只能和它的朋友交谈:不能和陌生单元交谈

      2. 只和自己直接的朋友交谈

    • 适用范围

      每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式:依赖、关联、组合、聚合等。其中。我们称出现在成员变量,方法参数,方法返回值中的类为直接朋友,而出现在局部变量中的类不是直接朋友。

    • 案例代码

      //客户端
      public class Demeter1 {
      
      public static void main(String[] args) {
      System.out.println("~~~使用迪米特法则的改进~~~");
      //创建了一个 SchoolManager 对象
      SchoolManager schoolManager = new SchoolManager();
      //输出学院的员工id 和  学校总部的员工信息
      schoolManager.printAllEmployee(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<CollegeEmployee>();
      for (int i = 0; i < 10; i++) { //这里我们增加了10个员工到 list
      CollegeEmployee emp = new CollegeEmployee();
      emp.setId("学院员工id= " + i);
      list.add(emp);
      }
      return list;
      }
      
      //输出学院员工的信息
      public void printEmployee() {
      //获取到学院员工
      List<CollegeEmployee> list1 = getAllEmployee();
      System.out.println("------------学院员工------------");
      for (CollegeEmployee e : list1) {
      System.out.println(e.getId());
      }
      }
      }
      
      //学校管理类
      
      //分析 SchoolManager 类的直接朋友类有哪些 Employee、CollegeManager
      //CollegeEmployee 不是 直接朋友 而是一个陌生类,这样违背了 迪米特法则
      class SchoolManager {
      //返回学校总部的员工
      public List<Employee> getAllEmployee() {
      List<Employee> list = new ArrayList<Employee>();
      
      for (int i = 0; i < 5; i++) { //这里我们增加了5个员工到 list
      Employee emp = new Employee();
      emp.setId("学校总部员工id= " + i);
      list.add(emp);
      }
      return list;
      }
      
      //该方法完成输出学校总部和学院员工信息(id)
      void printAllEmployee(CollegeManager sub) {
      
      //分析问题-改进地方
      //1. 将输出学院的员工方法,封装到CollegeManager
      sub.printEmployee();
      
      //获取到学校总部员工
      List<Employee> list2 = this.getAllEmployee();
      System.out.println("------------学校总部员工------------");
      for (Employee e : list2) {
      System.out.println(e.getId());
      }
      }
      }
    • 注意事项

      迪米特法则的核心是降低类之间的耦合,并不是要求完全没有依赖关系

    合成复用原则(Composite Reuse Principle)

    • 参考链接

      https://blog.csdn.net/u012361379/article/details/88605867

    • 概念

      原则是尽量使用合成/聚合的方式,而不是使用继承

      继承是is-A,聚合是has-A

    • 适用范围

      当B类要使用A类的方法,尽量选用聚合方式解耦

    • 其他

      合成表示一种强的拥有关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样,打个比方:人有两个胳膊,胳膊和人就是部分和整体的关系,人去世了,那么胳膊也就没用了,也就是说胳膊和人的生命周期是相同的

      聚合表示一种弱的拥有关系,体现的是A对象可以包含B对象,但是B对象并不是A对象的一部分,打个比方:人是群居动物,所以每个人属于一个人群,一个人群可以有多个人,所以人群和人是聚合的关系

    总结

    • 核心思想

        找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起

      1. 针对接口编程,而不是针对实现编程

      2. 为了交互对象之间的松耦合设计而努力

    • 点赞
    • 收藏
    • 分享
    • 文章举报
    Charles Yan 发布了16 篇原创文章 · 获赞 0 · 访问量 317 私信 关注
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: 
    相关文章推荐