Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Controller - co lepsze?
Forum PHP.pl > Forum > PHP > Pro > Archiwum Pro
Prph
Witam,

Co jest lepszym rozwiazaniem? Singletone, czy przekazywanie go obiektom?
Dla przykladu:

  1. <?php
  2. // 1 rozwiazanie, klasa akcji:
  3.  
  4. public function execute(IController $oController)
  5. {
  6. $oController->.......;
  7. }
  8.  
  9. // 2 rozwiazanie, klasa akcji:
  10.  
  11. public function execute()
  12. {
  13. $oController = Controller::getInstance();
  14. $oController->.......;
  15. }
  16. ?>


Adrian.
Ludvik
Lepiej przekazywać jako argument funkcji. Pytanie tylko - po co przekazywać obiekt kontrolera akcjom? Nigdy nie spotkałem się z taką potrzebą, dlatego mnie to dziwi.
Prph
Dlaczego? Zobacz Mojavi - tam wszedzie sie tak robi.
Dlaczego Akcjom? Kontroler jest otoczka dla calej aplikacji. Kontroler udostepnia kontekst, ten zas request.

Siadam do kodu, jednak zmienie na singletone. dotychczas caly framework mial zorganizowany na zasadzie przekazywania kontrolera obiektom. Ale zauwazylem, ze moze to powodowac bledy - chociazby typu referencja, kopia obiektu. Oczywistym jest ze powinny byc to wszedzie referencje.

Adrian.
anas
Hej.

Wydaje mi się że dużo lepiej jest przekazywać obiekt w konstruktorze po typie to daje możliwość podmiany implementacji danego interfejsu na inny - czyli jednym słowem zwiększa elastyczność kodu. Pracowanie na instancji tworzonej wewnątrz innej klasy powoduje że wymuszasz konkretny typ i w razie potrzeby modyfikować już istniejący kod - a nie o to chodzi w OOP.

Pozdrówka

anas
Prph
Z drugiej strony czesc obiektow otrzymuje w kontruktorze kontroler i nalezy sie do niego odwolac przez powiedzmy $this->_getController();. Czesc natomiast dostaje w wywolaniu metody execute().
Z nowu nie zawsze potrzebuje tego kontrolera, a jednak jest przekazywany w funkcji.

Najbardziej denerwuje mnie ta roznica w przekazywaniu w kontruktorze albo w execute();

No i na sam koniec - framework ma byc dla mnie maksymalnie wygodny.
orson
witam

Jestem w trakcie kończenia innego rozwiązania. Jest klasa która jest managerem instancji ... zwraca obiekt wg jego nazwy oraz nazwy instancji (nazwa nie jest wymagana - może być utworzona automatycznie dla singletona - wtedy wiadomo że nie trzeba znać instancji, wystarczy nazwa klasy) ... może automatycznie tworzyć singletony (oczywiście jak ktoś z palca wywoła klasę która ma publiczny interfejs to dostanie obiekt ) lub nie pozwalać by dana klasa była używana jako taki ( za każdym razem zwraca nową instancję) ... po ustawieniu dodatkowych pól wewnątrz klasy (nie są wymagane) klasa tworzona może współpracować z managerem ... użycie odbywa się na zasadzie:
  1. <?php
  2. $obj = aeClassManager::getInstance('someSingletonClass', 'instanceName', true, array('param'=>'value'));
  3. $obj2 = aeClassManager::getInstance(array('someSingletonClass', 'methodToGetSingletonInstance'), 'instanceName', true, array('param'=>'value'));
  4. ?>

ten manager nie jest związany z jakąś szczególną klasą ale może być pomocny przy dużej ilości obiektów (duża aplikacja intranetowa - w tym celu go opracowuje) ...

jeżeli uda mi się dziś skończyć to udostępnie w dziale gotowe skrypty może się komuś przyda ...

pozdrawiam
Ludvik
Skoro tak musisz robić, to bym się skłaniał do przekazywania albo przez konstruktor albo przy wywołaniu samej akcji. Którą metodę wybierzesz, to zależy od Ciebie.

