设计模式觉醒系列(01)设计模式的基石 | 六大原则的核心是什么?

本文涉及的产品
云原生网关 MSE Higress,422元/月
任务调度 XXL-JOB 版免费试用,400 元额度,开发版规格
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 本文介绍了设计模式的六大原则,包括单一职责原则(SRP)、开闭原则(OCP)、里氏替换原则(LSP)、接口隔离原则(ISP)、依赖倒置原则(DIP)和迪米特法则。通过具体案例分析了每个原则的应用场景及优势,强调了这些原则在提升代码可维护性、可复用性、可扩展性和降低耦合度方面的重要作用。文章指出,设计模式的核心在于确保系统模块间的低耦合高内聚,并为后续深入探讨23个经典设计模式打下基础。

读书笔记:活得通透的人,他们都有一个共性:自己不喜欢的事,尽量不做;如果是不得不做,就干脆自己主动去做,做完就放下;即使没有意义、却又不得不做的事,发起后也迅速放下。对于那些意义重大,但是发现自已无法完成的事,也会主动去清除并放下。通透的人不纠结也不会为难自己,专注享受自己喜欢的、擅长的事。即使去追逐那些看起来难度很大的事,也会坚持并享受其中。

关键在于:找到自己的优势、以及劣势,并干脆利落放下那些不擅长的事,坚持充分发挥自己的优势。


一、前言背景

二、设计模式概述

三、单一职责原则(SRP)

3.1 违反单一职责案例

3.2 遵循单一职责正例

四、开闭原则(OCP)

4.1 违反开闭原则案例

4.2 遵循开闭原则正例

4.2.1 代码demo

4.2.2 UML关系类图

五、里氏替换原则(LSP)

六、接口隔离原则(ISP)

七、依赖倒置原则(DIP)

八、迪米特法则

九、六大原则的核心是什么?


一、前言背景

    随着工作年限的不断增长,在技术经验积累的路上,我们在技术框架、性能优化、业务系统架构研发、踩坑经验等方面上投入了非常多的时间。然而在具体的代码架构设计、代码复用性、可读性、可扩展性、可靠性上容易被忽略。而编码能力的底子,除了丰富研发实践经验,设计模式的融会贯通也同样重要。设计模式的思想就像参天大树的根基,对未来可以触达的高度有着举足轻重的影响。

    设计模式的思想理念的应用,不限于技术栈、不限于岗位职责、不限于技术经验水平,只要工作内容与系统项目研发设计相关,都可以学习应用设计模式的优秀设计理念,可以说:每一个IT人员的工作都需要用到设计模式。

    那什么时候需要学习设计模式?个人觉得【有兴趣】任何时候都可以,而且永远都不晚。那什么时候才不需要学设计模式?有一本书作者说:当你心中没有设计模式的时候,就不再需要学习实践设计模式了。也就是人剑合一的最高境界,此后不再需要研究设计模式。

    今天正式开启设计模式觉醒系列分享,将通过结合正反案例、源码分析进行展开。希望未来我们每个人,都可以顺利成为优秀的架构师、行业的技术大佬。

二、设计模式概述

    从JAVA开发角度来看,在做JAVA项目开发时候,最基本的原则是面向对象开发,以及遵循面向对象的三大特性封装、多态、继承,此外还有接口、抽象类、重载等规范。这些都是JAVA开发的【设计模式】规范。而接口、抽象类在设计模式用的非常频繁。

    开篇先简单回顾一下,可能我们被我们忽略或者已经陌生的对象、类、抽象类、接口的概念关系。通俗的讲,类是对象的抽象,接口是对象行为的抽象,而抽象类是类的抽象。

    抽象类和接口的区别在于设计思路,抽象类是自下而上抽象出来,也就是从多个相似对象抽出公共部分。而接口是自上而下的抽象,先定义抽象的功能方法,最后再去实现。

    设计模式,作为核心思想理念,它也有自己的特性原则,以及具体的23个范例模式。

    今天我们简单分享她的6大基本原则,尤其梳理各个原则的优势优点,并打下基础。后续系列2开始,将具体对每个经典设计模式进行详细探讨。

