0x00.设计模式-序言

版权声明:本文版权归属于gdpbb.cn网站所有者,如需转发请注明出处http://gdpbb.cn

一、为什么想写这份文档

  现在的IT学习者是幸福的,大家通过网络能够非常便捷的获取各种学习资料,观摩大神的开源项目。但在十几年前,我上大学的时代,家里上网用的还是速度只有56K的猫,开源界的扛把子项目Linux虽然已经诞生,但国内还是以闭源软件为主,想要通过网络快捷的获取学习资料和阅读生产级的代码还是有一定困难的。

  C语言基本是计算机专业的必修课(我们学校当时貌似是全校必修课-_-“),当时在学函数指针时,示例基本是这样的:

int max(int val1, int val2) {
    if(val1>val2) {
        return val1;
    } else {
        return val2;
    }
}

int main() {
    int (*f)(int, int) = &max;
    int max_value = f(10, 20);
    printf("%d is largest", max_value);
}

  看到这样的代码,当时的内心的想法是,这有什么用?直接max(10, 20)不香吗?不过既然老师说了,这个不考,那就这样吧。毕业后的第一份工作是C开发,于是我看到了类似下面的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//#define DEBUG

// 定义缓冲区大小
#define MAX_LENGTH 1024

// 狗的数据结构
typedef struct DOG {
    char category[6];     // 数据类型
    char breed[6];        // 狗的品种
} DOG;

// 鸟的数据结构
typedef struct BIRD {
    char category[6];     // 数据类型
    char origin[10];      // 飞行起点
    char destination[10]; // 飞行终点
} BIRD;

// 业务处理结构(联合)
typedef union BODY {
    DOG dog;               // 狗
    BIRD bird;             // 鸟
} BODY;

// 数据分类处理用结构
typedef struct DATA {
    char category[6];             // 数据类型
    void (*process)(BODY *body);  // 处理函数
} DATA;

// 针对狗的处理
void process_dog(BODY *body) {

#ifdef DEBUG
    printf("body: %s\n", (char *)body);
#endif

    // c语言原生库中没有字符串,只有字符数组,
    // 在进行显示是以遇到的第一个0x00(\0)作为字符串的结束
    // 输入数据中没有字符串结束符(\0),这里通过中间变量增加字符串结束符(\0)
    // 初始化显示用变量
    int breed_size = sizeof(body->dog.breed)/sizeof(body->dog.breed[0]);
    char breed[breed_size+1];
    memset(&breed, 0x00, breed_size+1);

    // 复制输入数据中对应项目的值,并打印
    memcpy(&breed, body->dog.breed, breed_size);
    printf("It's a %s\n", breed);
}

// 针对鸟的处理
void process_bird(BODY *body) {

#ifdef DEBUG
    printf("body: %s\n", (char *)body);
#endif

    // c语言原生库中没有字符串,只有字符数组,
    // 在进行显示是以遇到的第一个0x00(\0)作为字符串的结束
    // 输入数据中没有字符串结束符(\0),这里通过中间变量增加字符串结束符(\0)
    // 初始化显示用变量
    int origin_size = sizeof(body->bird.origin)/sizeof(body->bird.origin[0]);
    int destination_size = sizeof(body->bird.destination)/sizeof(body->bird.destination[0]);
    char origin[origin_size + 1], destination[destination_size + 1];
    memset(&origin, 0x00, origin_size + 1);
    memset(&destination, 0x00, destination_size + 1);

    // 复制输入数据中对应项目的值,并打印
    memcpy(&origin, body->bird.origin, origin_size);
    memcpy(&destination, body->bird.destination, destination_size);
    printf("I'm a bird. I flew from %s to %s\n", origin, destination);
}

// 初始化处理映射表
DATA process[] = {
    {"dog   ", process_dog},    // 针对狗的处理
    {"bird  ", process_bird}    // 针对鸟的处理
};