Z drugiej strony jak dla mnie, to strasznie skomplikowany jest kontroler w mojavi. Nie prościej wyodrębnić wszystkie dane kontekstu do osobnego obiektu?
Prph
Owszem, kontroler Mojavi ma troche skomplikowanego kodu winksmiley.jpg
Dlatego ja piszac wlasny framework sporo usunalem. Rozwiazanie z Mojavi ma jedna zalete, ktora mi sie podoba - kontroler jest sercem calej aplikacji. To jak skrzyzowanie z ktorego mozna pojechac w kazde miejsce.

Dodalem jeszcze metody zwracania loggera oraz bazy danych.

Adrian.
anas
@orson: czy Twoje rozwiazanie to cos na zasadzie IoC? Jeśli tak to chętnie bym popatrzył jak rozwiązałeś przechowywanie relacji między obiektami. Jeśli nie to i tak ciekawi mnie Twoje podejście, czekam na skrypcik.

pozdrówka

anas
orson
witam

Niestety nie jest to IoC :/ może kiedyś winksmiley.jpg
Zapraszam do zapoznania się z kodem:
Temat: klasa Instance Manager zarzadzanie instacjami obiektow

pozdrawiam
bigZbig
@orson - byłbym wdzięczny gdybyś przedstawił mi zalety używania Twojego menagera do pobierania obiektu singletona w stosunku do zwykłego getInstance

-- edit --

@Prph - a nie powinno byc na odwrot, ze to akcje przekazuje sie do kontrolera, a nie kontroler do akcji?
Ludvik
Podpisuję się pod drugim pytaniem bigZbiga. W ten sposób tworzysz zbędne zależności pomiędzy akcjami a kontrolerem, kiedy akcja potrzebuje tylko dane z kontekstu, który można przekazać jako argument. Sam stosuję taką logikę i jakoś nigdy nie trafiłem na sytuację, która by wymagała zmiany mojego podejścia.
Cysiaczek
W zasadzie, to ja też chciałbym przeczytac opinie na temat "kto komu i co powinien przekazywac w tym ukladzie" smile.gif
orson
witam

@bigZbig: to jest rozwiązanie generalne. Pozwala ujednolicić sposób pobierania wszystkich obiektów ... nie tylko do singletownów ... nieraz wewnątrz metody musisz się odwołać do dblayera który nie może być singletonem (2 połączenia do 2 różnych baz w aplikacji) i zawsze chcesz dostawać określony obiekt ... podajesz nazwę klasy i instancję i zawsze dostaniesz ten sam obiekt co poprzednio ...

pozdrawiam
anas
Hej.

Również uważam, że bez sensu jest tworzyć sztywne powiązania między obiektami - lepszym i dającym większe pole manewru jest rozwiązanie pozwalające bez ingerencji w już stworzony kod, manipulować istniejącymi obiektami. Właśnie dlatego pytałem o IoC. Generalnie ciekawym podejściem jest AOP - czyli programowanie aspektowe. Pokrótce chodzi o to aby do już istniejąycych obiektów, bez ingerencji w kod dorzucać nową funkcjonalność, podmieniać, wykonywać coś przed wywołaniem, lub po wywołaniu metody itd.

Przykład zastosowania:

Mamy sklep internetowy - wyliczamy cenę na podstawie kilku obiektów - cena bazowa -> podatek -> waluta = cena za produkt. Załóżmy że klient stwierdza po 3 miesiącach że chciałbym na wybrane produkty dorzucać promocję. Wtedy dzieki możliwości zdefiniowania funkcjonalności która wykona się ma obiekcie product, np.

$product->getProductPrice(Tax $taxObject, Currency $currencyObject, ProductDAO $productDAO);

zaraz po zwróceniu przez powyższą metodę wartości -> (Invoke.AfterReturning - definiuje kiedy ma sie wykonac nowa funkcjonalnosc). Po tym wszystkim należałoby zdefiniować dla jakich metod ma się to wykonać - np. Include("getProductPrice*") i mamy powiazania - tym wszystkim musi zarzadzac specjalnie przygotowane narzedzie.

W chwili obecnej w php ze wgzledu na trudna implementacje nie spotkalem sie z takim czyms - ale to ciekawy pomysl, ktory warto by bylo poruszyc w przyszlosci - to daje naprawde ogromne mozliwosci manipulowania kodem, bez koniecznosci ingerowania w juz istniejacy. Co myślicie o takim podejściu?