三、单一职责原则(SRP)

    单一职责原则(Single Responsibility Principle),简称是SRP。单一职责原则,原义是:要求一个类只负责一个职责。具体要求是There should never be more than one reason for a class to change,也就是修改该类的原因只能有一个。单一职责是不仅是因为SOLID原则的S,也是六大原则最好理解的一个,所以放在第一位进行分享,下面举例说明。

3.1 违反单一职责案例

    举一个违反该原则的反例,公司实习生小白设计了订单Order这个类,但是承担了多个职责,同时负责订单金额计算、订单报告生成两个功能。以下demo设计问题在于,如果修改报告生成的样式内容,需要修改Order类,可维护性变差。

    具体如下:

package lading.java.designpattern.srp.baddeom;
/**
 * 订单类,负责了订单计算和订单报告生产两个功能
 * 违反单一职责原则
 * @author lading
 */
public class Order {
    private double price;
    private String customerType;
    public Order(double price, String customerType) {
        this.price = price;
        this.customerType = customerType;
    }
    /**
     * 功能1:根据客户类型,计算订单的总价
     *
     * @return
     */
    public double calculateTotal() {
        if (customerType.equals("vip")) {
            return price * 0.8;
        } else if (customerType.equals("normal")) {
            return price;
        }
        return price;
    }
    /**
     * 功能2:生成订单报告打印
     *
     * @return
     */
    public String generateReport() {
        return "原价:" + price + ",用户类型" + customerType + ",订单最终总价为:" + calculateTotal();
    }
}

3.2 遵循单一职责正例

    将Order类拆分为2个类,将订单报告生成功能拆到到OrderReport类去实现。改进后,修改订单生成功能,彼此职责分离,不在需要修改Order类。

package lading.java.designpattern.srp.nicedemo;
/**
 * 订单类,只负责订单最终金额计算
 * @author lading
 */
public class Order {
    private double price;
    private String customerType;
    public Order(double price, String customerType) {
        this.price = price;
        this.customerType = customerType;
    }
    public double getPrice() {
        return price;
    }
    public String getCustomerType() {
        return customerType;
    }
    /**
     * 功能1:根据客户类型,计算订单的总价
     *
     * @return
     */
    public double calculateTotal() {
        if (customerType.equals("vip")) {
            return price * 0.8;
        } else if (customerType.equals("normal")) {
            return price;
        }
        return price;
    }
}
package lading.java.designpattern.srp.nicedemo;
/**
 * 订单报告
 */
public class OrderReport {
    private Order order;
    public OrderReport(Order order) {
        this.order = order;
    }
    /**
     * 功能2:生成订单报告打印
     *
     * @return
     */
    public String generateReport() {
        return "原价:" + order.getPrice() + ",用户类型" + order.getCustomerType() + ",订单最终总价为:" + order.calculateTotal();
    }
}

    单一职责的好处是:由于一个类只负责一个职责,可以有效降低类的复杂性。此外可以提高类的可读性、可维护性,最重要也是降低了变更引起的风险,单元测试,精准测试覆盖将会非常容易实现。

    单一职责原则在现实研发设计实践当中,也不限于类,还有接口、方法也遵循适用。

四、开闭原则(OCP)

    开闭原则(Open-Closed Principle),简称OCP。原意是open for extention对扩展开放,close for modification对修改封闭。它核心思想是实体、类、方法函数,应当允许扩展,但是不许修改。也就是新增功能,可以通过扩展代码去实现,但是不能修改原来的代码。

    接下来我们结合一个案例具体分析应用。比如设计开发订单结算系统核心计算模块,需要根据用户VIP级别、促销活动规则来计算最终订单金额。具体规则有:客户有普通客户无折扣、VIP客户8折、节假日85折活动。

