Container DI/IoC

0

Jako PHP-owiec z 5 dniowym doswiadczeniem z C++ zrobilem cos takiego wg wzorcow ktore mozna spotkac w php. Probowalem sie bawic z szablonami, ale zbyt krotko mam do czynienia z C++ i caly czas pytykam sie na skladni zatem zrobilem cos wg tego co znam z php. Wydaje sie ze to "chodzi". Bede wdzieczny za jakies sugestie zwiazane z optymamizacja tego kodu.

#pragma once

#include <memory>
#include <functional>
#include "Config.h"
#include "Order.h"

class Container
{
private:	
	Config* config;
	Order* order;

	std::function<Config*()> setConfig;
	std::function<Order*()> setOrder;

public:
	Container();
	~Container();

	Config* getConfig();
	Order* getOrder();
};
#include "stdafx.h"
#include "Container.h"

Container::Container()
{
	this->setConfig = [this]() -> Config*
	{
		this->config = new Config();
		
		return this->config;
	};

	this->setOrder = [this]() -> Order*
	{
		this->order = new Order(this->setConfig());
		
		return this->order;
	};
}

Container::~Container()
{
}

Config* Container::getConfig()
{
	if (this->config) {
		return this->config;
	}
	
	return this->setConfig();
}

Order* Container::getOrder()
{
	if (this->order) {
		return this->order;
	}

	return this->setOrder();
}
0
  1. Wrzuciłeś nagłówke memory a nie korzystasz w ogóle z shared_ptr czy unique_ptr. Czemu nie shared_ptr albo unique_ptr z move?
  2. W poprzednim twoim temacie pytałem cię o desing co sam stwierdziłeś chyba pamiętasz. To moje 3 grosze.
    Po co używasz lambd do tworzenia obiektów? Jaka jest tego wyższość na tworzeniem nowego obiektu bezpośrednio w funkcjach get? Powiem więcej założę się że kompilator uwaga, zrobił inline i de facto nie będzie żadnej lambdy(ackzolwiek pamiętajmy std::function to nie to samo co auto labmda = {}). Zauważ komplikujesz sobie sam życie, już lepiej było użyć funkcji prywatnej która tworzy nowy obiekt jeśli jest on skomplikowany(tu nie jest) ba co szczególnie dużych obiektach mogło by być jeszcze lepsze niż rozwlekła lambda gdzieś w ciele..
  3. Zapytam inaczej jaki jest twój cel? Wtedy możemy pomyśleć nad rozwiązaniem a nie przenoszeniem czegoś z php.
0

Dzieki za reakcje,

  1. To moje przeoczenie. Poczatkowo probowalem wykorzystac unique_ptr, ale nie poradzilem sobie z dopasowaniem typu zwracanego z lambdy wiec jak widzisz poszedlem na skroty. Tutaj wlasciwie bylbym Ci wdzieczny za sugestie jak sobie z tym poradzic.

2.1 "... W poprzednim twoim temacie pytałem cię o desing co sam stwierdziłeś chyba pamiętasz. To moje 3 grosze."
Wybacz, ale wg mnie caly czas design aplikacji ma sie nijak do budowy contenera DI, wiec moze lepiej nie wracajmy juz do tego watku.

2.2 "...Po co używasz lambd do tworzenia obiektów? Jaka jest tego wyższość na tworzeniem nowego obiektu bezpośrednio w funkcjach get?"
Jesli chodzi o uzywanie lambd do tworzenia obiektow, wynika to (bycmoze z blednego) zalozenia, ze w c++ podobnie jak w php pamiec jest alokowana dla obiektu tworzonego w lambdzie dopiero w momencie odpalenia funkcji, a nie w momencie jej deklaracji. To pozwala tworzyc bardzo uniwersalne kontenery bez negatywnego wplywu na zasoby serwera.

2.3 "...Powiem więcej założę się że kompilator uwaga, zrobił inline i de facto nie będzie żadnej lambdy.?"
Z tym faktycznie nie moge dyskutowac - bo nie mam pojecia jak radzi sobie z tym kompilator - jestem skryptowcem. Zatem po raz drugi bylbym wdzieczny za jakies konstruktywne sugestie pozwalajace to zweryfikowac i ewentualnie zoptmalizowac moj kod.

2.4 "już lepiej było użyć funkcji prywatnej która tworzy nowy obiekt jeśli jest on skomplikowany(tu nie jest) ba co szczególnie dużych obiektach mogło by być jeszcze lepsze niż rozwlekła lambda gdzieś w ciele.."
Tutaj akurat nie moge sie z toba zgodzic co do rozwleklosci lambd w mojej implementacji. Wszystkie beda zajmowaly praktycznie tyle samo miejsca, jedyne co bedzie je roznic to pierwsza linia ciala:
Zatem tak:

this->order = new Order(this->setConfig());

albo w najgorszym przypadku tak:

