设计模式六大原则(六)----开闭原则

简介: 设计模式六大原则(六)----开闭原则

一. 什么是开闭原则?


开放封闭原则(OCP,Open Closed Principle)是所有面向对象原则的核心。软件设计本身所追求的目标就是封装变化、降低耦合,而开放封闭原则正是对这一目标的最直接体现。其他的设计原则,很多时候是为实现这一目标服务的.


1.1 先来看开闭原则的定义:


Software entities like classes,modules and functions should be open for extension but closed for modifications

一个软件实体, 如类, 模块, 函数等应该对扩展开放, 对修改封闭.

这也是开放封闭原则的核心思想:对扩展开放,对修改封闭.


1.2 这是什么含义呢?


  • 对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
  • 对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对已有代码进行任何修改


二. 如何实现开放封闭原则呢?


“需求总是变化”、“世界上没有一个软件是不变的”。这里投射出的意思是:需求总是变化的, 可是对于软件设计者来说, 如何才能做到不对原有系统修改的前提下, 实现灵活的扩展. 这就是开闭原则要实现的.


我们在设计系统的时候, 不可能设想一次性把需求确定后, 后面就不改变了.这不科学也不现实的. 既然需求是一定会变化的, 那么我们要如何优雅的面对这种变化呢? 如何设计可以使软件相对容易修改, 不至于需求一变, 就要把整个程序推到重来?


开封-封闭原则. 设计软件要容易维护且不容易出问题的最好办法, 就是多扩展, 少修改.

2.1 依赖与抽象


实现开放封闭的核心思想就是面对抽象编程,而不是面对具体编程,因为抽象相对稳定。

让类依赖于固定的抽象,所以对修改是封闭的;而通过面向对象的继承和多态机制,可以实现对抽象体的继承,通过覆写其方法来改变固有行为,实现新的扩展方法,所以对于扩展就是开放的。这是实施开放封闭原则的基本思路。


2.2 如何落地开闭原则


如果当前的设计不符合开放封闭原则,则必须进行重构。常用的设计模式主要有模板方法(Template Method)设计模式策略(Strategy)设计模式。而封装变化,是实现这一原则的重要手段,将经常发生变化的部分封装为一个类。


2.3 开闭原则的重要性


1.开闭原则对测试的影响


开闭原则可是保持原有的测试代码仍然能够正常运行,我们只需要对扩展的代码进行测试就可以了。


2.开闭原则可以提高复用性


在面向对象的设计中,所有的逻辑都是从原子逻辑组合而来的,而不是在一个类中独立实现一个业务逻辑。只有这样代码才可以复用,粒度越小,被复用的可能性就越大。


3.开闭原则可以提高可维护性


面向对象开发的要求。


2.4 如何使用开闭原则


1.抽象约束

第一,通过接口或者抽象类约束扩展,对扩展进行边界限定,不允许出现在接口或抽象类中不存在的public方法;
第二,参数类型、引用对象尽量使用接口或者抽象类,而不是实现类;
第三,抽象层尽量保持稳定,一旦确定即不允许修改。

2.元数据(metadata)控制模块行为

元数据就是用来描述环境和数据的数据,通俗地说就是配置参数,参数可以从文件中获得,也可以从数据库中获得。
Spring容器就是一个典型的元数据控制模块行为的例子,其中达到极致的就是控制反转(Inversion of Control)

3.制定项目章程

在一个团队中,建立项目章程是非常重要的,因为章程中指定了所有人员都必须遵守的约定,对项目来说,约定优于配置。

4.封装变化

对变化的封装包含两层含义:
第一,将相同的变化封装到一个接口或者抽象类中;
第二,将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中。

三. 案例分析


案例一: 画形状


需求: 有圆形, 有椭圆形, 根据要求画出相应的形状

public class GraphicEditor {
    public void draw(Shape shape) {
        if (shape.m_type == 1) {
            drawRectangle();
        } else if(shape.m_type == 2) {
            drawCircle();
        }
    }
    public void drawRectangle() {
        System.out.println("画长方形");
    }
    public void drawCircle() {
        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;
        }
    }
}

我们来看看, 这个代码, 初看是符合要求了, 再想想, 要是我增加一种形状呢? 比如增加三角形.

首先, 要增加一个三角形的类, 继承自Shape

第二, 要增加一个画三角形的方法drawTrriage()

第三, 在draw方法中增加一种类型type=3的处理方案.

这就违背了开闭原则-对扩展开发, 对修改关闭. 增加一个类型, 修改了三处代码.

我们来看看合适的设计

public class GraphicEditor1 {
    public void draw(Shape shape) {
        shape.draw();
    }
    interface Shape {
        void draw();
    }
    class Rectangle implements Shape {
        @Override
        public void draw() {
            System.out.println("画矩形");
        }
    }
    class Circle implements Shape {
        @Override
        public void draw() {
            System.out.println("画圆形");
        }
    }
}

各种类型的形状自己规范自己的行为, 而GraphicEditor.draw()只负责画出来. 当增加一种类型三角形. 只需要


第一: 增加一个三角形的类,实现Shape接口

第二, 调用draw方法,划出来就可以了.

