Skip to main content

Wzorzec Registry

Podczas opisywania wzorca Singleton, nazywanym również antywzorcem, obiecałem wyjaśnić zasadę działania wzorca Registry.

Wzorzec ma za zadanie przechowywać i udostępniać dane w obrębie całej aplikacji. Działa na podobnej zasadzie co zmienna globalna.
Wielu początkujących programistów ma problem z wymianą informacji pomiędzy różnymi częściami swojej aplikacji, np. formularzami.

Wzorzec Registry jest jednym ze sposobów, choć nie jedynym, który pozwala rozwiązać ten problem.

Zaawansowani programiści twierdzą wręcz, że przeczy idei hermetyzacji i mają rację. Nie mniej wszytko zależy od założeń naszej aplikacji, jej poziomu skomplikowania, wielkości, możliwości ponownego wykorzystania kodu itp.

Pocieszę Cię, kod zaprezentowany w artykule Wzorzec projektowy Singleton jest wciąż aktualny i łączy w sobie oba opisywane wzorce projektowe.

Jeśli chcesz przechowywać i udostępniać inne zmienne, po prostu tworzysz właściwość prywatną i odpowiednie metody, tzw, settery i gettery, które będą umożliwiały aktualizację zmiennej i jej pobieranie.
Jeśli wydaje Ci się to pracochłonne lub nieefektywne, zawsze możesz zdefiniować uniwersalną kolekcję obiektów, w której będziesz przechowywał obiekty dowolnego typu.

Poniższy kod prezentuje trochę inną budowę naszego rejestru, który potrafi przechowywać rożne obiekty pochodne od klasy Object, bez potrzeby modyfikacji samej klasy Registry i tworzenia dodatkowych metod i zmiennych.

/*
 * AppRegistry.h
 *
 *  Created on: 2011-08-20
 *      Author: markac
 */

#ifndef APPREGISTRY_H_
#define APPREGISTRY_H_

#include <FBase.h>

class ApplicationRegistry :
	public Osp::Base::Collection::HashMap
{

private:
	static ApplicationRegistry* __instance;
	ApplicationRegistry();

public:
	virtual ~ApplicationRegistry();

	static ApplicationRegistry* GetIntance();
	Osp::Base::Object* GetProperty(Osp::Base::String& key);
	void SetProperty(Osp::Base::String& key, Osp::Base::Object& val);
	bool HasProperty(Osp::Base::String& key);
};

#endif /* APPREGISTRY_H_ */
/*
 * AppRegistry.cpp
 *
 *  Created on: 2011-08-20
 *      Author: markac
 */

#include "ApplicationRegistry.h"

ApplicationRegistry* ApplicationRegistry::__instance = null;

ApplicationRegistry::ApplicationRegistry()
{
	Construct();
}

ApplicationRegistry::~ApplicationRegistry()
{
	RemoveAll(false);
}

ApplicationRegistry* ApplicationRegistry::GetIntance()
{
	if (__instance == null)
		__instance = new ApplicationRegistry();

	return __instance;
}

Osp::Base::Object* ApplicationRegistry::GetProperty(Osp::Base::String& key)
{
	if (HasProperty(key))
		return GetValue(key);
	else
		return null;
}

void ApplicationRegistry::SetProperty(Osp::Base::String& key, Osp::Base::Object& val)
{
	if (HasProperty(key))
		SetValue(key, val);
	else
		Add(key, val);
}

bool ApplicationRegistry::HasProperty(Osp::Base::String& key)
{
	bool contains;
	ContainsKey(key, contains);
	return contains;
}

Korzystanie z rejestru:

