白话设计模式之23种设计模式全解析

大家好!在软件开发的漫漫长路上,设计模式是我们提升编程能力、优化代码结构的得力助手。但这些模式常常让人觉得晦涩难懂,今天我就想和大家一起,把那些复杂的设计模式用通俗易懂的方式讲清楚,咱们共同学习进步!

一、设计模式的分类和整体框架

设计模式就像是一套编程的“武功秘籍”,根据解决问题的不同侧重点,主要分为创建型、结构型和行为型这几大类。创建型模式专注于对象的创建过程,比如怎么巧妙地生成对象;结构型模式则关注如何把类和对象组合起来,构建出更强大、更灵活的结构;行为型模式主要解决对象之间的交互和职责分配问题。

在这篇文档里,介绍了GoF(四人帮,即《设计模式 - 可复用面向对象软件的基础》的四位作者)提出的23种经典设计模式。从简单工厂模式开始,虽然它严格来说不算标准设计模式,但可以当作我们学习的热身运动。之后,就正式进入这23种模式的奇妙世界了。

二、各类设计模式详解

(一)创建型模式

  1. 单例模式:保证一个类只有一个实例,并且提供一个全局访问点。这就好比一个公司的总经理,整个公司只有一个,各个部门要找总经理办事,都通过特定的渠道(全局访问点)来联系。在代码里,比如配置文件的管理类,整个系统只需要一份配置信息,就可以用单例模式来实现。
class Singleton {
private:
    static Singleton* instance;
    Singleton() {}
public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
};
Singleton* Singleton::instance = nullptr;
  1. 工厂方法模式:定义一个创建对象的接口,让子类决定实例化哪个类。这就像开了一家手机工厂,工厂只规定了生产手机的接口,具体生产哪种型号的手机(实例化哪个类),由各个子工厂(子类)来决定。比如生产不同品牌手机的子工厂,各自决定生产自己品牌的手机。
  2. 抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,无需指定它们具体的类。想象一下,有一个家具工厂,它可以生产沙发、茶几等一系列家具,但是具体生产什么风格(比如欧式、中式)的家具,不需要在工厂接口里指定,由具体的生产子类来决定。

(二)结构型模式

  1. 外观模式:为子系统中的一组接口提供一个一致的界面,让子系统更容易使用。就像去医院看病,医院的导诊台就是外观模式的体现。导诊台把挂号、看病、缴费、取药等一系列复杂的流程整合起来,给患者提供一个统一的服务接口,让患者不用自己去了解医院内部各个科室和流程的细节。
  2. 适配器模式:把一个类的接口转换成客户希望的另一个接口,让原本不兼容的类可以一起工作。比如,你有一个老式的圆孔插座,但是你的新电器是扁头插头,这时就需要一个转换插头(适配器),把圆孔插座的接口转换成适合扁头插头的接口,让电器能正常使用。
  3. 代理模式:为其他对象提供一种代理,用来控制对这个对象的访问。比如明星有经纪人,经纪人就是明星的代理。粉丝想找明星签名、合影,都得通过经纪人安排,经纪人可以控制明星与外界的接触,保护明星的时间和隐私。

(三)行为型模式

  1. 观察者模式:定义对象间的一对多依赖关系,当一个对象状态改变时,依赖它的所有对象都会收到通知并自动更新。这就好比微博上的大V发了一条新动态,关注他的粉丝(依赖对象)都会收到推送通知,知道大V有新消息了。
  2. 命令模式:将一个请求封装为一个对象,这样就可以用不同请求对客户进行参数化,还能实现请求排队、记录日志以及支持撤销操作。例如在游戏里,玩家的操作(如攻击、防御)都可以封装成命令对象,这样可以方便地实现操作记录、撤销上一步操作等功能。
  3. 迭代器模式:提供一种顺序访问聚合对象中各个元素的方法,同时又不暴露对象的内部表示。比如你有一个装满水果的篮子,迭代器就像是一个可以按顺序拿出水果的工具,你不用知道篮子内部是怎么存放水果的,只需要通过迭代器就能一个一个地拿出水果。

三、学习设计模式的思路

文档里还提到了学习设计模式的好方法。先从实际场景出发,看看在某个具体的应用场景中会遇到什么问题,如果不用设计模式,我们通常会怎么解决,再分析这种普通解决方案存在哪些不足。接着,引入对应的设计模式,学习它的定义、结构,看看如何用它来解决刚才的问题。

学习模式的示例代码也很重要,通过代码能更直观地理解模式的实现方式。还要深入探讨模式的功能、运行顺序、优缺点等,思考在什么情况下适合使用这个模式,以及它和其他模式有什么关系。只有这样全面地学习,才能真正掌握设计模式的精髓。

四、代码示例:简单的观察者模式实现

下面通过一个简单的代码示例,帮助大家更好地理解观察者模式。假设我们有一个天气站,当天气发生变化时,要通知所有关注天气的用户。

#include <iostream>
#include <vector>
#include <string>

// 观察者接口
class Observer {
public:
    virtual void update(const std::string& weather) = 0;
    virtual ~Observer() {}
};

// 具体观察者
class User : public Observer {
private:
    std::string name;
public:
    User(const std::string& n) : name(n) {}
    void update(const std::string& weather) override {
        std::cout << name << " 收到天气更新: " << weather << std::endl;
    }
};

// 被观察对象(天气站)
class WeatherStation {
private:
    std::vector<Observer*> observers;
    std::string weather;
public:
    void addObserver(Observer* observer) {
        observers.push_back(observer);
    }
    void removeObserver(Observer* observer) {
        for (auto it = observers.begin(); it != observers.end(); ++it) {
            if (*it == observer) {
                observers.erase(it);
                break;
            }
        }
    }
    void setWeather(const std::string& w) {
        weather = w;
        notifyObservers();
    }
    void notifyObservers() {
        for (Observer* observer : observers) {
            observer->update(weather);
        }
    }
};

在这个示例中,User类是具体的观察者,实现了update方法来接收天气更新信息。WeatherStation类是被观察对象,它维护了一个观察者列表,当天气变化时,会通知所有的观察者。

五、总结与期待

设计模式是软件开发中的宝贵财富,掌握它们能让我们的代码更优雅、更易维护。希望大家通过这篇文章,对23种设计模式有更清晰的认识。

写作不易,如果这篇文章对你有所帮助,希望你能点赞、评论,让我知道我的分享对你有价值。也欢迎大家关注我的博客,我会持续分享更多关于设计模式和软件开发的知识,咱们下次再见!

更多推荐