整个过程都是在扩展, 而没有修改原来的类. 这个设计是符合开闭原则的


案例二:


比如现在有一个银行业务, 存钱, 取钱和转账. 最初我们会怎么思考呢?


  1. 首先有一个银行业务类, 用来处理银行的业务
  2. 银行有哪些业务呢? 存钱,取钱,转账, 这都是银行要执行的操作
  3. 那外部说我要存钱, 我要取钱,我要转账, 通过一个类型来告诉我们
    代码就生成了
package com.lxl.www.designPatterns.sixPrinciple.openclosePrinciple.bank;
/**
* 银行业务
*/
public class BankBusiness {
   public void operate(int type) {
       if (type == 1) {
           save();
       } else if(type == 2) {
           take();
       } else if(type == 3) {
           transfer();
       }
   }
   public void save(){
       System.out.println("存钱");
   }
   public void take(){
       System.out.println("取钱");
   }
   public void transfer() {
       System.out.println("转账");
   }
}

咋一看已经实现了需求. 但是现在有新的需求来了, 银行要增加功能---理财. 理财是银行业务的一种, 自然是新增一个方法.


然后在operate()方法里增加一种类型. 这就是一个糟糕的设计, 增加新功能, 但是却修改了原来的代码


我们设计成接口抽象的形式, 上代码

public interface Business {
    public void operate();
}
public class Save implements Business{
    @Override
    public void operate() {
        System.out.println("存钱业务");
    }
}
public class Take implements Business {
    @Override
    public void operate() {
        System.out.println("取钱业务");
    }
}
public class Transfer implements Business {
    @Override
    public void operate() {
        System.out.println("转账业务");
    }
}
/**
 * 银行业务类
 */
public class BankBusinesses {
    /**
     * 处理银行业务
     * @param business
     */
    public void operate(Business business) {
        System.out.println("处理银行业务");
        business.operate();
    }
}

通过接口抽象的形式方便扩展, 加入要新增理财功能. 只需新增一个理财类, 其他业务代码都不需要修改.


其实, 在日常工作中, 经常会遇到这种情况. 因为我们平时写业务逻辑会更多一些, 而业务就像流水账, 今天一个明天一个一点一点的增加. 所以,当业务增加到3个的时候, 我们就要思考, 如何写能够方便扩展j


3.3 关于作答链路的思考


作答链路包括

拉题-->初始化-->答题-->订正-->加积分-->主观题批改-->主观题批改回传等流程.

那么这么一条链路是否可以通过接口抽象的形式规范代码,实现开闭原则呢? 不至于后面增加一种类型, 就需要新增一个方法.


其实, 我觉得是可以的.


四. 总结:



  1. 遵守开闭原则可以提高软件扩展性和维护性。
  2. 大部分的设计模式和设计原则都是在实现开闭原则。
相关文章
|
3月前
|
设计模式
设计模式七大原则
这篇文章介绍了设计模式中的七大原则,特别强调了单一职责原则,即一个类应该只有一个引起其行为变化的原因,以确保类功能的高内聚和低耦合。
|
3月前
|
设计模式 存储 前端开发
React开发设计模式及原则概念问题之自定义Hooks的作用是什么,自定义Hooks设计时要遵循什么原则呢
React开发设计模式及原则概念问题之自定义Hooks的作用是什么,自定义Hooks设计时要遵循什么原则呢
|
2月前
|
设计模式 Java 关系型数据库
设计模式——设计模式简介和七大原则
设计模式的目的和核心原则、单一职责原则、接口隔离原则、依赖倒转原则、里氏替换原则、开闭原则、迪米特法则、合成复用原则
设计模式——设计模式简介和七大原则
|
3月前
|
设计模式 算法 开发者
设计模式问题之最小知识原则(迪米特法则)对代码设计有何影响,如何解决
设计模式问题之最小知识原则(迪米特法则)对代码设计有何影响,如何解决
|
3月前
|
设计模式 前端开发 JavaScript
React开发设计模式及原则概念问题之什么是HOC(Higher-order component),HOC遵循的设计原则都有哪些
React开发设计模式及原则概念问题之什么是HOC(Higher-order component),HOC遵循的设计原则都有哪些
|
3月前
|
设计模式 前端开发 JavaScript
React开发设计模式及原则概念问题之什么是设计模式,单一职责原则如何理解
React开发设计模式及原则概念问题之什么是设计模式,单一职责原则如何理解
|
5月前
|
设计模式 uml
设计模式学习心得之前置知识 UML图看法与六大原则(下)
设计模式学习心得之前置知识 UML图看法与六大原则(下)
40 2
|
5月前
|
设计模式 Java 数据库
深入理解设计模式六大原则
深入理解设计模式六大原则
|
15天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
2月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。

热门文章

最新文章

  • 1
    C++一分钟之-设计模式:工厂模式与抽象工厂
    42
  • 2
    《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
    46
  • 3
    C++一分钟之-C++中的设计模式:单例模式
    54
  • 4
    《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
    38
  • 5
    《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
    62
  • 6
    Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
    57
  • 7
    Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
    41
  • 8
    Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
    50
  • 9
    Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
    106
  • 10
    Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
    78