C语言-设计模式

设计模式的书相信很多人都看过。对于设计模式这样一种方法,相信不同的人有不同的理解。

C语言和设计模式(开篇)

关于软件设计方面的书很多,比如《 重构》,比如《 设计模式》。至于软件开发方式,那就更多了,什么极限编程、精益方法、敏捷方法。随着时间的推移,很多的方法又会被重新提出来。

其实,就我个人看来,不管什么方法都离不开人。一个人写不出二叉树,你怎么让他写?敏捷吗?你写一行,我写一行。还是迭代?写三行,删掉两行,再写三行。项目的成功是偶然的,但是项目的失败却有很多原因,管理混乱、需求混乱、设计低劣、代码质量差、测试不到位等等。就软件企业而言,没有比优秀的文化和出色的企业人才更重要的了。

从软件设计层面来说,一般来说主要包括三个方面:

(1)软件的设计受众,是小孩子、老人、女性,还是专业人士等等;
(2)软件的基本设计原则,以人为本、模块分离、层次清晰、简约至上、适用为先、抽象基本业务等等;
(3)软件编写模式,比如装饰模式、责任链、单件模式等等。

从某种意义上说,设计思想构成了软件的主题。软件原则是我们在开发中的必须遵循的准绳。软件编写模式是开发过程中的重要经验总结。灵活运用设计模式,一方面利于我们编写高质量的代码,另一方面也方便我们对代码进行维护。毕竟对于广大的软件开发者来说,软件的维护时间要比软件编写的时间要多得多。编写过程中,难免要有新的需求,要和别的模块打交道,要对已有的代码进行复用,那么这时候设计模式就派上了用场。我们讨论的主题其实就是设计模式。

讲到设计模式,人们首先想到的语言就是c#或者是java,最不济也是c++,一般来说没有人会考虑到c语言。其实,我认为设计模式就是一种基本思想,过度美化或者神化其实没有必要。其实阅读过linux kernel的朋友都知道,linux虽然自身支持很多的文件系统,但是linux自身很好地把这些系统的基本操作都抽象出来了,成为了基本的虚拟文件系统。

举个例子来说,现在让你写一个音乐播放器,但是要支持的文件格式很多,什么ogg,wav,mp3啊,统统要支持。这时候,你会怎么编写呢?如果用C++语言,你可能会这么写。

class music_file
{
   
    HANDLE hFile;
 
public:
    void music_file() {
   }
    virtual ~music_file() {
   }
    virtual void read_file() {
   }
    virtual void play() {
   }
    virtual void stop() {
   }
    virtual void back() {
   }
    virtual void front() {
   }
    virtual void up() {
   }
    virtual void down() {
   }    
};

其实,你想想看,如果用C语言能够完成相同的抽象操作,那不是效果一样的吗?

typedef struct _music_file
{
   
    HANDLE hFile;
    void (*read_file)(struct _music_file* pMusicFile);
    void (*play)(struct _music_file* pMusicFile);
    void (*stop)(struct _music_file* pMusicFile);
    void (*back)(struct _music_file* pMusicFile);
    void (*front)(struct _music_file* pMusicFile);
    void (*down)(struct _music_file* pMusicFile);
    void (*up)(struct _music_file* pMusicFile);           
}music_file;

当然,上面的例子比较简单,但是也能说明一些问题。写这篇文章的目的一是希望和朋友们共同学习模式的相关内容,另一方面也希望朋友们能够活学活用,既不要迷信权威,也不要妄自菲薄。只要付出努力,付出汗水,肯定会有收获的。有些大环境你改变不了,那就从改变自己开始。万丈高楼平地起,一步一个脚印才能真真实实学到东西。如果盲目崇拜,言必google、微软、apple,那么除了带来几个唾沫星,还能有什么受用呢?无非白费了口舌而已。

C语言和设计模式(之单件模式)

有过面试经验的朋友,或者对设计模式有点熟悉的朋友,都会对单件模式不陌生。对很多面试官而言,单件模式更是他们面试的保留项目。其实,我倒认为,单件模式算不上什么设计模式。最多也就是个技巧。
单件模式要是用C++写,一般这么写。

#include <string.h>
#include <assert.h>
 
class object
{
   
public:
    static class object* pObject;
	
    static object* create_new_object()
    {
   
        if(NULL != pObject)
			return pObject;
 
		pObject = new object();
		assert(NULL != pObject);
		return pObject;
    }
	
private:
    object() {
   }
    ~object() {
   }
};
 
class object* object::pObject = NULL;

单件模式的技巧就在于类的构造函数是一个私有的函数。但是类的构造函数又是必须创建的?怎么办呢?那就只有动用static函数了。我们看到static里面调用了构造函数,就是这么简单。

int main(int argc, char* argv[])
{
   
	object* pGlobal = object::create_new_object();
	return 1;
}

上面说了C++语言的编写方法,那C语言怎么写?其实也简单。大家也可以试一试。

typedef struct _DATA
{
   
    void* pData;
}DATA;
 
