Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [ABSTRACT][INTERFACE][STATIC][SINGLETON] Klasa abstrakcyjna, interfejs metody statyczne
Forum PHP.pl > Forum > PHP > Object-oriented programming
vokiel
Witam, przeczytałem zawartość działu OOP na temat klasa abstrakcyjna + interfejs. Jednak w większości nie był poruszany temat metod statycznych, a tym bardziej singletona.

Zaplanowałem sobie taki mały systemik logowań zdarzeń. Chciałem oprzeć go o klase abstrakcyjną + interfejs, a przy okazji chciałem użyć do tego celu singletona, aby późniejsze wywołanie mogło sprowadzić się do:
  1. <?php
  2. dbLog::write()->error();
  3. dbLog::write()->warning();
  4. ?>

Oto co próbowałem zrobić, jednak to nie działa, dostaję błąd:
Kod
Fatal error: Can't inherit abstract function logInterface::write() (previously declared abstract in log) in C:\xampp\htdocs\test.php on line 10


  1. interface logInterface {
  2. public static function write();
  3. public function error();
  4. public function warning();
  5. public function notice();
  6. }
  7. abstract class log implements logInterface{
  8. protected static $_instance = null;
  9. protected function __construct(){}
  10. protected function __clone(){}
  11.  
  12. abstract public static function write();
  13.  
  14. abstract public function error();
  15. abstract public function warning();
  16. abstract public function notice();
  17. }
  18. class fileLog extends log implements logInterface {
  19. public static function write(){
  20. return is_null(parent::$_instance) ? parent::$_instance = new fileLog() : parent::$_instance;
  21. }
  22. public function error(){echo 'fileLog - error';}
  23. public function warning(){echo 'fileLog - warning';}
  24. public function notice(){echo 'fileLog - notice';}
  25. }
  26. class dbLog extends log implements logInterface {
  27. public static function write(){
  28. return is_null(parent::$_instance) ? parent::$_instance = new dbLog() : parent::$_instance;
  29. }
  30. public final function error(){echo 'dbLog - error';}
  31. public final function warning(){echo 'dbLog - warning';}
  32. public final function notice(){echo 'dbLog - notice';}
  33. }


Myślałem o przeniesieniu metody write() do klasy abstrakcyjnej log, zrobienie z niej static (czyt final static)

Ewentualnie mógłbym zrobić w każdej metodzie error, warning, notice odwoływanie się do singletona tak, żeby użycie uprościć do
  1. <?php
  2. dbLog::error();
  3. fileLog::notice();
  4. ?>


Macie jakieś pomysły jak to rozwiązać?
Crozin
Po co deklarujesz w Log abstrakcyjne metody, które i tak są wymuszone przez interface?
vokiel
A no właśnie, tak to jest jak się za długo siedzi nad jednym kodem i go przerabia co chwila na różne sposoby.
W pierwszej wersji miało być bez interfejsu, ale później wymyśliłem, żeby go jednak dodać.

Chciałem przenieść metodę write do klasy abstrakcyjnej. Jednak nie wiem jak ugryźć w niej poprawne tworzenie nowego obiektu dla klas potomnych. Nie wiem czy jest poprawnie (ma sens) taki sposób: (co prawda skraca zapis użycia)
  1. <?php
  2. interface logInterface {
  3. public function error($err);
  4. public function warning();
  5. public function notice();
  6. }
  7. abstract class log implements logInterface{
  8. protected function __construct(){}
  9. protected function __clone(){}
  10.  
  11. public static function write(logInterface $log){
  12. return is_null($log->_instance) ? $log->_instance = new $log() : $log->_instance;
  13. }
  14. }
  15. class fileLog extends log implements logInterface {
  16. protected static $_instance = null;
  17. protected $logi;
  18.  
  19. public function __construct(){
  20. parent::write($this);
  21. }
  22. public function error($err){self::$_instance->logi .= "\n".$err; echo self::$_instance->logi;}
  23. public function warning(){echo 'fileLog - warning';}
  24. public function notice(){echo 'fileLog - notice';}
  25. }
  26. class dbLog extends log implements logInterface {
  27. protected static $_instance = null;
  28. protected $logi;
  29.  
  30. public function __construct(){
  31. parent::write($this);
  32. }
  33. public function error($err){self::$_instance->logi .= "\n".$err; echo self::$_instance->logi;}
  34. public function warning(){echo 'dbLog - warning';}
  35. public function notice(){echo 'dbLog - notice';}
  36. }
  37.  
  38. //Użycie
  39. fileLog::error('1');
  40. dbLog::error('2');
  41. dbLog::error('3');
  42.  
  43. // Daje w wyniku (czyli ok):
  44. /*
  45. 1
  46.  
  47. 2
  48.  
  49. 2
  50. 3
  51. */
  52.  
  53. ?>


