当前位置: 首页 > article >正文

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

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

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


一、前言背景

二、设计模式概述

三、单一职责原则(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、三个价格策略实现类

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。

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


http://www.kler.cn/a/561107.html

相关文章:

  • 二级公共基础之数据库设计基础(一) 数据库系统的基本概念
  • 基于数据可视化学习的卡路里消耗预测分析
  • 计算机毕业设计SpringBoot+Vue.js古典舞在线交流平台(源码+文档+PPT+讲解)
  • 在Spring Boot+Vue前后端分离的项目中使用JWT实现基本的权限校验
  • 2步本地安装部署国产之光大模型DeepSeek,附Mac安装教程和安装包!
  • Oracle Fusion Middleware更改weblogic密码
  • AI手机的技术细节
  • Brave132编译指南 Linux篇 - 构建与运行(七)
  • 安装Redis并把Redis设置成windows下的服务然后进行Redis实例演示
  • QT:paintEvent、QPainter、QPaintDevice
  • **模式的好处 (设计模式)
  • 计算机视觉基础|从 OpenCV 到频域分析
  • vue中使用地图
  • 第十三:路由两个注意点:
  • WordPress ABF Freight Edition sql注入漏洞复现(CVE-2024-13485)(附脚本)
  • 探索分布式 IO 模块网络适配器
  • 向日葵linux端ubuntu24.04LTS安装解决方案
  • Navicat连接GaussDB报错认证协议不支持
  • GEAR: Graph-based Evidence Aggregating and Reasoning for Fact Verification
  • 24.贪心算法2