Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Singleton i dziedziczenie
Forum PHP.pl > Forum > PHP > Object-oriented programming
relaxant
Witam,
Wiekszość ludzi piszacych tutaj rozumie chyba jak działa Singleton. Do dzis myslalem ze tez to rozumiem. Czy moze mi ktos wytlumaczyc dlaczego ten kod nizej działa?

Mam kod index.php
  1. <?php
  2. require_once('lib/core/DB.class.php');
  3. $kk = new DB();
  4. $kk->AA();
  5. ?>


DB.class.php:
  1. <?php
  2. class DB extends mysqli{
  3. private static $instance;
  4.  
  5. private function __construct($hostname=DB_HOST, $username=DB_USER, $password=DB_PASSWORD, $database=DB_DATABASE){
  6. $hostname = ($hostname=="")?DB_HOST:$hostname;
  7. $username = ($username=="")?DB_USER:$username;
  8. $password = ($password=="")?DB_PASSWORD:$password;
  9. $database = ($database=="")?DB_DATABASE:$database;
  10.  
  11. //parent::__construct($hostname, $username, $password, $database);
  12. }
  13.  
  14. public static function getInstance() {
  15. if (!isset(self::$instance)) {
  16. self::$instance = new DB();
  17. } 
  18. return self::$instance;
  19. }
  20.  
  21. public function __clone() {
  22. trigger_error('Clone is not allowed.', E_USER_ERROR);
  23. }
  24.  
  25. public function __destruct() {
  26. print "DESTRUCT DB <br>";
  27. }
  28.  
  29. public function AA() {
  30. print "text";
  31. }
  32. }
  33. ?>


I to działa!! Pomimo tego ze konstuktor DB jest prywatny!! Jezeli nie dziedzicze po mysqli wyskakuje blad (reakcja prawidlowa).

testuje na php Version 5.1.2-1
mariuszn3
Pierwsza sprawa, to pewnie nie dopatrzenie php. Powinien Ci wyskakiwać błąd - klasa DB dziedziczy klasę, w której konstruktor jest publiczny.. tak więc i klasa DB musi mieć publiczny konstruktor. Czyli z założenia masz zły kod.
No ale błędu nie ma i dlaczego w takim razie tak to działa(?) Podejrzewam, że php odwołuje się do konstruktora, który jest widoczny w danym miejscu, czyli jeśli klasa DB ma prywatny, jest on niedostępny z zewnątrz.. natomiast widoczny jest publiczny konstruktor mysqli więc jest on wywoływany.
Bez wnikania w algorytm kodu źródłowego php, trudno mieć pewność czemu to tak się zachowuje.
relaxant
Tez tak myslalem ze pewnie jest wykonywany publiczny konstruktor mysqli ale wtedy zdaje sie nie powinienem miec dostepu do metody AA, a mam tongue.gif
mariuszn3
A dlaczego miałbyś nie mieć dostępu do AA? To, że jest wywoływany konstruktor mysqli, nie zmienia faktu, że obiekt jest instancją klasy DB.
Cysiaczek
Jeśli posiadasz wersję php 5.1.x, to chocbyś nie wiem jak kombinował, to konstruktor zawsze jest publiczny. Nie może by prywatny, ani chroniony. Jest o tym wzmianka w changes dla php i chyba nawet w nowym manualu.
relaxant
to chcesz powiedzić, że te wszystkie implementacje singletona w php są nie dokońca ok bo można i tak je obejeść wywołując new ? Private działa, no chyba ze dodatkowo dziedziczymy wtedy się coś chrzani (patrz wyżej)
Cysiaczek
Ideą singletona jest to, że w zakresie całej aplikacji możesz miec tylko jedną instancję klasy. Oczywiście zawsze można to obejś, ale pytanie po co? Singleton to rozwiązanie, które zastępuje nam zmienne globalne (przynajmniej w większości przypadków). Tzw bezpieczeństwo Singletona nie może by rozumiane w takim sensie jak bezpieczeństwo danych etc. To raczej bezpieczeństwo dla programisty, który ma miec pewnośc, że obiekt, który posiada jest obiektem unikalnym i jedynym w całej aplikacji (w konkretnym procesie). Dlatego nie wywołujemy poprzez klucz 'new', tylko specjalną metodę getInstance(), która sprawdza, czy istnieje już jakiś obiekt danej klasy i jeśli nie, to próbuje go wywołac.
bigZbig
Ale skoro konstruktor zawsze jest publiczny to o kant stołu dyrektywy public, protected i private. Równie dobrze moglibysmy pozostać jedynie przy oznaczaniu metod prywatnych za pomoca pojedynczego podkreslenia. W koncu chodzi tak naprawde jedynie o poinformowanie programisty o tym z czym ma do czynienia, a co on juz z tym zrobi to już jego sprawa. Szczerze mowiąc w jezykach skryptowych wyzej wspomniane oznaczenia zawsze maja tylko charakter informacyjny bo deklaracje zawsze mozna zmienic, chyba ze mamy do czynienia ze zdalnie wykonywanym kodem, ale to juz inna bajka.
nospor
Cytat
Ale skoro konstruktor zawsze jest publiczny to o kant stołu dyrektywy public, protected i private.
i dlatego przy konstruktorze tej dyrektywy sie nie stosuje winksmiley.jpg