4.1 违反开闭原则案例

    设计一个订单对象Order,一个订单计算器OrderCaculator,还有一个柜台计费终端CounterDemo类。以下demo问题在于,每次新增活动规则,需要修改计算器OrderCaculator,代码的可维护性较差。

package lading.java.designpattern.ocp.baddemo;
public class Order {
    private String customerType;//客户类型
    private double totalPrice;//订单总价
    public Order(String customerType, double totalPrice){
        this.customerType = customerType;
        this.totalPrice = totalPrice;
    }
    public String getCustomerType() {
        return customerType;
    }
    public double getTotalPrice() {
        return totalPrice;
    }
}


package lading.java.designpattern.ocp.baddemo;
/**
 * 订单计费
 */
public class OrderCalculator {
    public double calculate(Order order){
        //VIP客户 8 折
        if("VIP客户".equals(order.getCustomerType())){
            return order.getTotalPrice() * 0.8;
        }
        //节假日 85 折
        if("节假日".equals(order.getCustomerType())){
            return order.getTotalPrice() * 0.85;
        }
        //普通客户 无折扣
        return order.getTotalPrice();
    }
}
package lading.java.designpattern.ocp.baddemo;
/**
 * 计费终端
 */
public class CounterDemo {
    public static void main(String[] args) {
        //订单总价
        double totalPrice = 1000;
        Order orderVip = new Order("VIP客户", totalPrice);
        Order orderHoliday = new Order("节假日", totalPrice);
        Order orderNormalCus = new Order("普通客户", totalPrice);
        OrderCalculator orderCalculator = new OrderCalculator();
        System.out.println("订单总价:"+ totalPrice);
        System.out.println(orderVip.getCustomerType() +" : " + orderCalculator.calculate(orderVip));
        System.out.println(orderVip.getCustomerType() +" : " + orderCalculator.calculate(orderHoliday));
        System.out.println(orderVip.getCustomerType() +" : " + orderCalculator.calculate(orderNormalCus));
    }
}

4.2 遵循开闭原则正例

    引入一个价格策略IPriceStrategy接口,Order订单对象不再依赖具体规则,而是依赖价格策略,新增促销规则,只需要单独实现IPriceStrategy接口,无需修改原来的代码。

    具体代码和UML关系类图如下。

4.2.1 代码demo

1、价格策略接口

package lading.java.designpattern.ocp.nicedemo;
/**
 * 价格策略
 * 1. 定义一个接口,里面定义一个方法,方法入参是订单的价格,返回值是订单的最终价格
 */
public interface IPriceStrategy {
    double calculateTotal(double price);
}

2、三个价格策略实现类

代码语言:txt

复制

package lading.java.designpattern.ocp.nicedemo;
/**
 * 会员价格策略
 * 8折
 */
public class VipPriceStrategy implements IPriceStrategy {
    @Override
    public double calculateTotal(double price) {
        return price * 0.8;
    }
}
package lading.java.designpattern.ocp.nicedemo;
/**
 * 普通客户收费策略-无折扣
 */
public class NormalCusPriceStrategy implements IPriceStrategy
{
    @Override
    public double calculateTotal(double price) {
        return price;
    }
}
package lading.java.designpattern.ocp.nicedemo;
/**
 * 节假日价格策略
 * 85折
 */
public class HolidayPriceStrategy implements IPriceStrategy{
    @Override
    public double calculateTotal(double price) {
        return price * 0.85;
    }
}
// 如要要新增收费规则或者促销策略,继续新增实现IPriceStrategy类即可

3、订单类

package lading.java.designpattern.ocp.nicedemo;
public class Order {
    private double price;
    private IPriceStrategy priceStrategy;
    public Order(double price, IPriceStrategy priceStrategy) {
        this.price = price;
        this.priceStrategy = priceStrategy;
    }
    public double getPrice() {
        return price;
    }
    public double getTotal(){
        return priceStrategy.calculateTotal(price);
    }
}

4、柜台收费终端

