五.抽象接口与依赖反转(C面向对象开发)

内容参考于《抽象接口技术和组件开发规范及其思想》

五.抽象接口与依赖反转

基于多态可以实现“与硬件无关”的应用程序。 在 C 编程中,多态的核心解决方法是充分利用“函数指针”,抽象接口就是只包含函数指针的类, 它们非常抽象,不包含任何具体的实现,仅定义了函数的调用规则。应用不在依赖具体的实现,根据接口去编写应用。实际上之前的综合示例最后一个示例已经完全体现

1. 示例:

时间示例
接口与应用程序(与底层无关)
itime.h

struct tm
{
    int tm_sec;   // 秒, 0 ~59
    int tm_min;   // 分, 0 ~ 59
    int tm_hour;  // 小时, 0 ~ 23
    int tm_mday;  // 日期, 1 ~ 31
    int tm_mon;   // 月份, 0 ~ 11
    int tm_year;  // 年
    int tm_wday;  // 星期
    int tm_yday;  // 天数
    int tm_isdst; // 夏令时(一般不使用,值为 0 或-1)
};

struct itime
{
    int (*pfn_time_get)(struct itime *p_this, struct tm *p_tm);
    int (*pfn_time_set)(struct itime *p_this, struct tm *p_tm);
};

static inline int itime_time_get(struct itime *p_this, struct tm *p_tm)
{
    return p_this->pfn_time_get(p_this, p_tm);
}

static inline int itime_time_set(struct itime *p_this, struct tm *p_tm)
{
    return p_this->pfn_time_set(p_this, p_tm);
}

app.c

void app_elec_watch(struct itime *p_time)
{
    struct tm now_tm;

    if (用户修改了当前时间)
    {
        struct tm set_tm = ...              // 用户设置的时间
            itim_time_set(p_time, &set_tm); // 设置时间
    }

    itime_time_get(p_watch->p_rtc, &now_tm); // 获取当前时间

    printf("Now time is : %04d-%02d-%02d %02d:%02d:%02d \r\n",
           now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday,
           now_tm.tm_hour, now_tm.tm_min, now_tm.tm_sec);

    // ...其它处理, 如在 LCD 上显示时间等
}

某一型号为 PCF85063 的 RTC 芯片
底层实现
pcf85063.h

#include "itime.h"

struct pcf85063
{
    struct itime itime;
};

#endif /* __PCF85063_H */

pcf85063.c

#include "pcf85063.h"

static int __pcf85063_time_get(struct itime *p_this, struct tm *p_tm)
{
    // 首先,从 PCF85063 中获取出年、月、日、时、分、秒等信息

    if (获取成功)
    {
        p_tm->tm_year =     // 从 PCF85063 中获取出的 年 信息
            p_tm->tm_mon =  // 从 PCF85063 中获取出的 月 信息
            p_tm->tm_mday = // 从 PCF85063 中获取出的 日 信息
            p_tm->tm_hour = // 从 PCF85063 中获取出的 时 信息
            p_tm->tm_min =  // 从 PCF85063 中获取出的 分 信息
            p_tm->tm_sec =  // 从 PCF85063 中获取出的 秒 信息

            return 0; // 获取时间成功
    }
    return -1; // 获取时间失败
}

static int __pcf85063_time_set(struct itime *p_this, struct tm *p_tm)
{
    // 首先,将 p_tm 中的年、月、日、时、分、秒等信息设置到 PCF85063 中

    if (设置成功)
    {
        return 0;
    }
    return -1;
}

struct itime *pcf85063_init(struct pcf85063 *p_dev);
{
    //... PCF85063 硬件初始化
    
    p_dev->itime.pfn_time_set = __pcf85063_time_set;
    p_dev->itime.pfn_time_get = __pcf85063_time_get;
    return &p_dev->itime;
}

main.c

#include "pcf85063.h"
static struct pcf85063 pcf85063_dev;
int main()
{
    struct itime *p_itime = pcf85063_init(&pcf85063_dev);
    app_elec_watch(p_itime); // 启动“电子表”应用程序
    while (1);
}
2. 分析

示例中,应用程序、 接口与实现类之间的关系
在这里插入图片描述
传统设计应用程序和实现的关系
在这里插入图片描述

在面向过程的实现中,main->app->pcf85063。如果pcf85063有变化和改动的话,将会影响到依赖他的上层。而这里,app依赖的是接口,只要接口不变,app是完全不会被底层影响到的。main提供什么给app,app就使用什么。如果有更多的底层,那么关系如下
在这里插入图片描述
所有具体的实现类都要基于接口类中定义的抽象方法来实现(功能、 函数原型等由抽象方法规定),因此,通常情况下,接口类会被很多具体的实现类所依赖,若修改了接口类,则改动会影响所有的实现类,影响范围很大。基于此,接口类应该是非常稳定的, 不应该轻易变化。 实际上, 这对接口类的定义提出了很高的要求, 接口类不能随便定义,应考虑到可能的变化,合理、正确的定义各个抽象方法。
这里APP不再依赖底层,高层模块不再依赖于低层模块,而是APP依赖的是上层main传入的参数。依赖关系颠倒了。这就是依赖反转

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值