int main() {

    // 获取处理映射表元素数
    int process_size = sizeof(process) / sizeof(process[0]);

    // 定义文件指针
    FILE *fp = NULL;
    // 定义缓冲区大小
    char buff[MAX_LENGTH];
    // 定义数据处理结构
    BODY *body = (BODY *)malloc(sizeof(BODY));

    // 打开文件
    fp = fopen("./data.dat", "r");
    // 逐行读取文件内容,直到文件结束
    while(fgets(buff, MAX_LENGTH, fp) != NULL) {
        // 清空
        memset(body, 0x00, sizeof(BODY));
		// 将数据复制到处理结构中,便于后续取值
        memcpy(body, buff, sizeof(BODY));
		
        for(int i=0; i<process_size; i++) {
			// 将读取数据的数据类型与处理映射表的类型进行比较,一致时执行对应的处理
            if(memcmp(body->dog.category, process[i].category, sizeof(process[i].category)) == 0) {
                process[i].process(body);
                break;
            }
        }
    }
	
	// 释放内存
	free(body);
    // 关闭文件
	if(fp!=NULL) {
		fclose(fp);
	}

    // 正常返回
    return 0;
}

  原来函数指针是这么用的!当然你也可以把函数指针作为函数的参数,来构造回调地狱!

  文档的后续内容以Java实现,所以C的内容就此打住了(主要还是C已经忘的差不多了,花了半个多小时才在度娘的帮助下测试通过-_-“)。

  对于设计模式,不论是出版的书籍还是网络教程都相当丰富,而且很多内容都非常好。那么我为什么还要写这份文档呢?首先、对于整理归纳,我一直是比较懒散的状态,写这一系列文章主要是对自己的一种督促。其次、目前设计设计模式的教程/文档大体分为两种形式,一种是专门讲述设计模式的,这类文档对各种模式的概念做了非常清晰的描述,也提供了非常好的示例代码。但是,为了更好的描述模式本身,示例代码也经过了必要的抽象,与业务场景脱离。另外一种是结合一些源代码解析(如Spring、Android)对设计模式进行介绍,对于源码中没有涉及的设计模式不会提及,设计模式的知识相对零散。希望能整合两种方式的优势,通过实际业务环境中经常能接触的一些通用模块的设计实现,来了解设计模式。

二、文章整体结构及注意点

  对于某一模式,文章主要分为三部分:

  1. 模式介绍
  2. 类图描述
  3. 示例代码

  本系列文章的核心内容不是对设计模式的理论论述,因此对于概念性的内容原则上借(chao)鉴(xi)《设计模式:可复用面向对象软件的基础》 一书及相关免费网络资源,如涉及版权问题,请及时与我联络。

  示例代码基于jdk8编译执行,如有类库依赖、环境设置等特殊需求,会进行单独说明。

  生产环境使用的代码需要保证足够的健壮性、性能等指标,示例代码并不确保具备生产级别的特性,因此,请勿将示例代码直接用于生产环境。

三、设计模式介绍

  软件设计模式(以下简称”设计模式”)是在软件开发过程中,对可复用的设计经验的总结。传统意义上的设计模式指1995 年,由艾瑞克·伽马(Erich Gamma)、理查德·海尔姆(Richard Helm)、拉尔夫·约翰森(Ralph Johnson)、约翰·威利斯迪斯(John Vlissides) 4 位作者合作出版的《设计模式:可复用面向对象软件的基础》(Design Patterns: Elements of Reusable Object-Oriented Software)一书中收录的23 个设计模式 。本系列文章仅包含《设计模式》一书中收录的23种设计模式。

  下面通过一张思维导图对23个设计模式进行简单的了解。

设计模式概览

  每个设计模式并非孤立存在。有些模式间存在相似的设计或可以应用在近似的场景;实际设计中经常会组合使用多种设计模式。下图展示了23种设计模式间的模式关系,有助于在实际应用中对设计模式进行灵活的组合转换。 设计模式间的关系

四、碎碎念念

  文章选用java语言作为实例的实现语言,但设计模式本身并不特定于某一种编程语言,因此文中并不会特别讨论某一模式在特定语言下的多种实现方式。例如、单例模式的java实现方式。

  我对设计模式的学习主要来源于《设计模式:可复用面向对象软件的基础》,文章写作过程中查询资料时看到了《研磨设计模式》一书的影印版,大致翻阅了内容觉得也很不错,推荐大家阅读,据说这本书仅影印了一版,目前已没有新的纸质版,大家可购买二手书或自行需要电子版。

  对设计模式的介绍顺序不会按照《设计模式》一书中的顺序,而主要根据与实例的相关性。

  文中出现的错漏希望能得到大家的及时指正,联系方式:18361846@qq.com

五、代码下载

代码下载

六、参考资料

1:《设计模式:可复用面向对象软件的基础》