「全网最细 + 实战源码案例」设计模式——六大设计原则

简介: 本文介绍了面向对象设计中的六大原则,旨在提高软件系统的可维护性、可复用性和可拓展性。这些原则包括:开闭原则(OCP)、里氏代换原则(LSP)、依赖倒转原则(DIP)、接口隔离原则(ISP)、迪米特法则(LoD)和合成复用原则(CARP)。每项原则通过具体示例展示了如何通过抽象、多态、组合等方式降低耦合度,增强系统的灵活性与稳定性,从而提升开发效率并降低成本。

目的

提高软件系统的可维护性和可复用性,增加软件的可拓展性和灵活性,程序员遵循 6 条原则来开发程序,从而提高软件开发效率、节约软件开发成本和维护成本。


开闭原则(OCP)

核心思想

1. 对拓展开放

  • 软件模块应该在不修改原有代码的情况下,通过扩展的方式增加新功能。
  • 目标:提高系统的可拓展性,适应不断变化的需求。

2. 对修改关闭

  • 在新增需求时,尽量避免修改现有代码。
  • 目标:降低由于修改代码引发的潜在问题,提高系统的稳定性。

实现方式

1. 使用抽象

  • 通过定义接口或抽象类,让具体实现依赖于抽象,而不是直接依赖具体类。
  • 增加新功能时,实现新的子类,不需要修改原有类。

2. 多态机制

  • 借助继承与多态,在运行时决定调用哪个具体类实现。

示例

AbstractSkin:

// 皮肤抽象类
public abstract class AbstractSkin {

    public abstract void display();

}

DefaultSpecificSkin:

// 默认皮肤
public class DefaultSpecificSkin extends AbstractSkin{

    @Override
    public void display() {
        System.out.println("默认皮肤.....");
    }
}

SupermanSpecificSkin:

// 超级英雄皮肤
public class SupermanSpecificSkin extends AbstractSkin{
    @Override
    public void display() {
        System.out.println("超人皮肤.....");
    }
}

SougouInput:

// 搜狗输入法
public class SougouInput {

    private AbstractSkin skin;

    public void setSkin(AbstractSkin skin) {
        this.skin = skin;
    }

    public void display() {
        skin.display();
    }
}

Client:

public class Client {
    public static void main(String[] args) {

        // 1.创建搜狗输入法对象
        SougouInput input = new SougouInput();

        // 2.创建皮肤对象
//        AbstractSkin skin = new SupermanSpecificSkin();
        AbstractSkin skin = new DefaultSpecificSkin();

        // 3.设置皮肤
        input.setSkin(skin);

        // 4.显示皮肤
        input.display();
    }
}

里氏代换原则(LSP)

核心思想

子类对象应该能够替代父类对象出现在程序中的位置,并且程序的行为不会因此受到影响。(一个程序使用父类的对象,那么在不改变程序行为的前提下,子类对象可以替代父类对象)。


实现方式

1. 子类不应该改变父类方法的预期行为,尽量保留父类的方法逻辑。

2. 方法的输入输出应符合父类的契约,子类应遵循父类的行为约定。

3. 使用接口或抽象类设计,可以减少子类对父类的依赖。

4. 确保子类与父类的行为一致性,避免引入错误或不一致的行为。


示例

Quadrilateral

// 四边形接口
public interface Quadrilateral {

    double getLength();
    double getWidth();
}

Rectangle

// 矩形类
public class Rectangle implements Quadrilateral{
    private double length;
    private double width;

    public void setLength(double length) {
        this.length = length;
    }

    public void setWidth(double width) {
        this.width = width;
    }

    @Override
    public double getLength() {
        return length;
    }

    @Override
    public double getWidth() {
        return width;
    }
}

Square

public class Square implements Quadrilateral{

    private double side;

    public double getSide() {
        return side;
    }

    public void setSide(double side) {
        this.side = side;
    }

    @Override
    public double getLength() {
        return side;
    }

    @Override
    public double getWidth() {
        return side;
    }
}

RectangleDemo

public class RectangleDemo {

    public static void main(String[] args) {

        // 1.创建长方形对象
        Rectangle rectangle = new Rectangle();

        // 2.设置长和宽
        rectangle.setLength(20);
        rectangle.setWidth(10);

        // 3.调用扩宽方法
        resize(rectangle);

        // 4.打印长和宽
        printLengthAndWidth(rectangle);
    }

    // 扩宽方法
    public static void resize(Rectangle rectangle){
        while (rectangle.getLength() >= rectangle.getWidth()){
            rectangle.setWidth(rectangle.getWidth() + 1);
        }
    }

    // 打印长和宽
    public static void printLengthAndWidth(Quadrilateral quadrilateral){
        System.out.println("长:" + quadrilateral.getLength() + " 宽:" + quadrilateral.getWidth());
    }

}

依赖倒转原则(DIP)