zastosuj przy innych funkcjach, a przekonasz sie ze dziala. Swoją drogą mi sie loginczne wydaje, ze konstruktor musi byc publiczny. W koncu pelni on pewna role w klasie smile.gif
Cysiaczek
Spokojnie. Nie jest tak źle. Wszystkie inne składowe i metody mozna sobie dowolnie ustawic. Całe zamieszanie jest tylko z konstruktorem. Nie jestem w tej dziedzinie ekspertem, ale wydaje mi się, że to może miec związek ze standardami programowania.

i jeszcze...
nospor ma rację. W końcu konstruktor ma za zadanie dokonac operacji, które sa wykonywane dla kazdej instancji klasy.
mariuszn3
Cytat(Cysiaczek @ 3.07.2006, 12:12 ) *
Jeśli posiadasz wersję php 5.1.x, to chocbyś nie wiem jak kombinował, to konstruktor zawsze jest publiczny. Nie może by prywatny, ani chroniony. Jest o tym wzmianka w changes dla php i chyba nawet w nowym manualu.

Skąd te informacje?!? Konstruktor jak najbardziej może być prywatny i nawet należy go tak deklarować w przypadku singletona. Każde odwołanie się z zewnątrz do takiej klasy poprzez 'new' wyrzuci fatal error. Korzystam z 5.1.x i wszystko działa tak jak powyżej.
bigZbig
Oj chlopaki - wiem, ze dla pozostalych metod i wlasciwosci to dziala, problem jest innej natury. Brak jest konsekwencji. Albo mozemy konstruktorowi okreslac dostepnosc i wtedy powinny dzialac dyrektywy protected i private albo nie mozemy i wtedy zdefiniowanie takiej deklaracji powinno skutkowac bledem. A jesli ma to byc jedynie oznaczenie umowne to ja jestem za tym zeby to bylo umowne dla wszystkich metod i wlasciwosci.

-- edit --
@mariuszn3 - no i tak powinno byc
Cysiaczek
Zaraz poszukam i wskażę źródło... loading
mariuszn3
Cytat(Cysiaczek @ 3.07.2006, 13:02 ) *
Zaraz poszukam i wskażę źródło... loading

Po co źródło (chyba, że to jakaś nadchodząca nowinka.. której w ogóle bym się nie spodziewał i na razie nie jestem w stanie zrozumieć).

Przykład na 5.1.3RC4-dev na windzie:
  1. <?php
  2. class test {
  3. private function __construct() {
  4. var_dump(__CLASS__);
  5. }
  6. }
  7. new test;
  8. ?>


Odpowiedź:
  1. Fatal error: Call to private test::__construct() from context ''
Cysiaczek
http://pl.php.net/distributions/manual/php...z#language.oop5

Sekcja Method Visibility (patrzcie komentarze w kodzie)

