in the "Big talk design mode C + + version-abstract Factory mode", we save the world attempted, leaving little regret, in this article we will give a solution--com component technology, but also pull the factory model in the application of COM component technology.
The root cause of the Factory mode violation of the open-closed principle is that the object generation cannot be controlled by the data outside the client's module, if we can write a class name from the XML, registry, configuration file, and then the module reads out the class name and creates the object based on the read-out class name, and C # Big on the reflection technology like the cow B coax. Fortunately, Microsoft's COM component technology provides such a platform.
1, COM components are God horse
In order to save space, this please own Baidu
2, the implementation of COM components
To uncover the nature of COM components, we built an in-process COM component from 0, without using an automated set of ATL.
2.1 The Basics of COM components (self-skipped, known)
2.1.1 Return types in COM components
HRESULT is a specialized return type in a COM component, HRESULT is actually a long type, is greater than or equal to 0 to indicate success, a negative value is a failure and itself is a failure code, is not a slight sense of pain, Microsoft great God is nothing to get a wonderful return value to play.
2.1.2 GUID
GUIDs and IID are actually a cargo, is a 128-digit number, known as the world's unique identification code, VS provides tools to generate, to ensure that each unique, COM component is used to replace the class name, to prevent conflicts with the same name.
2.1.3 IUnknown Interface
The IUnkown interface is the source of the COM component World, and all component interfaces and factory classes must inherit it, why is it Diao?
Classiunknown{public:virtualulongaddref () = 0;virtualulongrelease () = 0;virtualhresultqueryinterface (REFIID riid, void **ppvobject) = 0;};
IUnkown interface AddRef () and Release () are used to increase or decrease the interface object reference, when the reference count is 0 o'clock, delete the object itself, so after using the COM interface to remember release (), so it will be automatically released,Without the need for a delete。 The QueryInterface () function is a function that returns an interface to an object, riid is the IID of the object interface, can be understood as the requested class name, and the parameter ppvobject is a pointer to the interface object pointer.
2.1.4 IClassFactory
IClassFactory interface is the ancestor of all factory classes, all factory classes inherit it, of course, it must inherit the more hanging IUnknown interface.
Classiclassfactory:public Iunknown{public:virtualhresult CreateInstance (iunknown* pUnkOuter, REFIID riid, void** PPV) = 0;virtualhresult Lockserver (BOOL bLock) = 0;};
The most important thing in the IClassFactory interface is the CreateInstance () function, which returns the COM object used by the final user, the first parameter punkouter whether it is aggregated, we are null here, it means no aggregation, riid is a pointer to a COM object that returns the user's IID,PPV for the user's COM object.
2.2 Create a new DLL project, export the following 4 functions
Stdapi DllGetClassObject (Refclsid rclsid, REFIID riid, void** PPV) Stdapi DllCanUnloadNow () Stdapi DllRegisterServer () ST DAPI DllUnregisterServer ()
The key is the DllGetClassObject function, which is used to return the factory object and how to implement it later.
2.3 Still above the "Big talk design mode C + + version-abstract Factory mode" database operations as an example, instead of using COM component technology to achieve
2.3.1 Defining abstract interfaces ( note to inherit IUnknown)
Class Iemployee:public Iunknown{public:virtualboolinserttodb (employee& stemployee) = 0; Virtualemployeegetemployee (int NID) = 0;}; Class Idepartment:public Iunknown{public:virtualboolinserttodb (department& stdepartment) = 0; virtualdepartmentgetdepartment (int NID) = 0;};
2.3.2 COM Object Interface implementation, CDATABASEFROMMYSQL implementation similar (new implementation of IUnknown interface)
Classcdatabasefromaccess:public IEmployee, Public idepartment{public:cdatabasefromaccess (): M_ulRefCount (0) {}ULONG Stdmethodcalltype AddRef (void) {return++m_ulrefcount;} ULONG stdmethodcalltype Release (void) {m_ulrefcount--;//If the object reference count is 0, delete itself if (0 = = m_ulrefcount) {delete this;} return m_ulrefcount;} HRESULT stdmethodcalltype QueryInterface (REFIID riid, void **ppvobject) {hresulthr = s_ok;//if the default interface is requested or iid_ The IEmployee interface is returned IEmployee interface if (Isequaliid (riid, Iid_iemployee)) {*ppvobject = (iemployee*) this;} else if (isequaliid (riid, iid_idepartment)) {*ppvobject = (idepartment*) this;} ELSE{HR = E_nointerface;} if (SUCCEEDED (HR)) {AddRef ();} RETURNHR;} The IEmployee interface implements the function Boolinserttodb (employee& stemployee) {_tprintf ("Insert Employee%s into _t"), StEmployee.tstrName.c_str ()); returntrue;} Employeegetemployee (int nID) {employeestemployee;printf ("Get an employee from Access by ID%d\n", nID); returnstemployee;} The Idepartment interface implements the function Boolinserttodb (department& stdepartment) {_tprintf (_t ("Insert DePartment%s into access\n "), StDepartment.tstrDepartmentName.c_str ()); returntrue;} departmentgetdepartment (int nID) {departmentstdepartment;printf ("Get an Department from Access by ID%d\n", NID); Returnstdepartment;} Private:ulongm_ulrefcount;};
Because COM component technology requires that 1 or more COM interface objects be returned from QueryInterface (), and any interface returned can query to other returned COM interface objects, a new class inherits and implements the IEmployee and Idepartment interfaces. The riid corresponding COM interface object is then returned in QueryInterface () by a type cast, and the If else structure returns an interface object in a way that is somewhat like a simple factory pattern, but with a different approach.
2.3.3 Object Factory Implementation, Cfactoryfromaccess implements similar
Class Cfactoryfrommysql:public Iclassfactory{public:cfactoryfrommysql (void): M_ulrefcount (0) {}ulong Stdmethodcalltype AddRef (void) {return++m_ulrefcount;} ULONG stdmethodcalltype Release (void) {m_ulrefcount--;//If the object reference count is 0, delete itself if (0 = = m_ulrefcount) {delete this;} return m_ulrefcount;} HRESULT stdmethodcalltype QueryInterface (REFIID riid, void **ppvobject) {hresulthr = s_ok;//if the default interface is requested or iid_ The IEmployee interface returns the interface pointer and adds the reference if (Isequaliid (riid, iid_iclassfactory) | | Isequaliid (riid, Iid_ifactoryfrommysql)) {*ppvobject = (iclassfactory*) this;} else if (isequaliid (riid, IID_IUnknown)) {*ppvobject = (iunknown*) this;} ELSE{HR = E_nointerface;} if (SUCCEEDED (HR)) {AddRef ();} RETURNHR;} HRESULT stdmethodcalltype CreateInstance (iunknown* punkouter, REFIID riid, void** PPV) {if (punkouter) {return class_e_ noaggregation;//does not support aggregation}cdatabasefrommysql*pocdatabasefrommysql = new Cdatabasefrommysql (); if (NULL = = Pocdatabasefrommysql) {return e_outofmemory;} Hresulthr = Pocdatabasefrommysql->queryinterface (riid, PPV); if (FAILED (HR)) {deletepocdatabasefrommysql;returnhr;} RETURNS_OK;} HRESULT Stdmethodcalltype lockserver (BOOL block) {block? g_uldllrefcount++: G_ULDLLREFCOUNT--;RETURNS_OK;} Private:ulongm_ulrefcount;};
COM component technology requires a COM object to have a corresponding factory production, that is, to meet the factory method pattern, the production object is in the CreateInstance () in the new The QueryInterface function of requesting an object after a production object is riid to return the COM object that the user needs. So where did the factory object come from?
2.3.4 DllGetClassObject
DllGetClassObject is one of the 4 export functions, which is to return the factory object according to RIID, note that the riid here is the riid of the factory class, not the riid,rclsid of the COM interface object is the GUID of the COM component. Used to differentiate other COM component modules, which the user needs to specify when requesting a COM interface object.
Stdapi DllGetClassObject (Refclsid rclsid, REFIID riid, void** PPV) {if (! IsEqualGUID (Rclsid, clsid_idatabase)) {returnclass_e_classnotavailable;} HRESULT hr = S_ok;iclassfactory*pocclassfactory = null;do {//Traverse all factory classes to determine the successful return request RIID*PPV = Null;pocclassfactory = new CFac Toryfrommysql (); if (pocclassfactory) {if (S_OK = = Pocclassfactory->queryinterface (riid, PPV)) {break;} Else{pocclassfactory->release ();}} Pocclassfactory = new Cfactoryfromaccess (); if (pocclassfactory) {if (S_OK = = Pocclassfactory->queryinterface (riid, PPV)) {break;} Else{pocclassfactory->release ();}} hr = class_e_classnotavailable;} while (FALSE); returnhr;}
As mentioned earlier, COM component technology requires that the interface object be returned from QueryInterface (), and iclassfactory is naturally the same, so call the QueryInterface function of the existing factory and fetch the riid corresponding factory object. It is not optimized here in order to make it clear to everyone. Perhaps the reunion asked, why not write a simple factory, according to RIID return different factory objects, this is due to put the judgment inside the factory class, can give the factory class more flexibility, allow the factory to do some inspection or other work, decide whether to return factory objects, and Also maintains consistency with COM interface object requests.
2.4. Use of COM components
Summary of definitions for 2.4.1 CLSID and IID
{377c4a5c-52cb-4557-a9e5-a57018b34197}static Const GUID Iid_iemployee = {0x377c4a5c, 0X52CB, 0x4557, {0xa9, 0xe5, 0xa 5, 0x70, 0x18, 0xb3, 0x41, 0x97}};//{940e7c36-8472-46e9-be93-6c1f53699a1d}static const GUID iid_idepartment = {0x940e7 C36, 0x8472, 0x46e9, {0xbe, 0x93, 0x6c, 0x1f, 0x53, 0x69, 0x9a, 0x1d}};//{094ef4b6-406f-4cdc-a036-dbfc9e163196}static Const GUID Iid_ifactoryfrommysql = {0x94ef4b6, 0x406f, 0X4CDC, {0xa0, 0x36, 0xdb, 0XFC, 0x9e, 0x16, 0x31, 0x96}};//{2 08c8f65-4f26-499d-a075-c0a6dfc312b1}static Const GUID iid_ifactoryfromaccess = {0x208c8f65, 0x4f26, 0x499d, {0xa0, 0x75 , 0xc0, 0xa6, 0XDF, 0xc3, 0x12, 0xb1}};//{712ce48e-f1b4-4c5d-8ba8-6e367c4d6422}static const GUID clsid_idatabase = {0x 712ce48e, 0XF1B4, 0x4c5d, {0x8b, 0xa8, 0x6e, 0x36, 0x7c, 0x4d, 0x64, 0x22}};
2.4.2 Registering components
Enter Regsvr32 DataBaseCom.dll in CMD to complete the registration
2.4.3 Calling COM components
Voidtest () {CoInitialize (NULL); Hresulthr = S_OK;IIDSTFACTORYIID = iid_ifactoryfromaccess;//Replace here Iclassfactory*poiclassfactory = null;//Fetch Factory class object hr = CoGetClassObject (Clsid_idatabase, Clsctx_inproc_server, NULL, Stfactoryiid, (void**) &poiclassfactory); SUCCEEDED (HR)) {Iemployee*poiemployee = Null;if (SUCCEEDED (Poiclassfactory->createinstance (NULL, IID_IEmployee, ( void**) &poiemployee)) {Employeestemployee;stemployee.nid = 1;stemployee.tstrname = _T ("Jim");p oiemployee-> Inserttodb (Stemployee);p oiemployee->release ();} else{printf ("Get iemployee fail!!! \ n ");} Idepartment*poidepartment = Null;if (SUCCEEDED (Poiclassfactory->createinstance (NULL, IID_IDepartment, (void**) &poidepartment)) {Departmentstdepartment;stdepartment.nid = 2;stdepartment.tstrdepartmentname = _T ("Marketing" ); Stdepartment.tstrmanager = _t ("Jim");p Oidepartment->inserttodb (stdepartment);p oidepartment->release ();} else{printf ("Get idepartment fail!!! \ n ");} Poiclassfactory->release ();} else{printf ("GeT iclassfactory fail!!! \ n ");} CoUninitialize ();}
The component call first takes the factory object through CoGetClassObject (), and the component registers its CLSID and path to the registry CoGetClassObject () actually finds the path to the DLL through clsid_idatabase and loads it, Then call DllGetClassObject () to the IID of the factory class to get the factory class object, and then invoke the CreateInstance () function of the factory class object to pass in the IID of the COM interface object to get the COM interface object that the user needs, after using COM component technology implementation instead, If you need to replace the database, simply change the "IID stfactoryiid = iid_ifactoryfromaccess;" IID, the IID is actually a bunch of numbers that we can put into the configuration file, XML, registry, and so on, and let the client program load. If there is a new data type to join, such as SQL Server, after we join in the component, the client simply changes the configuration file of the factory class IID, without changing the client code, which perfectly embodies the open-close principle.
The CLSID and path of the COM component in the registry
because of the number of code, the entire solution has been packaged and uploaded, the code is compiled under VS2010, before the test in cmd input "Regsvr32 DataBaseCom.dll" to complete the registration, and then run Console.exe to see the results of the run
Code: http://download.csdn.net/detail/gufeng99/8806425
Big talk design mode C + +--typical application of Factory mode in COM