基于C++的《Head First设计模式》笔记——外观模式

目录

一.专栏简介

二.Jsoncpp库为例子

三.外观模式

四.最少知识原则

五.如何不赢得朋友和影响对象

六.遵守最少知识原则的类

七.外观模式和最少知识原则

八.总结


一.专栏简介

本专栏是我学习《head first》设计模式的笔记。这本书中是用Java语言为基础的,我将用C++语言重写一遍,并且详细讲述其中的设计模式,涉及是什么,为什么,怎么做,自己的心得等等。希望阅读者在读完我的这个专题后,也能在开发中灵活且正确的使用,或者在面对面试官时,能够自信地说自己熟悉常用设计模式。

本章将开始外观模式的学习。

二.Jsoncpp库为例子

Jsoncpp 是一个纯 C++ 编写的开源库,无任何外部依赖,核心作用是:

  1. 解析(反序列化):将 JSON 格式的字符串 / 文件,解析成 C++ 中可直接操作的对象;
  2. 生成(序列化):将 C++ 中的 Jsoncpp 对象,拼接 / 转换为标准的 JSON 格式字符串,也可直接写入文件;
  3. 支持完整的 JSON 语法:键值对、数组、嵌套结构、空值 (null)、各种基础数据类型 (int/double/bool/string)。

它的Json命名空间里的StreamWriterBuilder类CharReaderBuilder类 是一个简单工厂,用来创建Json命名空间里的StreamWriterCharReader ,它们就可以分别调用writeparse方法来对Json::Value序列化为string字符串和string反序列化为Json::Value对象。

我们不希望我们在使用的时候每次都用这个简单工厂创建一个对象,然后才序列化或者反序列化,这造成代码的重复,麻烦,难以维护。

那么我们就封装了以下这个工具类

cpp 复制代码
class json_util
{
public:
    static bool serialize(const Json::Value& root, std::string& str)
    {
        Json::StreamWriterBuilder swb;
        std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
        std::stringstream ss;
        int ret = sw->write(root, &ss);
        if(ret != 0)
        {
            ELOG("Json序列化失败");
            return false;
        } 
        str = ss.str();
        return true;
    }
    static bool unserialize(const std::string& str, Json::Value& root)
    {
        Json::CharReaderBuilder crb;
        std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
        std::string err;
        if(!cr->parse(str.c_str(), str.c_str() + str.size(), &root, &err))
        {
            ELOG("Json反序列化失败,原因:%s" , err.c_str());
            return false;
        }
        return true;
    }
};

两个工具函数,用于反序列化和反序列化。正如函数参数,我们只关心一个Json::Value可以序列化为一个string或者一个string可以反序列化为一个Json::Value对象,其它的东西我们不关心,所以我们把里面的细节都封装了起来。

三.外观模式

OK!上面的封装就是外观模式的应用。

外观模式为子系统中的一组接口提供了一个统一的接口。外观定义了一个更高级别的接口,使得子系统容易被使用。

外观提供一个简化的接口,但依然暴露系统的全部功能给需要的客户。

外观不仅简化接口,它还把客户从组件的子系统解耦。

外观和适配器的区别:外观和适配器可以包装多个类,但外观的意图是简化,而适配器的意图是转化接口为不同的接口。

下面我们由外观模式引出一个原则:最少知识原则

四.最少知识原则

最少知识原则引导我们减少对象之间的交互,减少到仅发生在一些亲密"朋友"之间。这个原则通常表述如下:

但这到底是什么意思?意思是,当我们设计一个系统时,对于任何对象,都要注意它所交互的类的数量,以及它和这些类如何交互。

这个原则防止我们创建有大量的类在一起的设计,免得系统一部分的变化会连锁影响到其他部分。当你在许多类之间造成许多依赖时,我们的系统就是一个易碎的系统,需要花费许多成本维护,而且复杂得让别人难以理解。

五.如何不赢得朋友和影响对象

该原则提供了一些指南:对于任何对象,从该对象的任何方法,只调用属于以下范围的方法:

  • 对象自身。
  • 作为参数传给方法的对象。
  • 该方法创建或实例化的任何对象。
  • 对象的任何组件。(has-a)

