深入浅出设计模式——创建型模式之阶段测试

发布于:2025-08-12 ⋅ 阅读:(17) ⋅ 点赞:(0)


代码仓库

1. 面向对象的特点是什么?

可维护、可复用、可扩展、灵活性好。

2. 让面向对象保持结构良好的秘诀是什么?

让面向对象保持结构良好的秘诀就是设计模式,面向对象结合设计模式,才能真正体会到程序变得可维护、可复用、可扩展、灵活性好。

3. 六大设计原则是什么?

开闭原则、单一职责原则、里氏替换原则、依赖倒置原则、迪米特原则、接口隔离原则。

  • 单一职责原则(SRP)
    一个类/模块只负责一项职责(只有一种导致它变化的原因)。好处:降低耦合、提升可维护性。
  • 开闭原则(OCP)
    对扩展开放、对修改关闭。通过抽象与多态,让新增功能以“加类/加实现”为主,尽量不改已有代码。
  • 里氏替换原则(LSP)
    子类必须能替换父类而不破坏程序正确性。不要收紧父类的前置条件、不要放宽后置条件;遵守行为契约。
  • 依赖倒置原则(DIP)
    高层模块不依赖低层模块,二者都依赖抽象;具体实现依赖抽象而不是相反。常用依赖注入/接口注入落地。
  • 接口隔离原则(ISP)
    使用多个小而专用的接口,而不是一个大而全的“胖接口”。客户端不应被迫依赖它不需要的方法
  • 迪米特法则(LoD,最少知道原则)
    减少不必要的耦合,只与“直接的朋友”交互, 避免“火车点”式调用(a.b().c().d()),隐藏内部细节。

4. 什么是里氏替换原则?

子类应该可以完全替换父类。也就是说在使用继承时,只扩展新功能,而不要破坏父类原有的功能。

5. 工厂模式是用于达到什么目的的设计模式?

封装对象。

6. 工厂模式有哪三种?

简单工厂模式、工厂方法模式、抽象工厂模式。

7. 工厂方法模式解决了简单工厂模式的哪两个弊端?

当生产的产品种类越来越多时,工厂类不会变成超级类。工厂类会越来越多,保持灵活。不会越来越大、变得臃肿。如果苹果的生产过程需要修改时,只需修改苹果工厂。梨子的生产过程需要修改时,只需修改梨子工厂。符合单一职责原则。

当需要生产新的产品时,无需更改既有的工厂,只需要添加新的工厂即可。保持了面向对象的可扩展性,符合开闭原则。

8. 抽象工厂模式是什么样的?

在创建时指定了具体的工厂类后,在使用时就无需再关心是哪个工厂类,只需要将此工厂当作抽象的 IFactory 接口使用即可。抽象工厂内部常用若干工厂方法来实现

工厂方法:一个工厂方法 → 造一种产品的多实现。

抽象工厂:一个工厂接口里含多种产品的工厂方法 → 造一整套配套产品。
在这里插入图片描述

9. 抽象工厂模式很好的发挥了哪些原则?

开闭原则、依赖倒置原则。

依赖倒置原则(DIP)要求的是:高层业务代码只依赖抽象(AbstractFactory、AbstractBall、AbstractShirt),不直接依赖具体类;

10. 抽象工厂模式的缺点是什么?

缺点是抽象工厂模式太重了,抽象工厂的“痛点”主要在新增“产品类型”(即给 IFactory 增加新的 createX())会牵一发动全身,会影响到所有的具体工厂类,必须修改所有具体工厂去实现它,否则编译不过。使用抽象工厂模式,替换具体工厂时只需更改一行代码,但要新增抽象方法则需要修改所有的具体工厂类。

但这不是模式“设计失误”,而是它的适用前提:类型稳定、族可变。

11. 抽象工厂模式适用于和不适用于哪些情况?

适用于增加同类工厂这样的横向扩展需求,不适合新增功能这样的纵向扩展。

12. 什么时候可以使用单例模式?

某个对象全局只需要一个实例时即可。

