(每日一问)设计模式:设计模式的原则与分类——如何提升代码质量?

(每日一问)设计模式:设计模式的原则与分类——如何提升代码质量?

设计模式不仅是解决常见软件开发问题的良方,还依赖于一些关键原则来指导其应用。这些原则帮助开发者创建出更灵活、更易维护的代码结构。与此同时,设计模式本身也分为三大类:创建型模式、结构型模式和行为型模式,每一类设计模式都有其独特的用途和应用场景。本文将探讨设计模式的核心原则与分类,并通过具体的代码示例说明如何在实践中应用这些原则,以提升代码的质量和可扩展性。


一、什么是设计模式?

设计模式(Design Patterns)是经验的总结,它们提供了一些可重用的解决方案,用来解决在软件设计中反复出现的问题。设计模式并不是代码,而是为某个特定问题提出的一种思路或者模板,开发者可以根据需求将其应用到具体的代码中。

1.1 设计模式的起源

设计模式的概念最早来源于建筑领域,由建筑学家克里斯托弗·亚历山大提出。他认为建筑设计中有一些通用的解决方案可以重复使用。这个思想后来被引入到软件开发中,由此诞生了软件设计模式。

1.2 设计模式的定义

简而言之,设计模式是一套针对常见软件设计问题的最佳实践,旨在帮助开发者以一种更灵活、更高效的方式进行编程。每种设计模式都有一个特定的名字、描述和用法,并且能够在不同的场景下重复使用。

1.3 设计模式的作用

设计模式在软件开发中扮演了至关重要的角色。它们不仅提供了解决问题的标准化方法,还帮助开发者以更少的代码实现更复杂的功能。以下是设计模式的几个主要作用:

  • 提高代码复用性:设计模式提供了一种通用的解决方案,使开发者能够在不同的项目中重复使用这些解决方案。
  • 增强代码可读性:设计模式有助于创建结构清晰、易于理解的代码,这对于团队协作尤为重要。
  • 简化维护:通过应用设计模式,代码结构更加清晰,模块之间的耦合度降低,从而使后期的维护更加简单。
  • 提高扩展性:设计模式为软件的扩展提供了良好的基础,使得新功能的添加不会破坏原有的系统。

二、设计模式的原则

设计模式的原则是指导我们如何编写优秀软件的基本准则。这些原则不仅适用于设计模式的应用,也适用于软件设计的各个方面。通过遵循这些原则,开发者可以创建出更为灵活、可维护的代码结构。

2.1设计模式原则

2.1.1 单一职责原则(Single Responsibility Principle,SRP)

定义:一个类应该只有一个引起它变化的原因,即一个类只负责一项职责。

解释:单一职责原则强调一个类应仅负责一个功能或模块。将多个职责混合在一个类中,会导致类变得臃肿和复杂,使得维护和修改变得困难。

代码示例

// 图书信息类,负责存储和管理图书的基本信息
public class BookInfo {
    private String title;
    private String author;
    // 省略详细实现
}

// 借阅管理类,负责管理图书的借阅逻辑
public class BorrowingManager {
    public void borrowBook(BookInfo book) {
        // 处理借阅逻辑
    }
    // 省略详细实现
}

// 库存管理类,负责管理图书的库存
public class InventoryManager {
    public void updateInventory(BookInfo book, int change) {
        // 更新库存逻辑
    }
    // 省略详细实现
}

解释:通过将图书信息、借阅管理和库存管理分离到不同的类中,每个类只负责一个职责,这样可以使得代码更清晰、更易于维护。

2.1.2 开闭原则(Open/Closed Principle,OCP)

定义:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。

解释:开闭原则提倡在不修改现有代码的前提下,通过扩展功能来增加新特性。这样可以减少引入新错误的风险,同时提高代码的可扩展性。

代码示例

// 支付接口,定义支付操作
public interface PaymentMethod {
    // 定义一个通用的支付方法,所有具体的支付方式都要实现这个方法
    void pay(double amount);
}

// 实现具体的支付方式:信用卡支付
public class CreditCardPayment implements PaymentMethod {
    @Override
    public void pay(double amount) {
        // 处理信用卡支付的具体逻辑
        System.out.println("Paid " + amount + " using Credit Card.");
    }
}

// 实现具体的支付方式:PayPal支付
public class PayPalPayment implements PaymentMethod {
    @Override
    public void pay(double amount) {
        // 处理PayPal支付的具体逻辑
        System.out.println("Paid " + amount + " using PayPal.");
    }
}

// 支付系统类,依赖于支付接口
public class PaymentProcessor {
    private PaymentMethod paymentMethod;

    // 构造函数接受一个支付方式的实现,通过依赖注入方式注入具体的支付方式
    public PaymentProcessor(PaymentMethod paymentMethod) {
        this.paymentMethod = paymentMethod;
    }

    // 处理支付请求,调用具体支付方式的pay方法
    public void processPayment(double amount) {
        paymentMethod.pay(amount); // 调用具体的支付逻辑
    }
}