这些指南告诉我们,不要调用从其他方法返回的对象的方法!

调用从另一个调用中返回的对象的方法,有什么坏处呢?好吧,如果我们这样做,相当于向另一个对象的子部分发出请求(而增加我们直接认识的对象数目)。在这种情况下,原则告诉我们,要求该对象为我们做出请求,这样我们就不必知道它的组件对象(保持我们的朋友圈尽可能小)。例如:

不遵守原则:

cpp 复制代码
float getTemp()
{
    Thermometer thermometer = station.getThermometer();
    return thermometer.getTemperature();
}

这里,我们从气象站取得温度计(thermometer)对象,然后自行调用getTemperature()方法。

遵守原则:

cpp 复制代码
float getTemp()
{
    return station.getTemperature();
}

当我们应用该原则时,我们给Station(气象站)类添加一个方法 ,向温度计发出请求。这减少了所依赖的类的数目

六.遵守最少知识原则的类

这是一个Car(汽车)类,展示了我们在调用方法同时依然遵守最少知识原则的所有方式:

cpp 复制代码
class Car
{
public:
    Car()
    {
        
    }
    
    // 我们可以调用作为参数传递的对象的方法
    void start(Key key)
    {
        // 这里我们创建了一个新的对象:调用它的方法是合法的
        Doors* doors = new Doors();
        // 我们可以调用作为参数传递的对象的方法
        bool authorized = key.turns();
        if(authorized)
        {
            // 我们可以调用对象组件的方法
            engine.start();
            // 我们可以调用对象内的局部方法
            updateDashboardDisplay();
            // 我们可以调用我们创建或实例化的对象的方法
            doors.lock();
        }
    }
    
    void updateDashboardDisplay()
    {
        // 更新显示
    }
private:
    // 这是这个类的一个组件,我们可以调用它的方法
    Engine engine;
};

七.外观模式和最少知识原则

八.总结

原则不叫法律是有原因的,我们不是必须遵循,而是看实际情况,具体问题具体分析。

虽然最少知识原则减少了对象之间的依赖,研究显示这会减少软件维护成本,应用这个原则也会导致编写更多的"包装者"类来处理对其他组件的方法调用。这会造成复杂度和开发时间增加,运行时性能下降。

下面是一些要点,包含本专栏上一篇博客适配器的:

  • 当你需要使用一个已有的类,而其接口不符合你的需要,就用适配器。
  • 当你需要简化并统一一个大接口,或者一个复杂的接口集,就用外观。
  • 适配器改变接口以符合客户的期望。
  • 外观将客户从一个复杂子系统解耦。
  • 实现一个外观,需要把外观和子系统结合,使用委托来执行外观的工作。
  • 适配器模式有两种形式:对象适配器和类适配器。类适配器需要用到多重继承。
  • 我们可以为一个子系统实现多于一个外观。
  • 适配器包装一个对象以改变其接口,装饰者包装一个对象以添加新的行为和责任,而外观"包装"一群对象以简化其接口。
相关推荐
a***59263 小时前
C++跨平台开发:挑战与解决方案
开发语言·c++
hetao17338373 小时前
2026-01-12~01-13 hetao1733837 的刷题笔记
c++·笔记·算法
CoderCodingNo3 小时前
【GESP】C++六级考试大纲知识点梳理, (5) 动态规划与背包问题
开发语言·c++·动态规划
情缘晓梦.3 小时前
C++ 类和对象(完)
开发语言·jvm·c++
代码游侠3 小时前
学习笔笔记——ARM 嵌入式系统与内核架构
arm开发·笔记·嵌入式硬件·学习·架构
wdfk_prog3 小时前
[Linux]学习笔记系列 -- [driver][base]container
linux·笔记·学习
qq_433554543 小时前
C++ 图论算法:强连通分量
c++·算法·图论
June bug3 小时前
【实习笔记】配置Hosts
笔记
航Hang*3 小时前
Photoshop 图形与图像处理技术——第9章:实践训练3——图像修饰和色彩色调的调整
图像处理·笔记·学习·ui·photoshop·期末