Agregasi
Agregasi adalah mekanisme penggunaan kembali objek di mana objek luar mengekspos antarmuka dari objek dalam seolah-olah diimplementasikan pada objek luar itu sendiri. Ini berguna ketika objek luar mendelegasikan setiap panggilan ke salah satu antarmukanya ke antarmuka yang sama di objek dalam. Agregasi tersedia sebagai kenyamanan untuk menghindari overhead implementasi ekstra di objek luar dalam hal ini. Agregasi sebenarnya adalah kasus khusus penahanan /delegasi.
Agregasi hampir sesederhana diterapkan sebagai penahanan, kecuali untuk tiga fungsi IUnknown: QueryInterface, AddRef, dan Release. Tangkapannya adalah bahwa dari perspektif klien, fungsi IUnknown apa pun pada objek luar harus memengaruhi objek luar. Artinya, AddRef dan Release memengaruhi objek luar dan QueryInterface mengekspos semua antarmuka yang tersedia pada objek luar. Namun, jika objek luar hanya mengekspos antarmuka objek dalam sebagai miliknya, anggota IUnknown objek dalam yang dipanggil melalui antarmuka tersebut akan bersifat berbeda dari anggota IUnknown pada antarmuka objek luar, pelanggaran absolut terhadap aturan dan properti yang mengatur IUnknown.
Solusinya adalah bahwa agregasi memerlukan implementasi eksplisit IUnknown pada objek dalam dan delegasi metode IUnknown dari antarmuka lain ke metode IUnknown objek luar.
Membuat objek yang dapat diagregasi bersifat opsional; namun, mudah dilakukan dan memberikan manfaat yang signifikan. Aturan berikut berlaku untuk membuat objek yang dapat diagregasi:
- Implementasi objek Agregat (atau dalam) dari QueryInterface, AddRef, dan Release untuk antarmuka IUnknown-nya mengontrol jumlah referensi objek dalam, dan implementasi ini tidak boleh mendelegasikan ke objek luar yang tidak diketahui (pengontrol IUnknown).
- Implementasi objek agregat (atau dalam) dari QueryInterface, AddRef, dan Release untuk antarmuka lain harus mendelegasikan ke IUnknown pengontrol dan tidak boleh secara langsung memengaruhi jumlah referensi objek dalam.
- IUnknown dalam harus menerapkan QueryInterface hanya untuk objek dalam.
- Objek yang dapat diagregasi tidak boleh memanggil AddRef saat memegang referensi ke pointer IUnknown pengontrol.
- Ketika objek dibuat, jika ada antarmuka selain IUnknown yang diminta, pembuatan harus gagal dengan E_NOINTERFACE.
Fragmen kode berikut mengilustrasikan implementasi objek yang dapat diagregasi dengan benar dengan menggunakan metode kelas berlapis untuk menerapkan antarmuka:
// CSomeObject is an aggregable object that implements
// IUnknown and ISomeInterface
class CSomeObject : public IUnknown
{
private:
DWORD m_cRef; // Object reference count
IUnknown* m_pUnkOuter; // Controlling IUnknown, no AddRef
// Nested class to implement the ISomeInterface interface
class CImpSomeInterface : public ISomeInterface
{
friend class CSomeObject ;
private:
DWORD m_cRef; // Interface ref-count, for debugging
IUnknown* m_pUnkOuter; // Controlling IUnknown
public:
CImpSomeInterface() { m_cRef = 0; };
~ CImpSomeInterface(void) {};
// IUnknown members delegate to the outer unknown
// IUnknown members do not control lifetime of object
STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
{ return m_pUnkOuter->QueryInterface(riid,ppv); };
STDMETHODIMP_(DWORD) AddRef(void)
{ return m_pUnkOuter->AddRef(); };
STDMETHODIMP_(DWORD) Release(void)
{ return m_pUnkOuter->Release(); };
// ISomeInterface members
STDMETHODIMP SomeMethod(void)
{ return S_OK; };
} ;
CImpSomeInterface m_ImpSomeInterface ;
public:
CSomeObject(IUnknown * pUnkOuter)
{
m_cRef=0;
// No AddRef necessary if non-NULL as we're aggregated.
m_pUnkOuter=pUnkOuter;
m_ImpSomeInterface.m_pUnkOuter=pUnkOuter;
} ;
~CSomeObject(void) {} ;
// Static member function for creating new instances (don't use
// new directly). Protects against outer objects asking for
// interfaces other than Iunknown.
static HRESULT Create(IUnknown* pUnkOuter, REFIID riid, void **ppv)
{
CSomeObject* pObj;
if (pUnkOuter != NULL && riid != IID_IUnknown)
return CLASS_E_NOAGGREGATION;
pObj = new CSomeObject(pUnkOuter);
if (pObj == NULL)
return E_OUTOFMEMORY;
// Set up the right unknown for delegation (the non-
// aggregation case)
if (pUnkOuter == NULL)
{
pObj->m_pUnkOuter = (IUnknown*)pObj ;
pObj->m_ImpSomeInterface.m_pUnkOuter = (IUnknown*)pObj;
}
HRESULT hr;
if (FAILED(hr = pObj->QueryInterface(riid, (void**)ppv)))
delete pObj ;
return hr;
}
// Inner IUnknown members, non-delegating
// Inner QueryInterface only controls inner object
STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
{
*ppv=NULL;
if (riid == IID_IUnknown)
*ppv=this;
if (riid == IID_ISomeInterface)
*ppv=&m_ImpSomeInterface;
if (NULL==*ppv)
return ResultFromScode(E_NOINTERFACE);
((IUnknown*)*ppv)->AddRef();
return NOERROR;
} ;
STDMETHODIMP_(DWORD) AddRef(void)
{ return ++m_cRef; };
STDMETHODIMP_(DWORD) Release(void)
{
if (--m_cRef != 0)
return m_cRef;
delete this;
return 0;
};
};
Saat mengembangkan objek yang dapat diagregasi, aturan berikut berlaku:
Saat membuat objek dalam, objek luar harus secara eksplisit meminta IUnknown-nya.
Objek luar harus melindungi implementasi Rilisnya dari reentransi dengan jumlah referensi buatan di sekitar kode penghancurannya.
Objek luar harus memanggil metode Rilis IUnknownpengontrolnya jika meminta penunjuk ke salah satu antarmuka objek dalam. Untuk membebaskan pointer ini, objek luar memanggil metode AddRef IUnknownpengontrolnya, diikuti oleh Rilis pada pointer objek dalam.
// Obtaining inner object interface pointer pUnkInner->QueryInterface(IID_ISomeInterface, &pISomeInterface); pUnkOuter->Release(); // Releasing inner object interface pointer pUnkOuter->AddRef(); pISomeInterface->Release();
Objek luar tidak boleh mendelegasikan kueri secara membabi buta untuk antarmuka yang tidak dikenal ke objek dalam, kecuali perilaku tersebut khususnya niat objek luar.