pozdrawiam

anas
NuLL
Cytat
Co myślicie o takim podejściu?
Podejscie fajne, ale raczej jestesmy bez szans jesli chodzi o implementacje w php. Musialbylo by to chyba dosc absudarlnie wygladac gdyz trzeba by jakoby sprawdzac kazde operacje co spowoduje narzut kodu wiekszy niz to wszystko warte.

Wracajac do tematu - przekazyc kontroler do akcji. To tak jakby umieszczac generala w oddziale, dac Kalacha to reki i kazac walczyc, a on ma przeciez dowodzic winksmiley.jpg
Cysiaczek
Wydaje mi się, że samo przekazanie obiektu kontrolera nie jest złe, bo w sumie chodzi o to, aby zbudowac aplikacje na kontrolerze. Z drugiej strony ciekwaszym i bardziej elastycznym rozwiązaniem będzie utworzenie klasy bramy udostępniającej obiekt kontrolera.

w teorii
user_code --> interface (obiekt lub statycznie) --> controller

Oczywiście można stworzy taki interfejs bezpośrednio w klasie, ale byłoby to zbytnie sprzęganie.
Wilk syty i owca cała. W każdym razie tak mi się wydaje.

W drugim przypadku (przekazywanie akcji kontrolerowi) możemy napotkac w dalszej perspektywie pewne trudne do przewidzenia problemy związane z budową samej akcji, która musi by zbudowana tak, aby rozumiała kontroler i była podatna na testowanie.

Ehh. To podobnie jak z prawami użytkowników - trzymac je razem z uzytkownikiem, czy w jakimś centralnym miejscu...a moze jeszcze inaczej.
bigZbig
Myślę, że tu jest generalnie problem z nazewnictwem bo kontroler u Prph'a jest jak podejrzewam specyficznym obiektem typu DTO (Data Transfer Object) służącym jedynie do transportowania informacji.

@orson - poczytaj na temat rejestru obiektów w Zend Frameworku. Moim zdaniem chyba chcesz przekombinować swojego menadżera.
Prph
Cytat(bigZbig @ 3.07.2006, 09:47 ) *
@Prph - a nie powinno byc na odwrot, ze to akcje przekazuje sie do kontrolera, a nie kontroler do akcji?


To zalezy jak napiszesz framework.

Moj kontroller przypomina nieco kontroler Mojavi. Stanowi on "otoczke" calej aplikacji. Kontroler wykonuje akcjie. Te natomiast potrzebuja dostepu do danych post i get trzymanych w Kontekscie. A to kontroler posiada ow kontekst.

Jest to calkiem dobre rozwiazanie - wygodne. Wszystko co potrzebujesz jest w kontrolerze.

Adrian.
NuLL
Prosty quiz winksmiley.jpg
  1. <?php
  2.  
  3. class user
  4. {
  5. private static $oInstance=null;
  6.  
  7. private $id=0,$lang='';
  8.  
  9. public static function getInstance()
  10. {
  11. if(self::$oInstance==null) self::$oInstance=&new user();
  12.  
  13. return self::$oInstance;
  14. }
  15.  
  16. private function __construct()
  17. {
  18. $userData=httpContext::session()->getAttribute('user');
  19.  
  20. if($userData{1})
  21. {
  22. $userData=unserialize($userData);
  23.  
  24. $this->id=(int)$userData['id'];
  25. $this->lang=$userData['lang'];
  26. }
  27. else
  28. {
  29. $this->id=0;
  30. $this->lang=DEFAULT_LANG;
  31. }
  32. }
  33.  
  34. public function isLoggedIn()
  35. {
  36. return ($this->id>0);
  37. }
  38.  
  39. public function login($login,$pass)
  40. {
  41. $auth=authenticator::getInstance();
  42.  
  43. $id=$auth->authenticate($login,$pass);
  44.  
  45. if($id>0)
  46. {
  47. $this->id=$id;
  48. $this->lang=DEFAULT_LANG;
  49. return 1;
  50. }
  51. else
  52. {
  53. return 0;
  54. }
  55. }
  56.  
  57. public function save()
  58. {
  59. httpContext::session()->setAttribute('user',serialize(array('id'=>$this->id,'lang'=>$this->lang)));
  60. }
  61.  
  62. public function getLang()
  63. {
  64. return $this->lang;
  65. }
  66.  
  67. public function logout()
  68. {
  69. $this->id=0;
  70. }
  71. }
  72.  
  73. ?>

