工厂模式是一个创建型的设计模式。它允许创造对象时不指定要创建的对象的具体类型。本质上工厂方法就是构造函数的一般化。首先回归一下,C++中构造函数的几个限制:
- 没有返回值。意味着在对象初始化期间不能通过返回NULL指针发出错误信号。(但可以借助在构造函数内抛出异常的方式发出错误信号)
- 命名限制。限制了灵活性,例如两个构造函数不能同时具有一个整型参数。
- 静态绑定创建。在构造对象时,必须指定编译时能够确定的特定类名。例如,Foo *f=new Foo(),Foo必须是编译器已知的具体类型。
- 不允许虚构造函数。正如上一条,必须指定编译时要构造对象的精确类型。然后,编译器才能为特定类型分配内存并为任意基类调用默认构造函数。编译器随后为指定类型本身调用构造函数。(这一点就是为何不能在构造函数调用虚函数并期望它们调用派生类的overwrite的原因,因为派生类此时还没有被初始化)。
更为详细的说明参见面试语法糖(一)。(虚函数的作用在于通过基类的指针或者引用来调用它的时候能够变成调用派生类的那个成员函数。而构造函数是在创建对象时自动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。)
先入为主,工厂模式就是绕开这些限制,从基本层面来看,工厂方法就是一个普通的方法调用,该调用返回类的实例。但是,它们经常和继承一起使用,即派生类能够重写工厂方法并返回派生类的实例。常见的做法是使用抽象基类(Abstract Base Classes)也可以称为纯虚类。
对于图像API,流行的libjpeg库提供了JPEG/JFIF编解码器的实现,libpng库用于处理PNG格式的图像,大量的libtiff库模式用于读写各种风格的TIFF文件。问题来了,能不能采用一个普遍适用的接口来读取各种形式的图片呢?工厂模式的应用就必不可少了!!!
让我们来分析一下Opncv2.3.1的图像读写框架。主要分析cvSaveImage函数的实现,位于"OpenCV/otherlibs/highgui/loadsave.cpp"
1 2 3 4 5 6 7 8 9 10 11 |
#define CV_IMPL CV_EXTERN_C CV_IMPL IplImage* cvLoadImage( const char* filename, int iscolor ){ return (IplImage*)icvLoadImage( filename, iscolor, false ); //最后根据第三个参数返回不同的类型: return load_as_matrix ? (void*)matrix : (void*)image; //第三个参数可以用于装载矩阵,这里被忽略(false表示读图像)。 } CV_IMPL CvMat* cvLoadImageM( const char* filename, int iscolor ){ return (CvMat*)icvLoadImage( filename, iscolor, true ); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
// global image I/O filters static CvImageFilters g_Filters; static void* icvLoadImage( const char* filename, int flags, bool load_as_matrix ){ GrFmtReader* reader = 0;//// 查找图像的读驱动 IplImage* image = 0; CvMat hdr, *matrix = 0; int depth = 8; CvSize size; int iscolor; int cn; //数据的传递通过下列语句,g_Filters是文件域中的静态变量 reader = g_Filters.FindReader( filename ); if( !reader ) EXIT; //用基类去操作派生类 if( !reader->ReadHeader() ) //利用图像读驱动读出图像头信息(高宽等属性),具体数据成员参见GrFmtReader抽象基类 EXIT; size.width = reader->GetWidth(); size.height = reader->GetHeight(); if( flags == -1 ) iscolor = reader->IsColor(); //是否彩色 else{ if( (flags & CV_LOAD_IMAGE_COLOR) != 0 || ((flags & CV_LOAD_IMAGE_ANYCOLOR) != 0 && reader->IsColor()) ) iscolor = 1; else iscolor = 0; if( (flags & CV_LOAD_IMAGE_ANYDEPTH) != 0 ){ reader->UseNativeDepth(true); depth = reader->GetDepth(); } } cn = iscolor ? 3 : 1; //彩色通道数为3,灰度为1 int type; if(reader->IsFloat() && depth != 8) type = IPL_DEPTH_32F; else type = ( depth <= 8 ) ? IPL_DEPTH_8U : ( depth <= 16 ) ? IPL_DEPTH_16U : IPL_DEPTH_32S; CV_CALL( image = cvCreateImage( size, type, cn ));//创建IplImage* // 利用读驱动读图像的所有像素,image->data.ptr对应数据的开始地址,image->step表示每行像素所在内存的大小 if( !reader->ReadData( matrix->data.ptr, matrix->step, iscolor )){ if( load_as_matrix ) cvReleaseMat( &matrix ); else cvReleaseImage( &image ); EXIT; } delete reader; return image; } |
CvImageFilters在构造的时候,将已知的图像读写驱动保存到一个链表中。内部实现的时候 可以看到很 有趣的现象,其使用了工厂函类 来实现。
GrFmtFactoriesList在grfmt_base.h/grfmt_base.cpp中定义,用于保存GrFmtFilterFactory对象指针的链表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/************************************************************************************* Image Readers & Writers Class ************************************************************************************/ class CvImageFilters{ public: CvImageFilters(); ~CvImageFilters(); GrFmtReader* FindReader( const char* filename ) const; GrFmtWriter* FindWriter( const char* filename ) const; //const CvFilePath& Path() const { return (const CvFilePath&)m_path; }; //CvFilePath& Path() { return m_path; }; protected: GrFmtFactoriesList* m_factories; }; |
维护的时候构造函数只需通过工厂类来不断加入新功能支持新的图片格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
CvImageFilters::CvImageFilters(){ m_factories = new GrFmtFactoriesList; m_factories->AddFactory( new GrFmtBmp() ); m_factories->AddFactory( new GrFmtJpeg() ); m_factories->AddFactory( new GrFmtSunRaster() ); m_factories->AddFactory( new GrFmtPxM() ); m_factories->AddFactory( new GrFmtTiff() ); #ifdef HAVE_PNG m_factories->AddFactory( new GrFmtPng() ); #endif #ifdef HAVE_JASPER m_factories->AddFactory( new GrFmtJpeg2000() ); #endif #ifdef HAVE_ILMIMF m_factories->AddFactory( new GrFmtExr() ); #endif } CvImageFilters::~CvImageFilters(){ delete m_factories; } //FindReader/FindWriter用于查找图像对应的驱动。通过保存图像读写驱动的链表 来去调用FindReader()方法 GrFmtReader* CvImageFilters::FindReader( const char* filename ) const{ return m_factories->FindReader( filename ); } GrFmtWriter* CvImageFilters::FindWriter( const char* filename ) const{ return m_factories->FindWriter( filename ); } |
工厂类链表定义如下:
FindReader/FindWriter用于查找图像对应的驱动。如果想修改查找的规则,可以通过GetNextFactory遍历链表来实现。
真正的读写类从GrFmtReader派生,分别对应grfmt_bmp/grfmt_jpeg等各种格式驱动。然后通过前面的CvImageFilters::CvImageFilters()构造函数来将各个驱动串添加到g_Filters.m_factories链表中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
//grfmt_base.h typedef void* ListPosition; class GrFmtFactoriesList{ public: GrFmtFactoriesList(); virtual ~GrFmtFactoriesList(){RemoveAll();}; void RemoveAll(){ if( m_factories ) { for( int i = 0; i < m_curFactories; i++ ) delete m_factories[i]; delete[] m_factories; } m_factories = 0; m_maxFactories = m_curFactories = 0; }; bool AddFactory( GrFmtFilterFactory* factory ); int FactoriesCount() { return m_curFactories; }; ListPosition GetFirstFactoryPos(){ return (ListPosition)m_factories; //获得首地址 }; GrFmtFilterFactory* GetNextFactory( ListPosition& pos ); virtual GrFmtReader* FindReader( const char* filename ); virtual GrFmtWriter* FindWriter( const char* filename ); protected: GrFmtFilterFactory** m_factories; int m_maxFactories; int m_curFactories; }; //grfmt_base.cpp bool GrFmtFactoriesList::AddFactory( GrFmtFilterFactory* factory ){ assert( factory != 0 ); if( m_curFactories == m_maxFactories ){ // reallocate the factorys pointers storage //这里的内存开辟方式有点类似于vector. int newMaxFactories = 2*m_maxFactories; if( newMaxFactories < 16 ) newMaxFactories = 16; GrFmtFilterFactory** newFactories = new GrFmtFilterFactory*[newMaxFactories]; for( int i = 0; i < m_curFactories; i++ ) newFactories[i] = m_factories[i]; delete[] m_factories; m_factories = newFactories; m_maxFactories = newMaxFactories; } m_factories[m_curFactories++] = factory; return true; } GrFmtFilterFactory* GrFmtFactoriesList::GetNextFactory( ListPosition& pos ){ GrFmtFilterFactory* factory = 0; GrFmtFilterFactory** temp = (GrFmtFilterFactory**)pos; assert( temp == 0 || (m_factories <= temp && temp < m_factories + m_curFactories)); if( temp ){ factory = *temp++; pos = (ListPosition)(temp < m_factories + m_curFactories ? temp : 0); } return factory; } GrFmtReader* GrFmtFactoriesList::FindReader( const char* filename ){ FILE* f = 0; char signature[4096]; int sign_len = 0; GrFmtReader* reader = 0; ListPosition pos = GetFirstFactoryPos(); if( !filename ) return 0; while( pos ){ GrFmtFilterFactory* tempFactory = GetNextFactory(pos); int cur_sign_len = tempFactory->GetSignatureLength(); if( sign_len < cur_sign_len ) sign_len = cur_sign_len; } assert( sign_len <= (int)sizeof(signature) ); f = fopen( filename, "rb" ); if( f ){ sign_len = (int)fread( signature, 1, sign_len, f ); fclose(f); pos = GetFirstFactoryPos(); while( pos ) { GrFmtFilterFactory* tempFactory = GetNextFactory(pos); int cur_sign_len = tempFactory->GetSignatureLength(); if( cur_sign_len <= sign_len && tempFactory->CheckSignature(signature)){ reader = tempFactory->NewReader( filename ); break; } } } return reader; } |
GrFmtFilterFactory为各种格式图像读写驱动的构造工厂基类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
////////////////////////////// base class for filter factories ////////////////// class GrFmtFilterFactory{ public: GrFmtFilterFactory(); virtual ~GrFmtFilterFactory() {}; const char* GetDescription() { return m_description; }; int GetSignatureLength() { return m_sign_len; }; virtual bool CheckSignature( const char* signature ); virtual bool CheckExtension( const char* filename ); virtual GrFmtReader* NewReader( const char* filename ) = 0; virtual GrFmtWriter* NewWriter( const char* filename ) = 0; protected: const char* m_description; // graphic format description in form: // <Some textual description>( *.<extension1> [; *.<extension2> ...]). // the textual description can not contain symbols '(', ')' // and may be, some others. It is safe to use letters, digits and spaces only. // e.g. "Targa (*.tga)", // or "Portable Graphic Format (*.pbm;*.pgm;*.ppm)" int m_sign_len; // length of the signature of the format const char* m_signature; // signature of the format }; |
3个virtual函数,分表用于读图像文件头、读数据、关闭图像文件。图像文件在读图像头的时候被打开。
对于图像的其他属性,可以通过在子类中直接操作m_iscolor等protected成员完成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
//grfmt_base.h ///////////////////////////////// base class for readers //////////////////////// class GrFmtReader{ public: GrFmtReader( const char* filename ); virtual ~GrFmtReader(); int GetWidth() { return m_width; }; int GetHeight() { return m_height; }; bool IsColor() { return m_iscolor; }; int GetDepth() { return m_bit_depth; }; void UseNativeDepth( bool yes ) { m_native_depth = yes; }; bool IsFloat() { return m_isfloat; }; virtual bool ReadHeader() = 0; virtual bool ReadData( uchar* data, int step, int color ) = 0; virtual void Close(); protected: bool m_iscolor; int m_width; // width of the image ( filled by ReadHeader ) int m_height; // height of the image ( filled by ReadHeader ) int m_bit_depth;// bit depth per channel (normally 8) char m_filename[_MAX_PATH]; // filename bool m_native_depth;// use the native bit depth of the image bool m_isfloat; // is image saved as float or double? }; ///////////////////////////// base class for writers //////////////////////////// class GrFmtWriter{ public: GrFmtWriter( const char* filename ); virtual ~GrFmtWriter() {}; virtual bool IsFormatSupported( int depth ); virtual bool WriteImage( const uchar* data, int step, int width, int height, int depth, int channels ) = 0; protected: char m_filename[_MAX_PATH]; // filename }; int m_sign_len; // length of the signature of the format const char* m_signature; // signature of the format }; //grfmt_base.cpp #include "grfmt_base.h" #include "bitstrm.h" GrFmtReader::GrFmtReader( const char* filename ){ strncpy( m_filename, filename, sizeof(m_filename) - 1 ); m_filename[sizeof(m_filename)-1] = '\0'; m_width = m_height = 0; m_iscolor = false; m_bit_depth = 8; m_native_depth = false; m_isfloat = false; } GrFmtReader::~GrFmtReader(){ Close(); } void GrFmtReader::Close(){ m_width = m_height = 0; m_iscolor = false; } |
定义了一个抽象基类,包含了纯虚方法。对于各种图像格式,定义了其派生类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
//grfmt_bmp.h Windows Bitmap reader class GrFmtBmpReader : public GrFmtReader{ public: GrFmtBmpReader( const char* filename ); ~GrFmtBmpReader(); bool ReadData( uchar* data, int step, int color ); bool ReadHeader(); void Close(); protected: RLByteStream m_strm; PaletteEntry m_palette[256]; int m_bpp; int m_offset; BmpCompression m_rle_code; }; //构造函数,用基类去初始化 GrFmtBmpReader::GrFmtBmpReader( const char* filename ) : GrFmtReader( filename ){ m_offset = -1; } // ... writer class GrFmtBmpWriter : public GrFmtWriter{ public: GrFmtBmpWriter( const char* filename ); ~GrFmtBmpWriter(); bool WriteImage( const uchar* data, int step, int width, int height, int depth, int channels ); protected: WLByteStream m_strm; }; //具体实现方法不再给出,可以自己阅读源码 |
所有的图片格式类 都是该基GrFmtFilterFactory的继承,如:GrFmtBmp 的继承,并通过NewReader生成真正处 理 图片的类: GrFmtBmpReader,传出的指针是它基类 :GrFmtReader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// filter factory class GrFmtBmp : public GrFmtFilterFactory{ public: GrFmtBmp(); ~GrFmtBmp(){}; GrFmtReader* NewReader( const char* filename ); GrFmtWriter* NewWriter( const char* filename ); }; GrFmtBmp::GrFmtBmp(){ m_sign_len = 2; m_signature = fmtSignBmp; m_description = "Windows bitmap (*.bmp;*.dib)"; } GrFmtReader* GrFmtBmp::NewReader( const char* filename ){ return new GrFmtBmpReader( filename ); } GrFmtWriter* GrFmtBmp::NewWriter( const char* filename ){ return new GrFmtBmpWriter( filename ); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
//grfmt.h ////////////////////////////// base class for filter factories ////////////////// class GrFmtFilterFactory{ public: GrFmtFilterFactory(); virtual ~GrFmtFilterFactory() {}; const char* GetDescription() { return m_description; }; int GetSignatureLength() { return m_sign_len; }; virtual bool CheckSignature( const char* signature ); virtual bool CheckExtension( const char* filename ); virtual GrFmtReader* NewReader( const char* filename ) = 0; virtual GrFmtWriter* NewWriter( const char* filename ) = 0; protected: const char* m_description; // graphic format description in form: // <Some textual description>( *.<extension1> [; *.<extension2> ...]). // the textual description can not contain symbols '(', ')' // and may be, some others. It is safe to use letters, digits and spaces only. // e.g. "Targa (*.tga)", // or "Portable Graphic Format (*.pbm;*.pgm;*.ppm)" int m_sign_len; // length of the signature of the format const char* m_signature; // signature of the format }; /////////////////////////// list of graphic format filters /////////////////////////////// typedef void* ListPosition; class GrFmtFactoriesList { public: GrFmtFactoriesList(); virtual ~GrFmtFactoriesList(); void RemoveAll(); bool AddFactory( GrFmtFilterFactory* factory ); int FactoriesCount() { return m_curFactories; }; ListPosition GetFirstFactoryPos(); GrFmtFilterFactory* GetNextFactory( ListPosition& pos ); virtual GrFmtReader* FindReader( const char* filename ); virtual GrFmtWriter* FindWriter( const char* filename ); protected: GrFmtFilterFactory** m_factories; int m_maxFactories; int m_curFactories; }; |
一些其他的方法实现,可以不用看。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#include "_highgui.h" #include "grfmt_base.h" #include "bitstrm.h" GrFmtFilterFactory::GrFmtFilterFactory(){ m_description = m_signature = 0; m_sign_len = 0; } bool GrFmtFilterFactory::CheckSignature( const char* signature ){ return m_sign_len > 0 && signature != 0 && memcmp( signature, m_signature, m_sign_len ) == 0; } static int GetExtensionLength( const char* buffer ){ int len = 0; if( buffer ){ const char* ext = strchr( buffer, '.'); if( ext++ ) while( isalnum(ext[len]) && len < _MAX_PATH ) len++; } return len; } |