this->order = new Order(this->setInject1(), this->setInject1(), this->setInject1(), this->setInject1(), this->setInject1());

i wg mnie wyglada to calkiem zgrabnie. Niezaleznie od tego jak zlozona jest kazda injekcjia de facto nie ma to wplywu na zlozonosc kodu funkcji lamba w przeciwienstwie do opcji z funkcja prywatna. Wszystko bowiem "laduje" sie kaskadowo zaczynajac od najnizszego poziomu.

  1. "... Zapytam inaczej jaki jest twój cel"
    Kontener dla DI/IoC zapewniajacy lazy loading.
0

Kontener dla DI/IoC zapewniajacy lazy loading.

A spamiętywanie masz, czy za każdym razem tworzysz nowe instancje? Tzn leniwe ładowanie powinno za pierwszym razem utworzyć obiekt, a w kolejnych odwołaniach nie tworzyć nowych tylko zwracać już ten utworzony.

0

Tak, w ciele gettera zanim odpalam lambde najpierw sprawdzam czy okreslony obiekt jest juz atrybutem kontenera.

0

To moje przeoczenie. Poczatkowo probowalem wykorzystac unique_ptr, ale nie poradzilem sobie z dopasowaniem typu zwracanego z lambdy wiec jak widzisz poszedlem na skroty. Tutaj wlasciwie bylbym Ci wdzieczny za sugestie jak sobie z tym poradzic.

Odpuśc lamby, twórz normalnie w funkcji, z resztą nie tworzysz lambd a std::function a ()[]{} nie zwraca std::function przy przypisaniu.

Wybacz, ale wg mnie caly czas design aplikacji ma sie nijak do budowy contenera DI, wiec moze lepiej nie wracajmy juz do tego watku.

Ma, i to czego szukasz to prawdopodobnie nazywa się dla ciebie wzorzec factory + lazy loading.

Jesli chodzi o uzywanie lambd do tworzenia obiektow, wynika to (bycmoze z blednego) zalozenia, ze w c++ podobnie jak w php pamiec jest alokowana dla obiektu tworzonego w lambdzie dopiero w momencie odpalenia funkcji, a nie w momencie jej deklaracji. To pozwala tworzyc bardzo uniwersalne kontenery bez negatywnego wplywu na zasoby serwera.

Tak złe założenie, wywołasz funkcję to za alokujesz pamięć i to tyle. Dodatkowo std::function nie jest za darmo(np. do poczytania https://stackoverflow.com/questions/33880642/overhead-with-stdfunction )

Tutaj akurat nie moge sie z toba zgodzic co do rozwleklosci lambd w mojej implementacji. Wszystkie beda zajmowaly praktycznie tyle samo miejsca, jedyne co bedzie je roznic to pierwsza linia ciala:

A co powiesz na(z resztą po co ci te this? To c++ ;) )

private:
auto setInject1() { // kod}
// Gdzieś tam tworze ten obiekt
order = new Order(setInject1(), (), setInject1(), setInject1(), setInject1());

Żadnej lambdy, żadnego możliwego obciążenia od std::function.

A teraz potencjalny gong. Tworzę twoją klasę, i pierwsze co wywołuje to
Klasa->getOrder()
Podpowiem, crash.
edit:
co ja pisze, przez te lamby mi się myli, nie będzie crasha.
edit2:
BTW. załóżmy masz obiekt tworzony z 10 DI. Czyli co 10 lambd w konstruktorze zrobionych, 10 pól z std::function, i 10 funkcji wywołujących tworzenie obiektów.

0

A teraz potencjalny gong. Tworzę twoją klasę, i pierwsze co wywołuje to
Klasa->getOrder()
Podpowiem, crash.

Hmm, zasadnicze znaczenie maja w tej chwli moje bezdyskusyjne ograniczenia zwiazane z c++, ale taki kod:

#include "Container.h"

int main()
{	
	Container* container;
		
	container = new Container();

	Order* order;
		
	order = container->getOrder();

	std::cout << order ;

	system("pause");

	std::cout << order->getConfig();

	system("pause");

	return 0;
}

drukuje mi bez problemow odpowiednie adresy :( , a konsola ostatecznie pokazuje tylko to:

'ConsoleApplication1.exe' (Win32): Loaded 'C:\Windows\System32\KernelBase.dll'. Cannot find or open the PDB file.> 
'ConsoleApplication1.exe' (Win32): Loaded 'C:\Windows\System32\msvcp140d.dll'. Cannot find or open the PDB file.
'ConsoleApplication1.exe' (Win32): Loaded 'C:\Windows\System32\vcruntime140d.dll'. Cannot find or open the PDB file.
'ConsoleApplication1.exe' (Win32): Loaded 'C:\Windows\System32\ucrtbased.dll'. Cannot find or open the PDB file.
Application "\??\C:\windows\system32\cmd.exe" found in cache
Application "\??\C:\windows\system32\cmd.exe" found in cache
The program '[16160] ConsoleApplication1.exe' has exited with code 0 (0x0).

To za to wyglada bardzo obiecujaco:

A co powiesz na(z resztą po co ci te this? To c++ ;) )

private:
auto setInject1() { // kod}
// Gdzieś tam tworze ten obiekt
order = new Order(setInject1(), (), setInject1(), setInject1(), setInject1());

Żadnej lambdy, żadnego możliwego obciążenia od std::function.

I tu mnie masz. Sprawdze to sobie i dam jutro znac czy dziala :), tymczasem dobranoc i dzieki za wsparcie.

BTW. załóżmy masz obiekt tworzony z 10 DI. Czyli co 10 lambd w konstruktorze zrobionych, 10 pól z std::function, i 10 funkcji wywołujących tworzenie obiektów.

Na to wychodzi :)