I teraz w jaki sposob majac kontekst w kontrolerze obsluzysz taka klase jesli ona nie ma singletonu questionmark.gif Bo wg mnie sztuka dla sztuki ( absurdem ) jest przekazywanie kotrolera z kontekstem do kazdej klasy withstupidsmiley.gif
splatch
Ej, panowie, ale chyba znacie definicje Front Controllera prawda? Odpowiedzialność ciążąca na akcji powinna się sprowadzać tylko do wykonania pewnej operacji, cytując Fowlera:
Cytat("Patterns of Enterprise Application Architecture")
In a complex Web site there are many similar things you need to do when handling a request. These things include security, internationalization, and providing particular views for certain users. If the input controller behavior is scattered across multiple objects, much of this behavior can end up duplicated. Also, it's difficult to change behavior at runtime.

The Front Controller consolidates all request handling by channeling requests through a single handler object. This object can carry out common behavior, which can be modified at runtime with decorators. The handler then dispatches to command objects for behavior particular to a request.


Padły tu tez jakies zdania o IoC, w Springu, który korzysta w pełni z IoC jest element, który pełni funkcję front controllera - jest nim DispatcherServlet (pakiet spring.web.servlet).
Prph
Cytat(NuLL @ 3.07.2006, 16:52 ) *
Bo wg mnie sztuka dla sztuki ( absurdem ) jest przekazywanie kotrolera z kontekstem do kazdej klasy withstupidsmiley.gif

Nie przekazuje go. Ale jezeli akcja go potrzebuje, to bierze go sobie poprzez Controller::getInstance();

Kontekst trzymam w kontrolerze, bo kontroler traktuje jak OTOCZKE aplikacji. I ta OTOCZKA ma kazdemu pozwolic wziac sobie co zechce. Jezeli potrzebuje parametru GET, niech poprosi o kontekst, a ten juz posiada HttpRequest.

Oczywiscie mozna zrobic Kontekst osobna klasa. Ale wtedy znowu byloby Context::getInstance->getRequest()...

Jak juz ktos powiedzial - ile osob, tyle roznych pomyslow. A ten pomysl nie jest akurat w pelni moj. Tak bylo bodajze w Mojavi, a i z Phienda2 cos zaczerpnalem.

Adrian.
Bora
Może warto zastosować tutaj rozwiązanie z javy? Np inversion of control (injection)
Kod
[Class1]
dep=Class2,Class4
singleton=true
[Class2]
singleton=true
[Class3]
singleton=true
[Class4]
singleton=true