Myślałem na początku, że nie da się utworzyć wspólnej metody dla singletona w klasie abstrakcyjnej.
Zmienna $_instance przechowująca obiekt musi być przecież inna dla fileLog, a inna dla dbLog. Zatem zmienne te trzeba tworzyć dla klas dziedziczących oddzielnie, i na nich operować, bo inaczej $_instance przechowywałaby obiekt pierwszej klasy która go wywołała. Nie wiem co ja chciałem zrobić, singletona na klasę abstrakcyjną sciana.gif Ale jak przeniosłem te zmienne do klas dziedziczących, i odwoływałem się do nich w klasie rodzica okazało się, że się da. Tylko czy może lepiej zrobić tak jak poniżej?
  1. <?php
  2. interface logInterface {
  3. public static function write();
  4. public function error();
  5. public function warning();
  6. public function notice();
  7. }
  8. abstract class log implements logInterface{
  9. protected function __construct(){}
  10. protected function __clone(){}
  11. }
  12. class fileLog extends log implements logInterface {
  13. protected static $_instance = null;
  14. protected $logi;
  15.  
  16. public static function write(){
  17. return is_null(self::$_instance) ? self::$_instance = new fileLog() : self::$_instance;
  18. }
  19. public function error(){self::write()->logi .= "\nfileLog - error"; echo self::write()->logi;}
  20. public function warning(){echo 'fileLog - warning';}
  21. public function notice(){echo 'fileLog - notice';}
  22. }
  23. class dbLog extends log implements logInterface {
  24. protected static $_instance = null;
  25. protected $logi;
  26.  
  27. public static function write(){
  28. return is_null(self::$_instance) ? self::$_instance = new dbLog() : self::$_instance;
  29. }
  30. public function error(){self::write()->logi .= "\ndbLog - error"; echo self::write()->logi;}
  31. public function warning(){echo 'dbLog - warning';}
  32. public function notice(){echo 'dbLog - notice';}
  33. }
  34. ?>
Crozin
Możesz jeszcze powiedzieć czy dbLog i fileLog używane są zamiennie (tj. programista decyduje się na używanie jednego albo drugiego) czy równolegle (tj. używane jest w jednym skrypcie zarówno jedno jak i drugie)? Wydaje mi się, że to pierwsze, ale jakbyś mógł wyjaśnić wszystko.

Swoją drogą... na Twoim miejscu zastanowiłbym się czy nie lepsze byłoby dobieranie się do obiektu Log przy pomocy np. kontekstu niż robić z niego singletona.

Nie ma też sensu robić trzech osobnych metod do zapisywania. Wystarczy jedna, z dodatkowym parameterm określającym "poziom":
  1. $log; //instancja naszego loggera
  2.  
  3. $log->log('my event', Log::NOTICE);
  4. $log->log('my event', Log::WARNING);
  5. //itp
Co do szkieletu klas...
  1. interface Logger{
  2. public function log($msg, $level);
  3. }
  4.  
  5. abstract class Log implements Logger{
  6. const NOTICE = 0;
  7. const WARNING = 1;
  8. const ERROR = 2;
  9. const SOMETHING = 4;
  10.  
  11. protected $logs = array();
  12. }
  13.  
  14. class DatabaseLog{
  15. public function log($msg, $level){
  16. //...
  17. }
  18. }
  19.  
  20. class FileLog{
  21. private $fileHandle;
  22.  
  23. public function __construct(){
  24. $this->fileHandle = fopen('./my/log/file');
  25. }
  26.  
  27. public function log($msg, $level){
  28. //...
  29. }
  30. }
vokiel
@Crozin dzięki wielkie za zainteresowanie.

Chcę zrobić dość uniwersalny system logów, będzie wykorzystywany moduł plikowy, jak id db, dlatego chciałem mieć to razem, z możliwością łatwego przełączania. Na razie bardziej zamiennie, ale z czasem może mi się coś odmienić. Poza tym zawsze przyda się mała wprawka bardziej obiektowego php winksmiley.jpg

Zrobiłem 3 metody zapisywania, aby później skrócić użycie. Co prawda zastanawiałem się też nad jedną metodą z flagami - w poprzedniej klasie logów tak miałem, jednak czasem zdarzało mi się niechcący wybrać nie tą flagę (wtedy jeszcze miałem 2 binarnie: 0 i 1). Później w podglądzie logów miałem nieodpowiednie kolorowanie.

Teraz chcę po prostu sprawdzić inny sposób, zobaczyć jak to się będzie sprawdzało. Ta wersja wydaje się być wygodniejsza, szybciej i jaśniej jest napisać
  1. log::error('widaomo, że błąd');
  2. // niż
  3. log->log("to jest błąd",log::ERROR);

Rozbudowy, czy dodatkowego rozszerzania tych klas, za bardzo nie widzę. Nawet jeśli, to zawsze można zedytować jedną klasę abstrakcyją i powinno bez problemu działać. Zdaję sobie sprawę, że jest to trochę przerost formy nad treścią, ale nie raz się robi coś nieekonomicznego dla wygody:)

Przewiduję, że te 3 metody będą odwoływały się do wspólnej jednej prywatnej, z tą tylko różnicą, że będą ustawiać inną flagę. Czyli zamiast trzech flag i jednej metody, są trzy metody (może 4) i nie ma flag biggrin.gif

Mam nadzieję, że będzie się później tego wygodniej używało winksmiley.jpg A jeśli nie, to skorzystam z Twojego schematu smile.gif

Pozdrawiam, i dziękuję za pomoc smile.gif
Crozin
Pamiętaj, że zawsze możesz ustawić domyślny poziom (ten najczęściej stosowany) dzięki czemu zapis skróci się do
  1. $log->log('my log');
Dodatkowo nie kieruj się jedynie długością zapisu. Z reguły jego czytelność oraz logiczność są dużo ważniejsze.
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-2025 Invision Power Services, Inc.