void* get_data()
{
   
    static DATA* pData = NULL;
    
    if(NULL != pData)
        return pData;
 
    pData = (DATA*)malloc(sizeof(DATA));
    assert(NULL != pData);
    return (void*)pData;
}

C语言和设计模式(之原型模式)

原型模式本质上说就是对当前数据进行复制。就像变戏法一样,一个鸽子变成了两个鸽子,两个鸽子变成了三个鸽子,就这么一直变下去。在变的过程中,我们不需要考虑具体的数据类型。为什么呢?因为不同的数据有自己的复制类型,而且每个复制函数都是虚函数。
用C++怎么编写呢,那就是先写一个基类,再编写一个子类。就是这么简单。

class data
{
   
public:
    data () {
   }
    virtual ~data() {
   }
    virtual class data* copy() = 0;
};
 
class data_A : public data
{
   
public:
    data_A() {
   }
    ~data_A() {
   }
    class data* copy()
    {
   
        return new data_A();
    }
};
 
class data_B : public data
{
   
public:
    data_B() {
   }
    ~data_B() {
   }
    class data* copy()
    {
   
        return new data_B();
    } 
};

那怎么使用呢?其实只要一个通用的调用接口就可以了。

class data* clone(class data* pData)
{
   
    return pData->copy();
}

就这么简单的一个技巧,对C来说,当然也不是什么难事。

typedef struct _DATA
{
   
    struct _DATA* (*copy) (struct _DATA* pData);
}DATA;

假设也有这么一个类型data_A,

DATA data_A = {
   data_copy_A};

既然上面用到了这个函数,所以我们也要定义啊。

struct _DATA* data_copy_A(struct _DATA* pData)
{
   
    DATA* pResult = (DATA*)malloc(sizeof(DATA));
    assert(NULL != pResult);
    memmove(pResult, pData, sizeof(DATA));
    return pResult;
}; 

使用上呢,当然也不含糊。

struct _DATA* clone(struct _DATA* pData)
{
   
    return pData->copy(pData);
};

C语言和设计模式(之组合模式)

组合模式听说去很玄乎,其实也并不复杂。为什么?大家可以先想一下数据结构里面的二叉树是怎么回事。为什么就是这么一个简单的二叉树节点既可能是叶节点,也可能是父节点?

typedef struct _NODE
{
   
    void* pData;
    struct _NODE* left;
    struct _NODE* right;
}NODE;

那什么时候是叶子节点,其实就是left、right为NULL的时候。那么如果它们不是NULL呢,那么很明显此时它们已经是父节点了。那么,我们的这个组合模式是怎么一个情况呢?

typedef struct _Object
{
   
    struct _Object** ppObject;
    int number;
    void (*operate)(struct _Object* pObject);
 
}Object;

就是这么一个简单的数据结构,是怎么实现子节点和父节点的差别呢。比如说,现在我们需要对一个父节点的operate进行操作,此时的operate函数应该怎么操作呢?

void operate_of_parent(struct _Object* pObject)
{
   
    int index;
    assert(NULL != pObject);
    assert(NULL != pObject->ppObject && 0 != pObject->number);
 
    for(index = 0; index < pObject->number; index ++)
    {
   
        pObject->ppObject[index]->operate(pObject->ppObject[index]);
    }
}   

当然,有了parent的operate,也有child的operate。至于是什么操作,那就看自己是怎么操作的了。

void operate_of_child(struct _Object* pObject)
{
   
    assert(NULL != pObject);
    printf("child node!\n");
}

父节点也好,子节点也罢,一切的一切都是最后的应用。其实,用户的调用也非常简单,就这么一个简单的函数。

void process(struct Object* pObject)
{
   
    assert(NULL != pObject);
    pObject->operate(pObject);
}

C语言和设计模式(之模板模式)

模板对于学习C++的同学,其实并不陌生。函数有模板函数,类也有模板类。那么这个模板模式是个什么情况?我们可以思考一下,模板的本质是什么。比如说,现在我们需要编写一个简单的比较模板函数。

template <typename type>
int compare (type a, type b)
{
   
    return a > b ? 1 : 0;
}    

模板函数提示我们,只要比较的逻辑是确定的,那么不管是什么数据类型,都会得到一个相应的结果。固然,这个比较的流程比较简单,即使没有采用模板函数也没有关系。但是,要是需要拆分的步骤很多,那么又该怎么办呢?如果相通了这个问题,那么也就明白了什么是template模式。
比方说,现在我们需要设计一个流程。这个流程有很多小的步骤完成。然而,其中每一个步骤的方法是多种多样的,我们可以很多选择。但是,所有步骤构成的逻辑是唯一的,那么我们该怎么办呢?其实也简单。那就是在基类中除了流程函数外,其他的步骤函数全部设置为virtual函数即可。

class basic
{
   
public:
    void basic() {
   }
    virtual ~basic() {
   }
    virtual void step1() {
   }
    virtual void step2() {
   }
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

从入门到捕蛇者说

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值