package lading.java.designpattern.ocp.nicedemo;
public class CounterDemo {
    public static void main(String[] args) {
        //订单总价
        double price = 1000;
        System.out.println("订单总价:"+ price);
        Order vipOrder = new Order(price, new VipPriceStrategy());
        Order normalOrder = new Order(price, new NormalCusPriceStrategy());
        Order holidayOrder = new Order(price, new HolidayPriceStrategy());
        System.out.println("VIP客户 total price: " + vipOrder.getTotal());
        System.out.println("普通客户 total price: " + normalOrder.getTotal());
        System.out.println("节假日 total price: " + holidayOrder.getTotal());
    }
}

4.2.2 UML关系类图

    在这个正例当中,为了方便管理生成收费策略,可以引入简单工厂模式进行优化。

   避免阅读疲劳以及控制篇幅长度,后面4个原则将不再举例。将在后续23个设计模式里,再继续结合demo案例和六大原则的应用进行探讨。

五、里氏替换原则(LSP)

    里氏替换原则(Liskov Substitution Principle),简称LSP。核心思想是,要求子类必须能够替换父类的对象,而且替换后程序的逻辑行为没有变。该原则对应的就是面向对象编程里的继承思想,子类继承父类后,拥有父类全部功能属性,而且子类可以扩展新增新的功能。但是子类不应该去改变父类之前原有的功能。

    LSP原则的最直观优点,就是提供代码的可复用性,以及降低子类和父类的耦合,提供系统可扩展性。

六、接口隔离原则(ISP)

    接口隔离原则(Interface Segregation Principle),简称ISP,它的核心思想是要求减少接口的复杂性,每个接口应该只负责一项功能,避免一个接口有过多的方法。

    如果一个接口方法比较多,也就是负责的功能可能也会非常多。实现接口的的类,将可能被迫要实现一些自己根本用不上的功能方法。这里就可以看出【接口隔离原则】和【单一职责原则】有异曲同工之妙。虽然各自侧重点不同,一个是针对接口,一个是针对类的设计要求。但在实践中都是要求功能行为职责单一,有效解决接口污染问题,定义小而专注的接口有效提高系统可扩展性、可读性。

七、依赖倒置原则(DIP)

    依赖倒置原则(Dependency Inversion Principle),简称DIP。它的核心思想是通过抽象降低模块间的依赖,所有编程面向接口或者抽象类去进行

    依赖倒置原则,要求模块与模块(或者说类与类)之间的关系通过抽象类或者接口进行交互,达到模块耦合度最小化的目标,它最大的优点就是降低耦合,增强系统代码灵活性、可读性和降低风险错误传播。

    看到这里是有点生涩,而且有没有发现,各个原则一直在强调都是接口、抽象、解耦、可扩展等等这些面向对象设计的核心特点?这个感觉就对了,设计模式的六大原则与面向对象设计思想其实是一致的。

    慢慢来,后面一个个去详细探讨。

八、迪米特法则

    迪米特法则Law of Demeter,也称为最少知识原则(Least Knowledge Principle)。核心思想是要求一个类对依赖的另一个类知道的越少越好,被依赖的类不管多复杂,它的逻辑都封装在内部,尤其是要定义为private属性或者方法,允许对外提供的才修饰为public。该原则最大优点是降低类与类之间的耦合关系

九、六大原则的核心是什么?

    设计模式的六大原则,有其中五个被称为面向对象设计的五大原则,简称SOLID核心原则。S、O、L、I、D分别是单一职责原则SRP,开闭原则OCP,里氏替换原则LSP,接口隔离原则ISP、依赖倒置原则DIP。

    如果说这些原则我们无法时刻完整记住,他们是否有一些共同的核心特性、或者优点?每个人的理解不一样,个人倾向于说,六大原则的核心在于:确保系统代码的可维护性、可复用性、可扩展性(灵活性)、可阅读性,模块之间实现低耦合高内聚。


推荐阅读拉丁解牛相关专题系列(欢迎交流讨论):

1、JVM进阶调优系列(3)堆内存的对象什么时候被回收?