核心思想:

  • 高层模块(负责实现核心业务逻辑的模块)不应该依赖低层模块(负责实现具体的细节,比如数据存储、网络通信),二者应该依赖抽象。
  • 抽象(接口或抽象类)不应该依赖细节(具体实现类),细节应该依赖抽象。
  • 若没有遵循 DIP,高层模块直接依赖于低层模块,使得两者耦合度变高,且低层模块的变化可能会直接影响到高层模块,从而增加了系统的复杂性和维护难度。
  • DIP 的关键在于,将高层模块与低层模块之间的依赖关系通过抽象接口或抽象类来分离,从而减少直接耦合,增加系统的灵活性。

示例

CPU

// 抽象CPU接口
public interface CPU {
    // 运行cpu
    void run();
}

HardDisk

// 硬盘接口
public interface HardDisk {

    // 存储数据
    void save(String data);
    // 获取数据
    String get();
}

Memory

// 内存条接口
public interface Memory {

    void save();
}

IntelCpu

// 英特尔CPU
public class IntelCpu implements CPU{
    @Override
    public void run() {
        System.out.println("英特尔CPU运行");
    }
}

XiJieHardDisk

// 希捷硬盘
public class XiJieHardDisk implements HardDisk{
    @Override
    public void save(String data) {
        System.out.println("希捷硬盘保存数据为:" + data);
    }

    @Override
    public String get() {
        System.out.println("希捷硬盘读取数据");
        return "数据";
    }
}

KingstonMemory

// 金士顿内存条
public class KingstonMemory implements Memory{
    @Override
    public void save() {
        System.out.println("金士顿内存条");
    }
}

Computer

public class Computer {

    private HardDisk hardDisk;
    private CPU cpu;
    private Memory memory;

    public HardDisk getHardDisk() {
        return hardDisk;
    }

    public void setHardDisk(HardDisk hardDisk) {
        this.hardDisk = hardDisk;
    }

    public CPU getCpu() {
        return cpu;
    }

    public void setCpu(CPU cpu) {
        this.cpu = cpu;
    }

    public Memory getMemory() {
        return memory;
    }

    public void setMemory(Memory memory) {
        this.memory = memory;
    }

    // 运行计算机
    public void run() {
        System.out.println("计算机开始运行...");
        String data = hardDisk.get();
        System.out.println("从硬盘中获取数据:" + data);
        cpu.run();
        memory.save();
    }
}

ComputerDemo

public class ComputerDemo {

    public static void main(String[] args) {

        // 1.创建计算机对象
        Computer computer = new Computer();

        // 2.创建计算机组件对象
        HardDisk hardDisk = new XiJieHardDisk();
        CPU cpu = new IntelCpu();
        Memory memory = new KingstonMemory();

        // 3.设置计算机组件
        computer.setHardDisk(hardDisk);
        computer.setCpu(cpu);
        computer.setMemory(memory);

        // 4.运行计算机
        computer.run();
    }
}

接口隔离原则(ISP)

核心思想

  • 不应该强迫一个类依赖于它不需要的接口。(客户端不应被迫实现它不使用的方法)
  • 接口的设计应该小而精,每个接口只包含一组相关的功能,避免一个大型接口包含不相关的方法,从而导致实现类必须实现他们(即使不需要)
  • 这个原则旨在通过拆分接口,使得类只依赖于它实际需要的接口,避免出现类与接口之间的强耦合。

示例


迪米特法则(LoD)

核心思想

  • 一个对象应该对其他对象有最少的了解,即一个对象应该只和它直接相关的对象进行交互,而不应该依赖于它的“朋友”的“朋友”或其他间接对象。
  • 又称最少知识原则,如果两个软件实体无需直接通信,那么就不应该发生直接的相互调用,可以通过第三方转发该调用,以此降低类之间的耦合度,提高模块的相对独立性。
  • 直接的朋友对象:
    • 当前对象本身
    • 当前对象的成员对象
    • 当前对象所创建的对象
    • 当前对象的方法参数等
    • 同当前对象存在关联、聚合和组合关系的对象。

示例


合成复用原则(CARP)

核心思想:

  • 尽量使用对象的组合(聚合)来实现功能复用,而不是通过继承来扩展功能。
  • 组合:一个对象作为另一个对象的成员,通过成员变量引用实现功能的复用。
  • 聚合:一个对象与另一个对象之间通过外部关联实现松散耦合,体现的是“部分-整体”关系。
  • 继承虽是一种复用代码的有效手段,但它是一种强耦合的复用方式,子类过多依赖父类实现,导致灵活性降低。
  • 组合/聚合可以使模块间的耦合度降低,组件可以自由组合,从而提高系统的可扩展性和可维护性。

示例