13. 单例模式的优点是什么?

• 它能够避免对象重复创建,节约空间并提升效率
• 避免由于操作不同实例导致的逻辑错误

14. 单例模式有哪两种实现方式?请分别简单解释。

饿汉式和懒汉式。饿汉式指变量在声明时便初始化。懒汉式指先声明一个空变量,需要用时才初始化。

15. 饿汉式的弊端是什么?

即使这个单例不需要使用,它也会在类加载之后立即创建出来,占用一块内存,并增加类初始化时间。

16. 建造者模式用于什么时候?

创建过程稳定,但配置多变的对象。

17. 建造者模式是什么意思?

将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

建造者(Builder)模式就是:把“复杂对象的构建过程”从“对象本身”里独立出来,用一步步配置 → 最后 build() 产出的方式创建对象。这样同样的一套构建步骤也能产出不同表现/配置的对象;调用端写法像“链式命名参数”,清晰、可校验。

18. 现在建造者模式主要用来做什么?

通过链式调用生成不同的配置。

可选参数很多:避免“伸缩构造函数地狱”,用流式 API 清晰表达
HttpRequest::Builder{}.method(“GET”).url(u).header(k,v).timeout(2000).build();

#include <string>
#include <vector>
#include <utility>
#include <stdexcept>

class HttpRequest {
public:
    class Builder {
    public:
        Builder& method(std::string m) { method_ = std::move(m); return *this; }
        Builder& url(std::string u)    { url_    = std::move(u); return *this; }
        Builder& timeout_ms(int t)     { timeout_ms_ = t;        return *this; }
        Builder& follow_redirects(bool f) { follow_redirects_ = f; return *this; }
        Builder& header(std::string k, std::string v) {
            headers_.emplace_back(std::move(k), std::move(v)); return *this;
        }
        Builder& body(std::string b)   { body_ = std::move(b);   return *this; }

        HttpRequest build() {
            // 集中校验 + 填默认值
            if (method_.empty() || url_.empty())
                throw std::logic_error("method and url are required");
            if (method_ == "GET" && !body_.empty())
                throw std::logic_error("GET must not have a body");
            if (timeout_ms_ <= 0) timeout_ms_ = 3000;
            return HttpRequest(method_, url_, headers_, body_, timeout_ms_, follow_redirects_);
        }

        // 防止共享中间状态导致错用
        Builder(const Builder&) = delete;
        Builder& operator=(const Builder&) = delete;

    private:
        std::string method_, url_, body_;
        std::vector<std::pair<std::string, std::string>> headers_;
        int  timeout_ms_ = 0;
        bool follow_redirects_ = false;
    };

private:
    HttpRequest(std::string m, std::string u,
                std::vector<std::pair<std::string,std::string>> h,
                std::string b, int t, bool f)
      : method_(std::move(m)), url_(std::move(u)),
        headers_(std::move(h)), body_(std::move(b)),
        timeout_ms_(t), follow_redirects_(f) {}

    std::string method_, url_, body_;
    std::vector<std::pair<std::string,std::string>> headers_;
    int  timeout_ms_;
    bool follow_redirects_;
};

// 调用处:链式“命名参数”,一眼能看懂含义
auto req1 = HttpRequest::Builder{}
              .method("GET").url("/api/ping").timeout_ms(2000).build();

auto req2 = HttpRequest::Builder{}
              .method("POST").url("/login")
              .header("Accept","application/json")
              .body(R"({"u":"alice","p":"secret"})")
              .follow_redirects(true)
              .timeout_ms(5000)
              .build();

伸缩构造函数地狱

在这里插入图片描述

19. 使用建造者模式的好处是什么?

不用担心忘了指定某个配置,保证了构建过程是稳定的。

20. 原型模式是什么?

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

之后我会持续更新,如果喜欢我的文章,请记得一键三连哦,点赞关注收藏,你的每一个赞每一份关注每一次收藏都将是我前进路上的无限动力 !!!↖(▔▽▔)↗感谢支持!


网站公告

今日签到

点亮在社区的每一天
去签到