2、JVM进阶调优系列(2)字节面试:JVM内存区域怎么划分,分别有什么用?

3、JVM进阶调优系列(1)类加载器原理一文讲透

4、JAVA并发编程系列(13)Future、FutureTask异步小王子

5、MySQL进阶突击系列(05)突击MVCC核心原理 | 左右护法ReadView视图和undoLog版本链强强联合

目录
打赏
0
2
3
1
106
分享
相关文章
设计模式觉醒系列(03)创建型模式的5个设计模式 | 一口气讲全讲透
本文详细介绍了设计模式中的创建型模式,包括建造者模式、原型模式、单例模式、工厂方法模式和抽象工厂模式。创建型模式关注对象的创建过程,隐藏了创建细节,以提高代码的可维护性和可扩展性。通过具体的实战demo和应用场景分析,展示了每种模式的特点和优势。例如,建造者模式适用于复杂对象的分步骤构建;原型模式通过复制对象实现高效复用;单例模式确保全局唯一实例;工厂方法模式和抽象工厂模式则提供了灵活的对象创建机制,支持多类型产品族的生产。这些模式在实际开发中能够简化客户端代码,提升系统灵活性和复用性。
设计模式觉醒系列(02)这几种设计模式很简单实用 | 相信你肯定见过
本文介绍了设计模式中的模板方法模式、外观模式和单例模式。模板方法模式通过父类定义算法框架,子类实现具体步骤,提高代码复用性和扩展性;外观模式提供高层次接口隐藏子系统复杂性,降低耦合度;单例模式确保一个类只有一个实例,适用于资源开销敏感场景。文中结合实战demo及Tomcat源码应用详细解析了这三种模式的实现与优点,并附带Spring中单例模式的应用示例。
【23种设计模式·全精解析 | 概述篇】设计模式概述、UML图、软件设计原则
本系列文章聚焦于面向对象软件设计中的设计模式,旨在帮助开发人员掌握23种经典设计模式及其应用。内容分为三大部分:第一部分介绍设计模式的概念、UML图和软件设计原则;第二部分详细讲解创建型、结构型和行为型模式,并配以代码示例;第三部分通过自定义Spring的IOC功能综合案例,展示如何将常用设计模式应用于实际项目中。通过学习这些内容,读者可以提升编程能力,提高代码的可维护性和复用性。
【23种设计模式·全精解析 | 概述篇】设计模式概述、UML图、软件设计原则
设计模式七大原则
这篇文章介绍了设计模式中的七大原则,特别强调了单一职责原则,即一个类应该只有一个引起其行为变化的原因,以确保类功能的高内聚和低耦合。
React开发设计模式及原则概念问题之自定义Hooks的作用是什么,自定义Hooks设计时要遵循什么原则呢
React开发设计模式及原则概念问题之自定义Hooks的作用是什么,自定义Hooks设计时要遵循什么原则呢
深入理解MVC设计模式:构建高效Web应用程序的基石
【7月更文挑战第4天】在软件工程领域,设计模式是解决常见问题的一系列经过验证的方法。其中,Model-View-Controller(MVC)设计模式自诞生以来,便成为了构建用户界面,特别是Web应用程序的黄金标准。MVC通过将应用程序逻辑分离为三个核心组件,提高了代码的可维护性、可扩展性和重用性。本文将深入探讨MVC设计模式的原理,并通过一个简单的代码示例展示其应用。
277 0
设计模式六大原则之迪米特法则
设计模式六大原则之迪米特法则
设计模式六大原则之依赖倒置原则
设计模式六大原则之依赖倒置原则
设计模式——设计模式简介和七大原则
设计模式的目的和核心原则、单一职责原则、接口隔离原则、依赖倒转原则、里氏替换原则、开闭原则、迪米特法则、合成复用原则
设计模式问题之最小知识原则(迪米特法则)对代码设计有何影响,如何解决
设计模式问题之最小知识原则(迪米特法则)对代码设计有何影响,如何解决
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等