Kod:
  1. <?php
  2. function getmicrotime(){
  3.  list($usec, $sec) = explode(" ",microtime());
  4.  return ((float)$usec + (float)$sec);
  5. }
  6. function dump($val){
  7. /*
  8. echo '<pre>';
  9. var_dump($val);
  10. echo '</pre>';
  11. */
  12. }
  13. function endTime($startTime){
  14. return getmicrotime() - $startTime;
  15. }
  16.  
  17. class Manager{
  18. protected $config = NULL;
  19. protected $contener = array();
  20. public function __construct(){
  21. $this->config = parse_ini_file("config.ini", true);
  22. }
  23. public function create($name){
  24. if(isset($this->contener[$name])){
  25. return $this->contener;
  26. }
  27. if(!isset($this->config[$name])){
  28. throw new ClassNotExistException($name);
  29. }
  30. $class = new $name();
  31. if(isset($this->config[$name]['dep'])){
  32. $deps = explode(',', $this->config[$name]['dep']);
  33. foreach($deps as $dep){
  34. $method = 'set'.$dep;
  35. $class->$method($this->create($dep));
  36. }
  37. }
  38. if(!empty($this->config[$name]['singleton'])){
  39. $this->contener[$name] = $class;
  40. }
  41. return $class;
  42. }
  43. }
  44.  
  45. class Class1{
  46. private $c2 = NULL;
  47. private $c4 = NULL;
  48. public function setClass2($c2){
  49. $this->c2 = $c2;
  50. }
  51. public function setClass4($c4){
  52. $this->c4 = $c4;
  53. }
  54. private static $_instance ; 
  55. public static function GetInstance() { 
  56. if (!isset(self::$_instance)){ 
  57. self::$_instance = new Class1() ; 
  58. } 
  59. return self::$_instance; 
  60. }
  61. }
  62. class Class2{
  63. private static $_instance ; 
  64. public static function GetInstance() { 
  65. if (!isset(self::$_instance)){ 
  66. self::$_instance = new Class2() ; 
  67. } 
  68. return self::$_instance; 
  69. }
  70. }
  71. class Class3{
  72. private static $_instance ; 
  73. public static function GetInstance() { 
  74. if (!isset(self::$_instance)){ 
  75. self::$_instance = new Class3() ; 
  76. } 
  77. return self::$_instance; 
  78. }
  79. }
  80. class Class4{
  81. private static $_instance ; 
  82. public static function GetInstance() { 
  83. if (!isset(self::$_instance)){ 
  84. self::$_instance = new Class4() ; 
  85. } 
  86. return self::$_instance; 
  87. }
  88. }
  89. class ClassNotExistException extends Exception{};
  90. $startTime = getmicrotime();
  91. $manager = new Manager();
  92. for ($i = 0; $i < 100; $i++) {
  93. try{
  94. dump($manager->create('Class2'));
  95. dump($manager->create('Class4'));
  96. dump($manager->create('Class1'));
  97. }catch(ClassNotExistException $e){
  98. dump($e->getMessage());
  99. }
  100. }
  101. echo "Czas:".endTime($startTime).' (menager)<br/>';
  102.  
  103. $startTime = getmicrotime();
  104. for ($i = 0; $i < 100; $i++) {
  105. $c2 = new Class2();
  106. $c4 = new Class4();
  107. $c1 = new Class1();
  108. $c1->setClass2($c2);
  109. $c1->setClass4($c4);
  110. dump($c2);
  111. dump($c4);
  112. dump($c1);
  113. }
  114. echo "Czas:".endTime($startTime).' (single)<br/>';
  115.  
  116. $startTime = getmicrotime();
  117. for ($i = 0; $i < 100; $i++) {
  118. $s2 = Class2::GetInstance();
  119. $s4 = Class4::GetInstance();
  120. $s1 = Class1::GetInstance();
  121. $c1->setClass2($c2);
  122. $c1->setClass4($c4);
  123. dump($c2);
  124. dump($c4);
  125. dump($c1);
  126. }
  127. echo "Czas:".endTime($startTime).' (instance)<br/>';
  128. ?>


Co ciekawe działa podobnie szybko jak za każdym razem używać jawnie wywołania instancji, jedynie troszke dłużej trwa wczytywanie ini.

Czasy u mnie na lh:
Czas:0.0023281574249268 (menager)
Czas:0.0016329288482666 (single)
Czas:0.0024371147155762 (instance)