目录
相关文章
|
13天前
|
设计模式 存储 算法
「全网最细 + 实战源码案例」设计模式——命令模式
命令模式(Command Pattern)是一种行为型设计模式,将请求封装成独立对象,从而解耦请求方与接收方。其核心结构包括:Command(命令接口)、ConcreteCommand(具体命令)、Receiver(接收者)和Invoker(调用者)。通过这种方式,命令的执行、撤销、排队等操作更易扩展和灵活。 适用场景: 1. 参数化对象以操作。 2. 操作放入队列或远程执行。 3. 实现回滚功能。 4. 解耦调用者与接收者。 优点: - 遵循单一职责和开闭原则。 - 支持命令组合和延迟执行。 - 可实现撤销、恢复功能。 缺点: - 增加复杂性和类数量。
50 14
「全网最细 + 实战源码案例」设计模式——命令模式
|
13天前
|
设计模式 存储 Java
「全网最细 + 实战源码案例」设计模式——责任链模式
责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,允许将请求沿着处理者链进行发送。每个处理者可以处理请求或将其传递给下一个处理者,从而实现解耦和灵活性。其结构包括抽象处理者(Handler)、具体处理者(ConcreteHandler)和客户端(Client)。适用于不同方式处理不同种类请求、按顺序执行多个处理者、以及运行时改变处理者及其顺序的场景。典型应用包括日志处理、Java Web过滤器、权限认证等。
51 13
「全网最细 + 实战源码案例」设计模式——责任链模式
|
15天前
|
设计模式 算法 开发者
「全网最细 + 实战源码案例」设计模式——策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,用于定义一系列可替换的算法或行为,并将它们封装成独立的类。通过上下文持有策略对象,在运行时动态切换算法,提高代码的可维护性和扩展性。适用于需要动态切换算法、避免条件语句、经常扩展算法或保持算法独立性的场景。优点包括符合开闭原则、运行时切换算法、解耦上下文与策略实现、减少条件判断;缺点是增加类数量和策略切换成本。示例中通过定义抽象策略接口和具体策略类,结合上下文类实现动态算法选择。
51 8
「全网最细 + 实战源码案例」设计模式——策略模式
|
15天前
|
设计模式 SQL 算法
「全网最细 + 实战源码案例」设计模式——模板方法模式
模板方法模式是一种行为型设计模式,定义了算法的骨架并在父类中实现不变部分,将可变部分延迟到子类实现。通过这种方式,它避免了代码重复,提高了复用性和扩展性。具体步骤由抽象类定义,子类实现特定逻辑。适用于框架设计、工作流和相似算法结构的场景。优点包括代码复用和符合开闭原则,缺点是可能违反里氏替换原则且灵活性较低。
60 7
「全网最细 + 实战源码案例」设计模式——模板方法模式
|
2月前
|
设计模式 前端开发 搜索推荐
前端必须掌握的设计模式——模板模式
模板模式(Template Pattern)是一种行为型设计模式,父类定义固定流程和步骤顺序,子类通过继承并重写特定方法实现具体步骤。适用于具有固定结构或流程的场景,如组装汽车、包装礼物等。举例来说,公司年会节目征集时,蜘蛛侠定义了歌曲的四个步骤:前奏、主歌、副歌、结尾。金刚狼和绿巨人根据此模板设计各自的表演内容。通过抽象类定义通用逻辑,子类实现个性化行为,从而减少重复代码。模板模式还支持钩子方法,允许跳过某些步骤,增加灵活性。
147 11
|
3月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
27天前
|
设计模式
「全网最细 + 实战源码案例」设计模式——模式扩展(配置工厂)
该设计通过配置文件和反射机制动态选择具体工厂,减少硬编码依赖,提升系统灵活性和扩展性。配置文件解耦、反射创建对象,新增产品族无需修改客户端代码。示例中,`CoffeeFactory`类加载配置文件并使用反射生成咖啡对象,客户端调用时只需指定名称即可获取对应产品实例。
88 40
|
5月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
28天前
|
设计模式 关系型数据库
「全网最细 + 实战源码案例」设计模式——简单工厂模式
简单工厂模式是一种创建型设计模式,通过工厂类根据传入参数创建不同类型的对象,也称“静态工厂方法”模式。其结构包括工厂类、产品接口和具体产品类。优点是封装性强、代码复用性好;缺点是扩展性差,增加新产品时需修改工厂类代码,违反开闭原则。适用于对象种类较少且调用者无需关心创建细节的场景。
54 19
|
26天前
|
设计模式 Java
「全网最细 + 实战源码案例」设计模式——生成器模式
生成器模式(Builder Pattern)是一种创建型设计模式,用于分步骤构建复杂对象。它允许用户通过控制对象构造的过程,定制对象的组成部分,而无需直接实例化细节。该模式特别适合构建具有多种配置的复杂对象。其结构包括抽象建造者、具体建造者、指挥者和产品角色。适用于需要创建复杂对象且对象由多个部分组成、构造过程需对外隐藏或分离表示与构造的场景。优点在于更好的控制、代码复用和解耦性;缺点是增加复杂性和不适合简单对象。实现时需定义建造者接口、具体建造者类、指挥者类及产品类。链式调用是常见应用方式之一。
53 12