前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >我常用的六种设计模式

我常用的六种设计模式

作者头像
程序员的园
发布于 2025-03-17 07:36:04
发布于 2025-03-17 07:36:04
8300
代码可运行
举报
运行总次数:0
代码可运行

本文来源于读者朋友提问,期望出一篇设计模式相关的文章。

关于设计模式,我看了很多书籍和视频,但由于部分设计模式并不常用,所以也难免有些遗忘。后来索性就用到哪个时再深入学习哪个。

那我是怎么学习设计模式的呢,我现在会花更多的时间和精力了解设计原则,毕竟李建忠老师曾讲过:了解设计原则远比知道23种设计模式更重要。

本文将在介绍设计原则的基础上,抛砖引玉,简述我常用的六种设计模式。

设计原则

设计原则是架构设计的指导准则,也是我们学习设计模式的基础。八大设计原则如下:

  • 单一职责原则:一个类只做一件事。每个类需要明确的职责划分,如果一个类承担的职责过多,需要思考下,其是否可以继续拆分。
  • 开闭原则:对扩展开放,对修改关闭。该原则是指不要修改现有代码,可以增加新代码来扩展功能。
  • 里氏替换原则:子类可以替换父类,但不影响程序的正确性。
  • 依赖倒置原则:应该依赖抽象而不是具体类。这个我在实际使用较多,通过提供虚基类,其他类依赖虚基类,而不是依赖某个具体的实现类。
  • 接口隔离原则:接口要小而专,避免大而全。其要求接口应该小而完备,多用private,少用public。
  • 合成复用原则:优先使用组合而不是继承。继承在某种程度上会破坏封装性,而组合则不会,组合只要求被组合对象具备完备的接口,这样就可以通过接口来约束,从而可以保证封装性。
  • 封装变化点:使用封装来创建对象间的分解层,让设计者可以在一侧进行修改,而不会影响到另一侧。从而实现层次间的松耦合。
  • 针对接口编程:面向接口编程,而不是面向实现编程。客户程序无需知道对象的具体类型,只需要知道对象所具有的接口。

常用的设计模式

接下来抛砖引玉,简单介绍下我常用的几种设计模式。

单例模式

单例模式保证一个类只有一个实例的特性,适用于日志、公共数据等全局性质的对象。

适用场景

  • 需要频繁实例化的类,如数据库连接池。
  • 资源管理器,控制资源的访问(如数据库连接)。

优点

  • 提供了对唯一实例的全局访问点。
  • 实现了唯一实例的控制。

缺点

  • 违反了单一职责原则(既要负责该类的具体动作,还要负责对象的创建)。
  • 单例模式的泛滥可能导致系统难以测试和维护。尤其是在两个单例相互依赖的场景中。

代码示例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Singleton {
private:
    static Singleton* instance;
    Singleton() {}
public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
};
Singleton* Singleton::instance = nullptr;

工厂模式

工厂模式用于创建对象,尤其是同一类型对象的创建多使用工厂模式,如图形类(下含各种类型的图形)、数据库类(下含各种数据类型的数据库)等。

适用场景

  • 创建对象需要大量重复代码。
  • 创建多个对象时,这些对象属于一个产品族或产品等级结构。

优点

  • 将创建代码集中到一个地方,便于维护。
  • 使得系统在创建对象时更加灵活。

缺点

  • 违反了开闭原则,添加新产品需要修改现有代码。

代码示例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class IProduct {
public:
    virtual void use() = 0;
};

class ConcreteProductA :public IProduct {
public:
    void use() override {
        std::cout << "Using Product A" << std::endl;
    }
};

class ConcreteProductB :public IProduct {
public:
    void use() override {
        std::cout << "Using Product B" << std::endl;
    }
};

class Factory {
public:
    static IProduct* createProduct(int type) {
        if (type == 1) {
            returnnew ConcreteProductA();
        } elseif (type == 2) {
            returnnew ConcreteProductB();
        }
        returnnullptr;
    }
};

策略模式

策略模式用于定义支持不同行为的相同方法,使得方法可以在不改变类的情况下改变行为。最常见的就是普通会员、VIP会员、SVIP会员的计费方法,我们可以使用策略模式来实现。

