C 观察者模式

在模版模式中,调用 int_sorter("/line-e.txt");

结果返回两行以下两行错误

/line-e.txt:No such file or directory
/line-e.txt:No such file or directory

这是因为程序中以下代码会计算文件大小

   long size = file_size(p);
    if (size == -1)
    {
        file_error(pBufCtx->pAppCtx);
        return false;
    }

由于返回的函数指针是NULL,所以size是-1.

因此 file_error函数会输出信息。reader函数也返回false,这样 do_with_buffer函数也会检测到错误,将错误信息显示出来。

static bool do_with_buffer(BufferContext * p)
{
    //3
    MybufferContext * pBufCtx= (MybufferContext *)p;
    MyFileAccessorContext readFileCtx = {{NULL,pBufCtx->pAppCtx->pFname,"rb",reader},pBufCtx};
    
    //4
    if (!access_file(&readFileCtx.base))
    {
        file_error(pBufCtx->pAppCtx);
        return false;
    }


这样就造成了错误信息被输出2次。怎么看都感觉后面的代码有些多余。将 FileAccessorContext 修改成可以注册观察者对象(接收错误信息的外部对象)


file_accessor.h


struct FileAccessorContext;

typedef struct FileErrorObserver{
    void(* const onError)(struct FileErrorObserver * pThis,struct FileAccessorContext * pFileCtx);
}FileErrorObserver;

extern FileErrorObserver default_file_error_observer;

typedef struct FileAccessorContext{
    FILE  * fp;
    const char * const pFname;
    const char * const pMode;
    bool (* const processor)(struct FileAccessorContext * pThis);
    FileErrorObserver * pFileErrorObserver;
}FileAccessorContext;

file_accessor.c

bool access_file(FileAccessorContext * pThis)
{
    assert(pThis);
    if (pThis->pFileErrorObserver == NULL)
    {
        pThis->pFileErrorObserver = &default_file_error_observer;//2
    }
    bool ret = pThis->processor(pThis);
    if (pThis->fp != NULL)
    {
        if (fclose(pThis->fp) != 0)
        {
            pThis->pFileErrorObserver->onError(pThis->pFileErrorObserver,pThis);//1
            ret = false;
        }
    }

    return ret;
}



FILE * get_file_pointer(FileAccessorContext * pThis)
{
    assert(pThis);
    if (pThis->fp == NULL)
    {
        pThis->fp = fopen(pThis->pFname, pThis->pMode);
        if (pThis->pFileErrorObserver != NULL)
        {
            pThis->pFileErrorObserver->onError(pThis->pFileErrorObserver,pThis);
        }
    }
    
    return pThis->fp;
}

//3
static void default_file_error_handler(FileErrorObserver * pThis,FileAccessorContext * pFileCtx)
{
    fprintf(stderr,"File access error'%s'(mode:%s):%s\n",pFileCtx->pFname,pFileCtx->pMode,strerror(errno));
}

FileErrorObserver default_file_error_observer={
    &default_file_error_handler

};


1. 发生错误时,调用观察者的onError处理。这里很重要的一点是不必知道onError中到底急性了怎样的处理。这样就可以将发送错误通知的处理与错误处解耦。

2.在access_file中,如果发现没有指定onError处理,则会指定 default_file_error_observer函数作为默认错误处理。

3.default_file_error_observer 函数非常简单,只是使用标准输出函数strerror将最后发生的错误输出。



二、将文件读入缓存和将缓存中的内容写入文件的处理也改造为共通函数

1.file_accessor.h

bool read_file(FileAccessorContext * pThis,BufferContext * pBufCtx);
bool write_file(FileAccessorContext * pThis,BufferContext * pBufCtx);
2.file_accessor.c
bool read_file(FileAccessorContext * pThis,BufferContext * pBufCtx)
{
    FILE * fp = get_file_pointer(pThis);
    if (fp == NULL)
    {
        return false;
    }
    
    if (pBufCtx->size != fread(pBufCtx->pBuf, 1, pBufCtx->size, fp))
    {
        pThis->pFileErrorObserver->onError(pThis->pFileErrorObserver,pThis);
        return false;
    }
    return true;
}

bool write_file(FileAccessorContext * pThis,BufferContext * pBufCtx)
{
    FILE * fp = get_file_pointer(pThis);
    if (fp == NULL)
    {
        return false;
    }
    
    if (pBufCtx->size !=  fwrite(pBufCtx->pBuf, 1, pBufCtx->size, fp))
    {
        pThis->pFileErrorObserver->onError(pThis->pFileErrorObserver,pThis);
        return false;
    }
    return true;
}

3.int_sorter.c


#include "int_sorter.h"
#include <stdbool.h>
#include <stdlib.h>
#include <stddef.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include "file_accessor.h"
//1
typedef struct{
    FileAccessorContext base;
    long size;
} SizeGetterContext;

typedef struct{
    FileAccessorContext base;
    MybufferContext * pBufCtx;
} MyFileAccessorContext;

static void size_reader(FileAccessorContext * p,FILE * fp);
static void file_error(FileErrorObserver * pThis,FileAccessorContext * pFileCtx);
static long file_size(FileAccessorContext * pThis);

static bool reader(FileAccessorContext * p);
static bool writer(FileAccessorContext * p);

static long file_current_pos(FileAccessorContext * pFileCtx);
static int set_file_pos(FileAccessorContext * pFileCtx,long offset,int whence);
int comparator(const void *a, const void *b)
{
    int i1 = * (const int *)a;
    int i2 = * (const int *)b;
    if (i1<i2) {
        return -1;
    }
    if (i1>i2) {
        return 1;
    }
    return 0;
}

static bool do_with_buffer(BufferContext * p)
{
    //3
    MybufferContext * pBufCtx= (MybufferContext *)p;
    MyFileAccessorContext readFileCtx = {{NULL,pBufCtx->pAppCtx->pFname,"rb",reader},pBufCtx};
    
    //4
    if (!access_file(&readFileCtx.base))
    {
        //file_error(pBufCtx->pAppCtx);
        return false;
    }
    //11
    qsort(p->pBuf, p->size/sizeof(int), sizeof(int), comparator);
    
    MyFileAccessorContext writeFileCtx = {{NULL,pBufCtx->pAppCtx->pFname,"wb",writer},pBufCtx};
    //    if (!access_file(&writeFileCtx.base))
    //    {
    //        file_error(pBufCtx->pAppCtx);
    //        return false;
    //    }
    //
    //    return true;
    return !access_file(&writeFileCtx.base);
}

static bool reader(FileAccessorContext * p)
{
    
    //5
    MyFileAccessorContext * pFileCtx = (MyFileAccessorContext *)p;
    MybufferContext * pBufCtx = pFileCtx->pBufCtx;
    //6
    long size = file_size(p);
    if (size == -1)
    {
        //file_error(pBufCtx->pAppCtx);
        return false;
    }
    
    //9
    if (!allocate_buffer(&pBufCtx->base, size))
    {
        pBufCtx->pAppCtx->errorCategory = ETT_CAT_MEMORY;
        return false;
    }
    
    return read_file(p, &pFileCtx->pBufCtx->base);
    //    FILE * fp = get_file_pointer(p);
    //    //10
    //    if (pBufCtx->base.size != fread(pBufCtx->base.pBuf, 1, pBufCtx->base.size, fp))
    //    {
    //        file_error(pBufCtx->pAppCtx);
    //        return false;
    //    }
    //    return true;
}

static bool writer(FileAccessorContext * p)
{
    //12
    MyFileAccessorContext * pFileCtx = (MyFileAccessorContext *)p;
    return write_file(p,&pFileCtx->pBufCtx->base);
    //    MybufferContext * pBufCtx = pFileCtx->pBufCtx;
    
    //    FILE * fp = get_file_pointer(p);
    //    if (fwrite(pBufCtx->base.pBuf,1,  pBufCtx->base.size, fp)!=pBufCtx->base.size)
    //    {
    //        file_error(pBufCtx->pAppCtx);
    //        return false;
    //    }
    //
    //    return true;
}

static long file_size(FileAccessorContext * pThis)
{
    //7
    long save = file_current_pos(pThis);
    if(save<0)
    {
        return -1;
    }
    if (set_file_pos(pThis,save,SEEK_END)!=0)
    {
        return -1;
    }
    
    long  size = file_current_pos(pThis);
    if (set_file_pos(pThis,save,SEEK_SET)!=0)
    {
        return -1;
    }
    
    
    return size;
}

static long file_current_pos(FileAccessorContext * pFileCtx)
{
    assert(pFileCtx);
    //8
    FILE * fp = get_file_pointer(pFileCtx);
    if (fp == NULL)
    {
        return -1;
    }
    return ftell(fp);
}

static int set_file_pos(FileAccessorContext * pFileCtx,long offset,int whence)
{
    assert(pFileCtx);
    //8
    FILE * fp = get_file_pointer(pFileCtx);
    if (fp == NULL)
    {
        return -1;
    }
    return fseek(fp, offset, whence);
}
static void size_reader(FileAccessorContext * p,FILE * fp)
{
    SizeGetterContext * pThis = (SizeGetterContext*)p;
    pThis->size = -1;
    if (fseek(fp, 0, SEEK_END)==0)
    {
        pThis->size = ftell(fp);
    }
}
//1
IntSorterError int_sorter(const char * pFname)
{
    Context ctx = {pFname,ERR_CAT_OK};
    
    MybufferContext bufCtx = {{NULL,0,do_with_buffer},&ctx};
    
    buffer(&bufCtx.base);
    
    return ctx.errorCategory;
    
}

static void file_error(FileErrorObserver * pThis,FileAccessorContext * pFileCtx)
{
    MyFileAccessorContext * pMyFileCtx = (MyFileAccessorContext *)pFileCtx;
    pMyFileCtx->pBufCtx->pAppCtx->errorCategory = ETT_CAT_FILE;
}


4.运行程序

File access error'/line-e.txt'(mode:rb):No such file or directory
Program ended with exit code: 0


三、动态添加观察者

上面的代码中,仅仅在编译时设置了一个观察者,但在实际开发中,需要动态增加多个观察者 或删除多个观察者,像上面的例子中静态指定观察者的功能就满足不了。所以需要进一步修改上面的例子中代码。

1.创建array_list文件

1.1 array_list.h


typedef struct ArrayList{
    const int  capaccity;
    void ** const pBuf;
    size_t  index;
    struct   ArrayList *(*const add)(struct ArrayList * pThis,void * pData);
    void * (* const remove)(struct ArrayList * pThis,void * pData);
    void * (* const get)(struct ArrayList * pThis,int  index);
    size_t (* const size)(struct ArrayList * pThis);
} ArrayList;

ArrayList * add_toarray_list(ArrayList * pThis,void * pData);
void * remove_from_array_list(ArrayList * pThis,void * pData);
void * get_from_array_list(ArrayList * pThis,int  index);

size_t array_list_size(ArrayList * pThis);

#define new_array_list(array)                       \
      {                                             \
        sizeof(array)/sizeof(void *),array,         \
        0,add_toarray_list, remove_from_array_list, \
        get_from_array_list,array_list_size         \
}

1.2 array_list.c

#include "array_list.h"
#include <assert.h>
#include <string.h>
ArrayList * add_toarray_list(ArrayList * pThis,void * pData)
{
    assert(pThis->capaccity>pThis->index);
    pThis->pBuf[pThis->index++] = pData;
    return pThis;
}
void * remove_from_array_list(ArrayList * pThis,void * pData)
{

    for ( int  i = 0; i<pThis->index; ++i)
    {
        if (pThis->pBuf[i] == pData)
        {
            memmove(pThis->pBuf+i, pThis->pBuf+i+1, (pThis->index -i-1) *
            sizeof(void *));
            --pThis->index;
            return pData;
        }
    }
    return NULL;
}
void * get_from_array_list(ArrayList * pThis,int  index)
{
    assert(0<=index && pThis->index>index);
    return pThis->pBuf[index];
}

size_t array_list_size(ArrayList * pThis)
{
    return pThis->index;
}


2. file_accessor.h


typedef struct FileErrorObserver{
    void(* const onError)(struct FileErrorObserver * pThis,struct FileAccessorContext * pFileCtx);
}FileErrorObserver;

extern FileErrorObserver default_file_error_observer;

typedef struct FileAccessorContext{
    FILE  * fp;
    const char * const pFname;
    const char * const pMode;
   
    bool (* const processor)(struct FileAccessorContext * pThis);
    ArrayList  observer_table;
//    FileErrorObserver * pFileErrorObserver;
}FileAccessorContext;


2. file_accessor.c


#include "file_accessor.h"
#include <assert.h>
#include <errno.h>
#include <string.h>
static void file_error(FileAccessorContext * pThis);
bool access_file(FileAccessorContext * pThis)
{
    assert(pThis);
//    if (pThis->pFileErrorObserver == NULL)
//    {
//        pThis->pFileErrorObserver = &default_file_error_observer;
//    }
    bool ret = pThis->processor(pThis);
    if (pThis->fp != NULL)
    {
        if (fclose(pThis->fp) != 0)
        {
            file_error(pThis);
            
//            pThis->pFileErrorObserver->onError(pThis->pFileErrorObserver,pThis);
            
            ret = false;
        }
    }

    return ret;
}

static void file_error(FileAccessorContext * pThis)
{
     ArrayList * pTbl = &pThis->observer_table;
    for ( int i  = 0; i<pTbl->index; ++i)
    {
        FileErrorObserver * pObserver = pTbl->get(pTbl,i);
        pObserver->onError(pObserver,pThis);
    }
}

FILE * get_file_pointer(FileAccessorContext * pThis)
{
    assert(pThis);
    if (pThis->fp == NULL)
    {
        pThis->fp = fopen(pThis->pFname, pThis->pMode);
        if (pThis->fp == NULL)
        {
              file_error(pThis);
//            pThis->pFileErrorObserver->onError(pThis->pFileErrorObserver,pThis);
        }
    }
    
    return pThis->fp;
}

void add_file_error_observer(FileAccessorContext * pThis,FileErrorObserver * pErrorObserver)
{
    ArrayList * pTable= &pThis->observer_table;
    pTable->add(pTable,pErrorObserver);
}

void remove_file_error_observer(FileAccessorContext * pThis,FileErrorObserver * pErrorObserver)
{
    ArrayList * pTable= &pThis->observer_table;
    pTable->remove(pTable,pErrorObserver);
}
bool read_file(FileAccessorContext * pThis,BufferContext * pBufCtx)
{
    FILE * fp = get_file_pointer(pThis);
    if (fp == NULL)
    {
        return false;
    }
    
    if (pBufCtx->size != fread(pBufCtx->pBuf, 1, pBufCtx->size, fp))
    {
        file_error(pThis);
//        pThis->pFileErrorObserver->onError(pThis->pFileErrorObserver,pThis);
        return false;
    }
    return true;
}

bool write_file(FileAccessorContext * pThis,BufferContext * pBufCtx)
{
    FILE * fp = get_file_pointer(pThis);
    if (fp == NULL)
    {
        return false;
    }
    
    if (pBufCtx->size !=  fwrite(pBufCtx->pBuf, 1, pBufCtx->size, fp))
    {
          file_error(pThis);
//        pThis->pFileErrorObserver->onError(pThis->pFileErrorObserver,pThis);
        return false;
    }
    return true;
}
static void default_file_error_handler(FileErrorObserver * pThis,FileAccessorContext * pFileCtx)
{
    fprintf(stderr,"File access error'%s'(mode:%s):%s\n",pFileCtx->pFname,pFileCtx->pMode,strerror(errno));
}

FileErrorObserver default_file_error_observer={
    &default_file_error_handler

};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值