Ja mam php 5.1.4 na Linuksie. Źródło, które podaję, to nieco inne niż to, z którego czerpałem informację. Jestem niemal pewien, że to był changelog lub inny plik w mojej dystrybucji php
mariuszn3
Cytat(Cysiaczek @ 3.07.2006, 13:10 ) *
http://pl.php.net/distributions/manual/php...z#language.oop5

Sekcja Method Visibility (patrzcie komentarze w kodzie)

Myślę, że mocno znadinterpretowałeś ten komentarz.. a swoją drogą nie jest on za bardzo przemyślany jak na komentarz w podręcznikowym przykładzie
Myślę, że autorowi chodziło po prostu o to, że konstruktor musi być publiczny by można było utworzyć obiekt klasy.. ale z zewnątrz.. tak jak tam dalej jest w tym przykładzie.
Zresztą to przykład dla początkujących, którzy dopiero dowiadują się czym jest dostępność i do singleton'ów czy innych wzorców jest tam daleko.. stąd pewnie to nadużycie.

EDIT: W tym samym dziale zobacz sekcję Patterns i Singleton smile.gif
relaxant
No i spoko " // Contructors must be public" zeby przyklad zadziałał. Ja tu pisze o czymś troche innym patrz ten sam dokument sekcja Patterns (Przykład 19-25. Singleton Function) czyli singletonach w klasach ktore dziedzicza po innych klasach. Zgadzam sie z mariuszn3. Jest to poprostu błąd php'a!
ActivePlayer
jako ze nie mam dostępu do php5 tutaj, zadam pytanie:

  1. <?php
  2. class DB extends mysqli{
  3. private static $instance;
  4.  
  5. private function __construct($hostname=DB_HOST, $username=DB_USER, $password=DB_PASSWORD, $database=DB_DATABASE){
  6. $hostname = ($hostname=="")?DB_HOST:$hostname;
  7. $username = ($username=="")?DB_USER:$username;
  8. $password = ($password=="")?DB_PASSWORD:$password;
  9. $database = ($database=="")?DB_DATABASE:$database;
  10. $this->AA();
  11. //parent::__construct($hostname, $username, $password, $database);
  12. }
  13.  
  14. public static function getInstance() {
  15. if (!isset(self::$instance)) {
  16. self::$instance = new DB();
  17. } 
  18. return self::$instance;
  19. }
  20.  
  21. public function __clone() {
  22. trigger_error('Clone is not allowed.', E_USER_ERROR);
  23. }
  24.  
  25. public function __destruct() {
  26. print "DESTRUCT DB <br>";
  27. }
  28.  
  29. public function AA() {
  30. print "text";
  31. }
  32. }
  33. ?>

  1. <?php
  2. require_once('lib/core/DB.class.php');
  3. $kk = new DB();
  4. ?>

Jaki jest rezultat?
Cysiaczek
Nie chcę się o to kłócic. Jak tylko postawię net na linuksie, to jeszcze raz wszystko sprawdzę i przejrzę moje pliki dystrybucji. Faktycznie niegdy do tej pory nie robiłem prywatnego konstruktora, bo nie widzę w tym większego sensu (używam często zewnętrznych wywołań). Ten kod w manualu widziałem też wcześniej, ale nadal nie moge oprzec się wrażeniu, że czytałem to w jeszcze innym miejscu i to nie w formie komantarza, tylko ładnego okrągłego zdania. Jeśłi się mylę, to oczywiście przepraszam za zamieszanie. Śledztwo w toku smile.gif
NuLL
Cytat
Jeśłi się mylę, to oczywiście przepraszam za zamieszanie

Mylisz sie winksmiley.jpg

@AP - rezultatem powinno byc
Cytat
Fatal error: Call to private DB::__construct() from context ''
relaxant
Ok, od wczoraj z cvs'a na php.net można ściągnąć poprawioną wersje php'a, w której powyższy błąd został poprawiony.
NuLL
O jakim bledzie mowisz questionmark.gif
relaxant
Patrz - 1 wpis, to co mówiłeś

Cytat
@AP - rezultatem powinno byc
CYTAT
Fatal error: Call to private DB::__construct() from context ''


to jak powiedziałeś "powinno byc" , a nie jest (nie było) smile.gif .
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.