适用场景

  • 一个类定义了多种行为,并且这些行为在运行时需要动态切换。
  • 需要使用不同的算法变体。

优点

  • 策略对象提供了封装和交换不同算法的能力。
  • 使算法可以独立于使用它的客户而变化。

缺点

  • 客户必须了解不同的策略。
  • 初始代码比简单的方法调用更长。

代码示例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class IStrategy {
public:
    virtual double calculatePrice(double originalPrice) = 0;
};

class NormalStrategy :public IStrategy {
public:
    double calculatePrice(double originalPrice) override {
        return originalPrice;
    }
};

class VIPStrategy :public IStrategy {
public:
    double calculatePrice(double originalPrice) override {
        return originalPrice * 0.8;
    }
};

class SVIPStrategy :public IStrategy {
public:
    double calculatePrice(double originalPrice) override {
        return originalPrice * 0.5;
    }
};

class Context {
private:
    IStrategy* strategy;

public:
    Context(IStrategy* s) : strategy(s) {}

    double executeStrategy(double originalPrice) {
        return strategy->calculatePrice(originalPrice);
    }
};

模板模式

模板模式定义了一个算法的骨架,而将一些步骤延迟到子类中。比如一个扬声器的实现类来讲,出声音具备相同的流程:初始化设备、播放。但是不同的扬声器的初始化设备的方法不同,我们可以使用模板模式来实现。

适用场景

  • 一次性实现一个算法的不变部分,并将可变行为留给子类来实现。
  • 各子类中公共的行为应被提取出来并集中在一个公共父类中以避免代码重复。

优点

  • 封装不变部分,扩展可变部分。
  • 提取公共代码,便于维护。

缺点

  • 父类中的代码可能难以理解。
  • 代码的重构需要花费一定的时间和精力。

代码示例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class AbstractSpeaker {
public:
    void play() {
        initializeDevice();
        openDevice();
        doPlay();
        closeDevice();
    }

protected:
    virtual void initializeDevice() = 0;
    virtual void openDevice() {
        std::cout << "Opening device" << std::endl;
    }
    virtual void doPlay() {
        std::cout << "Playing" << std::endl;
    }
    virtual void closeDevice() {
        std::cout << "Closing device" << std::endl;
    }
};

class SpeakerA :public AbstractSpeaker {
protected:
    void initializeDevice() override {
        std::cout << "Initializing Speaker A" << std::endl;
    }
};

class SpeakerB :public AbstractSpeaker {
protected:
    void initializeDevice() override {
        std::cout << "Initializing Speaker B" << std::endl;
    }
};

观察者模式

观察者模式定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

适用场景

  • 一个抽象模型有两个方面,其中一个是依赖于另一个的。
  • 一个对象需要通知其他对象,而它又不知道这些对象是谁。

优点

  • 实现了主题和观察者之间的抽象耦合。
  • 支持广播通信。

缺点

  • 如果观察者数量很多,可能会导致效率问题。
  • 如果主题和观察者之间的依赖关系复杂,可能会导致系统难以维护。

代码示例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class IObserver {
public:
    virtual void update() = 0;
};

class ISubject {
public:
    virtual void attach(IObserver* observer) = 0;
    virtual void detach(IObserver* observer) = 0;
    virtual void notify() = 0;
};

class ConcreteSubject :public ISubject {
private:
    std::vector<IObserver*> observers;
    int state;

public:
    void attach(IObserver* observer) override {
        observers.push_back(observer);
    }

    void detach(IObserver* observer) override {
        observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
    }

    void notify() override {
        for (IObserver* observer : observers) {
            observer->update();
        }
    }

    void setState(int newState) {
        state = newState;
        notify();
    }
};

class ConcreteObserver :public IObserver {
private:
    ConcreteSubject* subject;

public:
    ConcreteObserver(ConcreteSubject* subj) : subject(subj) {}

    void update() override {
        std::cout << "Observer updated: " << subject->getState() << std::endl;
    }
};

适配器模式

适配器模式分为类适配器和对象适配器,主要用于接口的匹配。