如果需要添加一个新的支付方式,例如“Google Pay”,只需创建一个实现PaymentMethod接口的新类:

// 实现具体的支付方式:Google Pay支付
public class GooglePayPayment implements PaymentMethod {
    @Override
    public void pay(double amount) {
        // 处理Google Pay支付的具体逻辑
        System.out.println("Paid " + amount + " using Google Pay.");
    }
}

然后在使用PaymentProcessor时,通过构造函数传入这个新类的实例:

PaymentMethod googlePay = new GooglePayPayment();
PaymentProcessor processor = new PaymentProcessor(googlePay);
processor.processPayment(100.0); // 输出: Paid 100.0 using Google Pay.

解释:通过定义支付接口并实现不同的支付方式,我们可以在不修改PaymentProcessor类的情况下添加新的支付方式,这符合开闭原则。

2.1.3 里氏替换原则(Liskov Substitution Principle,LSP)

定义:子类对象必须能够替换父类对象,并且程序的行为不会改变。

解释:里氏替换原则强调子类必须完全实现父类的功能,并且不能改变父类的预期行为。这确保了继承关系的正确性,并防止多态行为中的意外错误。

代码示例

// 鸟类基类,具有飞行能力
public class Bird {
    public void fly() {
        // 实现飞行功能
    }
}

// 鹰类,继承自鸟类,可以飞行
public class Eagle extends Bird {
    // 继承fly方法
}

// 企鹅类,不会飞行,继承自不能飞的鸟类
public class Penguin extends NonFlyingBird {
    // 企鹅没有fly方法
}

// 不飞的鸟类基类
public class NonFlyingBird extends Bird {
    @Override
    public void fly() {
        //当NonFlyingBird类继承自Bird类时,它重写了fly()方法。Java允许子类重写父类的方法,这样子类可以提供与父类不同的实现。
        throw new UnsupportedOperationException("不飞的鸟不会飞");
    }
}

解释:通过创建一个不飞的鸟类基类,企鹅继承自这个基类,从而避免了子类不完全符合父类行为的问题,符合里氏替换原则。

2.1.4 接口隔离原则(Interface Segregation Principle,ISP)

定义:客户端不应该被强迫依赖于它不需要的接口。

解释:接口隔离原则提倡将庞大的接口拆分为更小、更专一的接口,以便于实现类只需依赖其需要的接口。这减少了不必要的依赖,也提高了代码的灵活性。

代码示例

// 用户添加接口
public interface UserAdd {
    void addUser();
}

// 用户删除接口
public interface UserDelete {
    void deleteUser();
}

// 用户更新接口
public interface UserUpdate {
    void updateUser();
}

// 实现只需要添加用户功能的类
public class UserManager implements UserAdd {
    public void addUser() {
        // 添加用户的实现
    }
}

解释:通过将用户操作接口拆分为更小的接口,实现类只需实现自己所需的接口,避免了不必要的依赖,符合接口隔离原则。

2.1.5 依赖倒置原则(Dependency Inversion Principle,DIP)

定义:高层模块不应该依赖于低层模块,两者都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。

解释:依赖倒置原则强调模块间的依赖关系应该基于抽象(接口或抽象类),而不是具体实现。这有助于减少模块间的耦合,增强系统的可维护性和灵活性。

代码示例

// 电池接口,定义了一个供电方法
public interface Battery {
    void supplyPower(); // 定义供电的方法
}

// AA电池实现类,实现了Battery接口
public class AABattery implements Battery {
    @Override
    public void supplyPower() {
        System.out.println("Providing power using AA Battery");
    }
}

// 锂电池实现类,实现了Battery接口
public class LithiumBattery implements Battery {
    @Override
    public void supplyPower() {
        System.out.println("Providing power using Lithium Battery");
    }
}


// 电源模块类,表示一个依赖于电池供电的模块
public class PowerModule {
    // Battery是一个接口类型,battery是这个接口的引用(字段)
    private Battery battery;

    // 构造函数,接受一个Battery类型的参数
    public PowerModule(Battery battery) {
        // this.battery 指的是当前对象的battery字段
        // battery(参数)是通过构造函数传入的具体Battery实现类的实例
        // 将传入的具体电池实现类保存到PowerModule对象的battery字段
        this.battery = battery;
    }

    // 提供电源的方法,调用Battery接口的supplyPower方法
    public void providePower() {
        battery.supplyPower(); // 使用接口提供电源,不关心具体的电池实现
    }
}

在实际使用中,你可以根据需要选择不同的电池实现类传递给PowerModule,然后调用providePower()方法来为电源模块供电。

public class Main {
    public static void main(String[] args) {
        // 使用AA电池为电源模块供电
        Battery aaBattery = new AABattery(); // 创建一个AA电池的实例
        PowerModule powerModule = new PowerModule(aaBattery); // 创建一个依赖于AA电池的电源模块
        powerModule.providePower(); // 输出: Providing power using AA Battery

        // 使用锂电池为电源模块供电
        Battery lithiumBattery = new LithiumBattery(); // 创建一个锂电池的实例
        PowerModule powerModule2 = new PowerModule(lithiumBattery); // 创建一个依赖于锂电池的电源模块
        powerModule2.providePower(); // 输出: Providing power using Lithium Battery
    }
}