bool AppRegistrySample::OnAppInitializing(AppRegistry& appRegistry)
{
	// TODO:
	// Initialize UI resources and application specific data.
	// The application's permanent data and context can be obtained from the appRegistry.
	//
	// If this method is successful, return true; otherwise, return false.
	// If this method returns false, the application will be terminated.

	// Uncomment the following statement to listen to the screen on/off events.
	//PowerManager::SetScreenEventListener(*this);

	// Create a form
	Form1 *pForm1 = new Form1();
	pForm1->Initialize();

	// Add the form to the frame
	Frame *pFrame = GetAppFrame()->GetFrame();
	pFrame->AddControl(*pForm1);

	// Set the current form
	pFrame->SetCurrentForm(*pForm1);

	// Draw and Show the form
	pForm1->Draw();
	pForm1->Show();

	ApplicationRegistry* appReg = ApplicationRegistry::GetIntance();

	String* key1 = new String(L"Form1");  // Nie usuwamy operatorem delete! Robi to za nas destruktor.
	String* key2 = new String(L"Int1");  // Nie usuwamy operatorem delete! Robi to za nas destruktor.

	appReg->SetProperty(*key1, *pForm1);
	appReg->SetProperty(*key2, *(new Integer(5)));

	return true;
}
void Form1::OnActionPerformed(const Osp::Ui::Control& source, int actionId)
{
	switch(actionId)
	{
	case ID_BUTTON_OK:
		{
			AppLog("OK Button is clicked! n");

			ApplicationRegistry* appReg = ApplicationRegistry::GetIntance();

			String* key1 = new String(L"Form1");  // należy zwolnić operatorem delete
			String* key2 = new String(L"Int1");  // należy zwolnić operatorem delete

			if (appReg->HasProperty(*key1))
			{
				Form1* form1 = static_cast<Form1*>(appReg->GetProperty(*key1));
				AppLogDebug("Nazwa formularza: %S", form1->GetName().GetPointer());
			}
			else
				AppLogDebug("Nie ma takiego klucza.");

			if (appReg->HasProperty(*key2))
			{
				Integer* int1 = static_cast<Integer*>(appReg->GetProperty(*key2));
				AppLogDebug("Wartość Int: %S", int1->ToString().GetPointer());

			}
			else
				AppLogDebug("Nie ma takiego klucza.");

			delete key1;
			delete key2;

		}
		break;
	default:
		break;
	}
}

Na końcu naszego programu możemy już zwolnić nasz Rejestr:

ApplicationRegistry* appReg = ApplicationRegistry::GetIntance();
delete appReg;

Nie zawsze to wystarczy! Jeśli przechowujesz w rejestrze obiekt formularza, zostanie on usunięty automatycznie przez sam framework i nie możesz go usunąć ręcznie z Rejestru!
Jeśli natomiast w rejestrze umieszczasz inne obiekty, jak. np. wcześniej wymieniony Integer(), należy go usunąć ręcznie:

appReg->Remove(*key2, true);
delete appReg;

Poniższy kod usuwa wszystkie obiekty w Rejestrze, ale jak wspomniałem, nie możesz użyć tej funkcji w przypadku obiektów, którymi zarządza Framework bada.

appReg->RemoveAll(true);
delete appReg;

Po tej operacji nie można ponownie użyć rejestru! Nie tylko dlatego, że zwolniliśmy pamięć operatorem delete, ale także dlatego, że nie da się powtórnie przydzielić pamięci na nowy obiekt!
Zmienna statyczna w konstruktorze naszego singletona posiada wartość inną niż null i zostanie zwrócony obiekt, który już nie istnieje.

Jeśli szukasz sposobu, na ominięcie tego problemu, odsyłam do dodatkowej lektury:
http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=148.

Opis klasy HashMap: http://developer.bada.com/help/topic/com.osp.apireference.help/classOsp_1_1Base_1_1Collection_1_1HashMap.html.

Podsumowując.

Z rejestru możesz korzystać w obrębie całej swojej aplikacji, tak jakby to była zmienna globalna i nie martwić się o przekazywanie zmiennych między rożnymi jej częściami.
Jedynym utrudnieniem jest konieczność tworzenia kluczy typu String, co wymusza od nas klasa HashMap. O ile łatwiej byłoby, gdyby wystarczył zapis:

appReg->GetProperty(L"Form1");

zamiast:

String* key1 = new String(L"Form1");
appReg->GetProperty(*key1);

Nie jesteśmy zmuszeni do korzystania z klasy HashMap. Możemy zaimplementować własną kolekcję obiektów, która będzie przechowywała klucze i wartości w strukturze:

struct KeyVal {
  String key;
  Object* val;
};

ArrayListT<KeyVal*> map;
map.Construct();

KeyVal* kv = new KeyVal();
kv->key = L"Form1";
kv->val = this;

map.Add(kv);

Pozostałe metody należy zaimplementować we własnym zakresie 🙂

Ważna uwaga. Ostatni kod źródłowy nie został już przeze mnie przetestowany, ani tym bardziej kompilowany i był pisany z głowy.
Nie miałem już sił tego sprawdzać…

markac

Full-stack Web Developer