0
revcorey napisał(a):

A co powiesz na(z resztą po co ci te this? To c++ ;) )

private:
auto setInject1() { // kod}
// Gdzieś tam tworze ten obiekt
order = new Order(setInject1(), (), setInject1(), setInject1(), setInject1());

Ok wyglada to w sposob nastepujacy:

// Container.h

#pragma once

#include "Config.h"
#include "Order.h"
#include "Structure.h"

class Container
{
private:
	auto setConfig() 
	{
		Config* config = new Config();

		return config;
	};
	
	auto setOrder()
	{
		Order* config = new Order(setConfig());

		return config;
	};

	auto setStructure()
	{
		Structure* structure = new Structure(setConfig());

		return structure;
	};

private:	
	Config* config;
	Order* order;
	Structure* structure;
	
public:
	Container();
	~Container();

	Config* getConfig();
	Order* getOrder();
	Structure* getStructure();
};
// Container.cpp

#include "stdafx.h"
#include "Container.h"

Container::Container()
{
}

Container::~Container() 
{
}

Config* Container::getConfig()
{
	if (config) {
		return config;
	}

	config = setConfig();

	return config;
}

Order* Container::getOrder()
{
	if (order) {
		return order;
	}

	order = setOrder();

	return order;
}

Structure* Container::getStructure()
{
	if (structure) {
		return structure;
	}

	structure = setStructure();

	return structure;
}

Mam z tym dwa problemy.

  1. Kazda subinjekcja "config" w przypadku obiektow "order" oraz "structure" powoduje nadpisanie i nie za bardzo mi to pasuje bo prawde mowiac akutrat w tym przypadku preferuje pojedynczy instancje. Trudno to osiagnac z powodu braku dostepu do atrybutu kontenera w ciele funkcji set.
  2. Zastanawia mnie niezgodna z konwencja koniecznosc umieszczenia ciala funkcji set w header'ze kontenera - z drugiej strony nie moglem jej po prostu zadeklarowac w header'ze i opisac body w cpp :/ ...
  3. Dlaczego jednoznaczy zwracany typ w funkcji set jest deklarowany jako auto?

Z drugiej strony klasyczne rozwiazanie wydaje sie byc odpowiednie biorac pod uwage zarowno kontrole nad wszystkimi obiektami jak i tez konwencje:

// Container.h

#pragma once

#include "Config.h"
#include "Order.h"
#include "Structure.h"

class Container
{	
private:	
	Config* config;
	Order* order;
	Structure* structure;

public:
	Container();
	~Container();

	Config* getConfig();
	Order* getOrder();
	Structure* getStructure();
};
// Container.cpp

#include "stdafx.h"
#include "Container.h"

Container::Container()
{
}

Container::~Container() 
{
}

Config* Container::getConfig()
{
	if (config) {
		return config;
	}

	config = new Config();

	return config;
}

Order* Container::getOrder()
{
	if (order) {
		return order;
	}

	order = new Order(getConfig());

	return order;
}

Structure* Container::getStructure()
{
	if (structure) {
		return structure;
	}

	structure = new Structure(getConfig());

	return structure;
}

ale zastanawiam sie dlaczego zaproponowales poprzednie podejscie :/ ...

1

ale zastanawiam sie dlaczego zaproponowales poprzednie podejscie :/ ...

Ja mówię o klasycznym podejściu, to co dałeś u góry na początku to taki mały potworek. Na myśli miałem to co wrzuciłeś w drugiej części postu, tylko skróciłem sobie drogę i dałem auto setInject1() który w zamyśle był funkcją typu getOrder czy inną taką jak w tym drugim przykładzie co przytoczyłeś. Chodziło mi właśnie o wyeliminowanie lambd i std::function.

Teraz zastanów się nad użyciem std::shared_ptr :)

edit:
jak nie potrzebujesz destruktora i konstruktora to nie musisz ich definiować, albo możesz ewentualnie użyć = default przy deklaracji. nie trzeba ponawiać private, pod nim mogą jednocześnie być funkcje i zmienne.

0

Dzieki i do nastepnego :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1