解释:电源模块依赖于抽象的电池接口,而不是具体的电池类型,这样可以轻松替换不同类型的电池,而不需要修改电源模块的代码,符合依赖倒置原则。

2.2如何在设计模式中应用这些原则?

2.2.1 识别职责并分离

在应用设计模式时,始终考虑单一职责原则。例如,在设计工厂模式时,确保每个工厂类只负责创建一种类型的对象,避免让工厂类承担过多职责。

2.2.2 使用接口和抽象类进行扩展

在设计系统时,通过定义接口或抽象类来确保代码对扩展开放、对修改关闭。例如,在策略模式中,将可变的算法部分封装在独立的策略类中,通过接口扩展新的算法,而不修改现有代码。

2.2.3 正确使用继承

继承是设计模式中的一个重要机制,但使用时应遵循里氏替换原则。确保子类可以完全替代父类,而不会引起程序的异常行为。例如,在装饰器模式中,装饰类应能够替换被装饰的对象,而不影响其行为。

2.2.4 拆分接口

在设计接口时,避免创建过于庞大的接口,应用接口隔离原则。例如,在适配器模式中,通过将接口细化,可以避免实现类承担不必要的职责。

2.2.5 依赖于抽象而非具体实现

在设计系统的模块间依赖时,尽量使用依赖倒置原则。例如,在观察者模式中,让观察者依赖于抽象的通知接口,而不是具体的通知实现。

三、设计模式的分类(前面可以不看,归纳表格必看)

常见的设计模式可以分为三大类:创建型模式结构型模式行为型模式。下面是每类设计模式的详细列表及其简要说明:

3.1 创建型模式

设计模式说明
单例模式确保一个类只有一个实例,并提供一个全局访问点。
工厂方法模式定义一个用于创建对象的接口,但由子类决定实例化哪个类。
抽象工厂模式提供一个创建一系列相关或依赖对象的接口,而无需指定它们具体的类。
生成器模式将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
原型模式通过复制现有对象来创建新的对象,而不是直接实例化类。

3.2结构型模式

设计模式说明
适配器模式将一个类的接口转换成客户希望的另一个接口,使原本不兼容的类可以一起工作。
桥接模式将抽象部分与实现部分分离,使它们可以独立变化。
装饰器模式动态地给对象添加一些额外的职责,就增加功能而言,装饰器模式比生成子类更灵活。
外观模式为子系统中的一组接口提供一个一致的界面,定义一个高层接口使子系统更易使用。
享元模式通过共享已经存在的对象来减少创建对象的数量,以节省内存。
代理模式为其他对象提供一种代理以控制对这个对象的访问。

3.3 行为型模式

设计模式说明
责任链模式使多个对象都有机会处理请求,从而避免请求的发送者与接收者耦合在一起。
命令模式将请求封装成对象,使得可以用不同的请求对客户进行参数化。
解释器模式给定一种语言,定义它的文法表示,并定义一个解释器来解释语言中的句子。
迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
中介者模式用一个中介对象来封装一系列对象的交互。中介者使各对象不需要显式地相互引用。
备忘录模式在不破坏封装性的前提下,捕获并保存一个对象的内部状态,以便以后恢复它。
观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都将得到通知并自动更新。
状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
策略模式定义一系列算法,将每一个算法封装起来,并使它们可以相互替换。
模板方法模式定义一个操作中的算法骨架,而将一些步骤延迟到子类中。
访问者模式在不改变数据结构的前提下,增加作用于对象结构上的新操作。

四、总结

设计模式的核心原则为我们提供了编写高质量代码的指南,而设计模式的分类则帮助我们更好地理解这些模式的应用场景。通过遵循单一职责、开闭原则等关键原则,开发者可以设计出更加灵活和可扩展的软件系统。同时,理解并应用创建型、结构型和行为型模式,可以使我们在面对不同类型的问题时,选择最合适的解决方案。在实际开发中,掌握这些原则和模式,能够显著提升代码质量,使软件开发更加高效和可靠。

✨ 我是专业牛,一个渴望成为大牛🏆的985硕士🎓,热衷于分享知识📚,帮助他人解决问题💡,为大家提供科研、竞赛等方面的建议和指导🎯。无论是科研项目🛠️、竞赛🏅,还是图像🖼️、通信📡、计算机💻领域的论文辅导📑,我都以诚信为本🛡️,质量为先!🤝 如果你觉得这篇文章对你有所帮助,别忘了点赞👍、收藏📌和关注🔔哦!你的支持是我继续分享知识的动力🚀!✨ 如果你有任何问题或需要帮助,随时留言📬或私信📲,我都会乐意解答!😊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

upgrador

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值