W javie (spring) działa to pięknie ale php z racji tworzenia od 0 za każdym wywołaniem czasami może warto poprostu nie przekombinować i tak wyszukane rozwiązania zostawić w spokoju.
Nie korzystałem z tego jeszcze w żadnym projekcie bo ostatnio nie robie wiele w php i stworzyłe tylko z ciakawości czy sie wogóle uda.
Ludvik
Hm... muszę przynać, że ciekawie to wygląda i nawet narzut czasowy jest do zaakceptowania. Sam będę musiał przemyśleć zastosowanie czegoś takiego u siebie.
Sh4dow
Moim zdaniem wszystko zalezy od konstrukcji aplikacji. Jesli chodzi o jakies schematy obiektow ktore beda odpalane, czyli obiekty maja takie same nazwy metod to mozna przekazywac. Ale ogolnie ja sie przykladam do przekazywania instancji. I mozna to robic w roznoraki sposob. Albo poprzez Magazyn albo przez singletone. Ja uzywam singletone. Jest to o tyle wygodne ze jesli chce przekazac instancje obiektu w parametrze, to musze miec ja juz wywolana.
Jesli nie stworzylem obiektu jakies klasy bo nie była potrzebna wczesniej, to musze stworzyc obiekt tylko po to zeby go przekazac. Wiec po co ? skoro w metodzie ktora wymaga obiektu moge odwolas sie do getInstance() i juz mam. Sadze ze jest to wygodniejsze. Przy jakichkolwiek zmienach nie musze pamietac o tym ze wymagam jakiegos obiektu w atrybucie.
Przy okazji, jesli mam grupe roznych modeli, i potrzebuje pobrac 3 modele do akcji, to bedziesz je przekazywac w atrybutach ? Dziwnie by to wygladało.
Cysiaczek
Ale czy w tym wypdku o którym mówisz Sh4dow, to nie może się zdazyć tak, że jeśli będziesz miał dwie klasy (potrzebujące tego samego obiektu) z metodami getInstance(), to każda powoła do życia obiekty tego samego typu, ale różne?
bigZbig
@Cysiaczek - jesli to jet klasa Singleton (poprawnie zaimplementowana) to w ramach jednego wywolania nie ma szansy na powolanie do zycia wiecej niz jednej jej instancji.
Cysiaczek
Tak. Zdaję sobie z tego sprawę - mimo to zastanawiam się nad samym mechanizmem jawnego wywołania instancji w ciele klasy. przy singletonie jest ok, ale gdzie indziej? Po prostu pytam.
bigZbig
Ale w czym problem? Zalezy od ciebie czy przekazesz obiekt do klasy czy wywolasz go wewnatrz niej. Wzorzec budowniczego np. zaklada z definicji tworzenie instancji wewnatrz klasy bo klasy te maja wlasnie takie przeznaczenie.

Klopot z wywolywaniem Singeltona wewnatrz klasy polega na tym ze wywolujesz konkretna klase. Nie jej rozszerzenie np, ale wlasnie te klase. No chyba, ze przekazujesz do swojej klasy nazwe klasy bedacej singletonem, ale to juz chyba lepeiej przekazac obiekt.
Cysiaczek
Aha. Własnie do tego zmierzałem - po co "psuć" klasę trzymając tam nazwę innej klasy. Lepiej chyba od razu przekazać obiekt. Zastanawia mnie jeszcze coś takiego.

  1. <?php
  2. class somethingCool{
  3. .../
  4. private $_classMagicBox; //array
  5.  
  6. public function assignClassName($className){}
  7.  
  8. private function getInstance(){}
  9.  
  10. .../
  11.  
  12. }
  13. ?>



Czy to byłoby dobre do wywołania/utrzymywania potrzebnych pierdół?
splatch
Singleton jest zły. Dlaczego? Jest to niemalże to samo co global. Singleton jest po to by mieć 1 instancje a nie po to by nie przekazywać obiektów!
Inversion of Controll idzie dalej, zapewnia obsługę obiektów. Kontener IoC możę się zachowywać jak fabryka i wypluwać kolejne instancje albo trzymać tylko 1 instancje. Dostęp do usług (np połączenia z bazą) jest tylko w tych miejscach, gdzie przekazaliśmy instancję kontenera.
Bora
podejrzewam że IoC może sie nie podobać osobom które mają np pare klass obsługujących cały kod. Np klassa User powinna mieć dostęp do obketku Groups ale jeśli ktoś trzymałby w tej klasie dodatkowo tworzenie miniaturki usera, zarządznaie wiadomościami i jeszcze coś wówczas rzeczywiście może być sporo niepotrzebnych stworzonych obiektów. W javie lazy-init pomaga gdzie obiekt jest tworzony dopiero gdy jest potrzebny.

Cytat
Przy jakichkolwiek zmienach nie musze pamietac o tym ze wymagam jakiegos obiektu w atrybucie.

Takie rzeczy powinny wyjść w testach.

Tak jak pisze Splatch IoC zapewnia większe bezpieczeństwo, kodu. Pozatym klassy są mniej z sobą powiązane przez co w przyszłości prościej można je wykorzystać w innym projekcie.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2024 Invision Power Services, Inc.