适用场景

  • 想要使用一个已经存在的类,但其接口不符合你的需求。
  • 想要创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。

优点

  • 提高了代码的可复用性。
  • 让那些由于接口不兼容而不能一起工作的那些类可以一起工作。

缺点

  • 过多使用适配器,会让系统变得复杂,不易维护。

代码示例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class ITarget {
public:
    virtual ~ITarget() {}
    virtual void request() = 0;
};

class Adaptee {
public:
    void specificRequest() {
        std::cout << "Adaptee specific request" << std::endl;
    }
};

class Adapter :public ITarget {
private:
    Adaptee* adaptee;

public:
    Adapter(Adaptee* adaptee) : adaptee(adaptee) {}

    void request() override {
        adaptee->specificRequest();
    }
};

总结

本文所述只是我常用的几种设计模式,也仅仅是抛砖引玉。但是设计原则是值得我们深入理解,并值得在工程中加以运用,熟能生巧。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-03-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员的园 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
​设计模式之单例、工厂、发布订阅者模式
近段时间兵长在开发一个小项目,需要用到设计模式的思想,但是兵长苦于没有接触过设计模式,于是走到胖sir座位旁边
阿兵云原生
2023/02/16
3510
详细讲解23种设计模式
工厂方法模式是一种创建型设计模式,它定义了一个用于创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法把对象的实例化推迟到子类。
tokengo
2023/03/01
9320
PHP 中最全的设计模式(23种)
1. 按照目的分,目前常见的设计模式主要有23种,根据使用目标的不同可以分为以下三大类:
botkenni
2019/09/03
1.4K0
PHP 中最全的设计模式(23种)
.NET 中的设计模式应用
设计模式是软件设计与开发过程中常见问题的可复用解决方案。它们是通用模板或最佳实践,用于指导开发人员创建结构良好、可维护且高效的代码。
郑子铭
2025/02/18
540
.NET 中的设计模式应用
每个 C# 开发人员都应该掌握的 5 种强大的设计模式
想象一下你正在建造一栋房子。你是更愿意使用来自专业建筑师那些经过实践检验的蓝图呢,还是从头开始绘制所有的设计图呢?软件开发也是同样的道理!设计模式是针对常见问题的经实践验证的解决方案,它们提供了清晰的蓝图,能让你的代码更高效、更可复用且更易于维护。
郑子铭
2025/02/20
1580
每个 C# 开发人员都应该掌握的 5 种强大的设计模式
巧妙之中见真章:深入解析常用的创建型设计模式
开始之前推荐一篇实用的文章:《mysql数据量很大的数据库迁移最优的方案》,作者:【用户10024547】。
Lion 莱恩呀
2024/11/28
1200
巧妙之中见真章:深入解析常用的创建型设计模式
基础知识_设计模式
文章目录 1. 单例模式 1.1. 懒汉式 1.2. 饿汉式 2. Oberver观察者模式 记录一下常见的设计模式的实现方法(Cpp实现)。 单例模式 当只允许类创建一个实例的时候,可以使用单例模式。 懒汉式 懒汉式是在需要创建实例的时候才创建。 将构造函数设置为私有可以组织创建对象,然后通过static函数从内部调用构造函数。 线程不安全 #include <iostream> using namespace std; class Singleton{ public: static Si
yifei_
2022/11/14
2070
【C++】设计模式:观察者、策略、模板
观察者模式的基本原理,通过观察者模式可以实现对象之间的松耦合,当一个对象的状态发生变化时,所有依赖于它的对象都会得到通知并作出相应的响应。
DevFrank
2024/07/24
1060
设计模式泛谈
设计模式一直是程序员津津乐道的事情,经常codereview的时候就会有人提出,这个代码不符合XX设计原则或者XX设计模式。关于设计模式的书籍市场上也是林林种种,多如牛毛。笔者有幸拜读了GOF(gang of four)的神作《设计模式--可复用的面向对象软件的基础》在感慨四位大师智慧的同时不得不承认有些模式确实是已经跟不上时代了,毕竟这本书是1995年出版的,限于当时机器的一些硬件(内存,cpu等)原因,还有当时一些高级的语言和数据结构和标准没有形成,所以书中会描述一些在今天看来已经跟不上潮流的模式。本文不打算对GOF的23种设计模式一一详细描述,有些比较有共鸣的模式会有具体的代码示例和详细描述,一些没有共鸣的模式可能就一笔带过了,本文中所有的示例都是C++的伪代码,或者是一部分代码。C++实现设计模式就要强依赖虚函数,虚函数可以在运行时动态绑定具体的函数,从而给了程序更多的可拓展性。
用户2937493
2019/09/10
4050
设计模式泛谈
观察者模式
《Head First Design Pattern》一书中对观察者模式的定义如下: The Observer Pattern defines a one-to-many dependency objects so that when one object changes state, all of its dependents are notified and updated automatically.
卡尔曼和玻尔兹曼谁曼
2019/01/22
7400
观察者模式
工作中最常用的 8 种设计模式
设计模式在我们日常的软件开发中无处不在,它们帮助我们编写更易扩展、更具可读性的代码。
苏三说技术
2024/12/19
1540
工作中最常用的 8 种设计模式
C++代码重构和设计模式:改善代码结构和可维护性
在软件开发过程中,代码的结构和可维护性对于项目的成功和长期发展至关重要。对于使用C++编写的代码而言,合理的重构和设计模式的应用可以帮助我们改善代码的结构和可维护性。本文将介绍C++代码重构的基本原则,并探讨一些常见的设计模式在代码重构中的应用。
大盘鸡拌面
2023/12/05
5530
深入浅出设计模式
设计模式目的是为了代码可重用,降低代码的耦合度,提高代码的可扩展性、可维护性,不可为了使用设计模式而过度设计,要平衡复杂度和灵活性。
王二蛋
2024/07/26
950
深入掌握设计模式:提升软件工程的艺术
设计模式是软件工程中的经验总结,是开发高质量、易维护和可扩展的软件的关键。本文将深入探讨一些设计模式,从基础概念到实际应用,帮助开发者更好地理解和运用设计模式来提升软件工程的艺术水平。
海拥
2023/09/16
2620
深入掌握设计模式:提升软件工程的艺术
设计模式奇才:掌握创建型设计模式的核心技巧
设计模式的出现是为了解决软件开发中的一些常见问题,帮助开发人员更高效地编写可维护和可扩展的代码。通过使用设计模式,开发人员可以借鉴先前的成功经验,避免重复发明轮子,同时提高代码的可读性和可理解性。
Lion 莱恩呀
2024/10/15
1170
设计模式奇才:掌握创建型设计模式的核心技巧
设计模式 (二)——观察者模式(Observer,行为型)
使用设计模式可以提高代码的可复用性、可扩充性和可维护性。观察者模式(Observer Pattern)属于行为型模式,在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新。
恋喵大鲤鱼
2018/08/03
6410
设计模式 (二)——观察者模式(Observer,行为型)
C#设计模式15——观察者模式的写法
观察者模式是一种设计模式,它定义了对象之间的一种一对多的依赖关系,使得当一个对象状态发生改变时,它的所有依赖者都能够得到相应的通知并作出相应的反应。观察者模式也被称为发布-订阅模式。
明志德道
2023/10/21
3270
C++设计模式——Observer观察者模式
观察者模式是一种行为型设计模式,又被称为"发布-订阅"模式,它定义了对象之间的一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都会收到通知并自动更新。
Coder-ZZ
2024/07/01
5560
C++设计模式——Observer观察者模式
Android 设计模式之观察者模式
AntDream
2024/06/13
1620
Android 设计模式之观察者模式
设计模式学习(二): 观察者模式 (C#)
《深入浅出设计模式》学习笔记第二章 需求: 开发一套气象监测应用,如图: 气象站,目前有三种装置,温度、湿度和气压感应装置。 WeatherData对象追踪气象站的数据,并更新到布告板,布告板(目前是
solenovex
2018/03/01
7640
设计模式学习(二): 观察者模式 (C#)
相关推荐
​设计模式之单例、工厂、发布订阅者模式
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验