Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

> Czy to jest poprawne - w duchu obiektowości?, proszę o komentarze
Aztech
post
Post #1





Grupa: Zarejestrowani
Postów: 276
Pomógł: 3
Dołączył: 22.10.2003
Skąd: Wrocław

Ostrzeżenie: (0%)
-----


Zacząłem niedawno pisać na poważnie (w końcu praca duplomowa) w php5 i chciałbym się spytać czy to co napisałem, jest w pełni w duchu obiektowości. Może widzicie tam jakieś błędy, może macie jakieś wskazówki. Za wszelkie rady, spostrzeżenia, w szczególności te bardzo krytyczne, będę niezmiernie wdzięczny.

Gwoli wyjaśnienia, klasa ta ma odpowiadać za obsługę użytkownika:
(*) dodawać
(*) usuwać
(*) uaktualniać
(*) parsować poprawność wprowadzonych danych
itp.

A więc jeszcze raz proszę o jak najwięcej uwag i krytyki (ew. pochwały też przyjmuję (IMG:http://forum.php.pl/style_emoticons/default/biggrin.gif) )


  1. <?php 
  2.  
  3.  
  4. /*
  5.     UWAGA: wszystkie zapytanie sql sformatować odpowiednio dla adodb
  6. */
  7.  
  8. require_once("../core/adodb/adodb.inc.php");
  9.  
  10. class TUser {
  11.     private $user_id=-1;
  12.     private $nick ="";
  13.     private $name= "";
  14.     private $group_id=0;
  15.     private $password="";
  16.     private $password_crypt="";
  17.     private $email="";
  18.     private $gg="";
  19.     private $photo="";
  20.     private $description="";
  21.     private $phone="";
  22.     private $last_error="";
  23.     private $is_error=0;   //0 - no error, 1 - error
  24.  
  25. ################################## need to update all validation functions #####
    ##############################
  26.  
  27. /********************************************************************************
    *****    
  28.                         VALIDATE FUNCTIONS                    
  29. ********************************************************************************
    *****/
  30.  
  31.     public function validateName($name) {
  32.         // validate if name is correct, name have 2 words which begin with uppercase and s
    pace beetween of them
  33.         if (ereg("^[A-z](a-zA-Z/-)*( A-Za-z/-)*",$name)) {
  34.                $this->unsetError();
  35.                return true;           
  36.         }
  37.         else {
  38.             $this->last_error = "NO_VALID_NAME";
  39.             $this->setError();
  40.             return false;
  41.         }       
  42.     }
  43.     
  44.     public function validateEmail() {
  45.         //validate if email is correct
  46.         if (eregi("^([a-z0-9_-.]{1,25})@([a-z0-9_-.]){1,25}$",$this->$email)){
  47.                $this->unsetError();
  48.                return true;           
  49.         }
  50.         else {
  51.             $this->last_error = "NO_VALID_EMAIL";
  52.             $this->setError();
  53.             return false;
  54.         }
  55.     }
  56.     
  57.     public function validatePassword($pass) {
  58.         //6-25 signs, one big letter and one number        
  59.         if (ereg("[A-Za-z0-9]")) {
  60.                $this->setError();
  61.                return true;           
  62.         }
  63.         else {
  64.             $this->last_error = "NO_VALID_PASSWORD";
  65.             $this->setError();
  66.             return false;
  67.         }
  68.     }
  69.     
  70. /********************************************************************************
    *****    
  71.                         COMPARE FUNCTIONS                   
  72. ********************************************************************************
    *****/
  73.  
  74.     public function comparePassword($value="") {
  75.         if ($this->password == $value) {
  76.             $this->unsetError();            
  77.             return true;            
  78.         }
  79.         else {
  80.             $this->last_error = "PASSWORD_NOT_SAME";
  81.             $this->unsetError();
  82.             return false;
  83.         }           
  84.     }    
  85.  
  86. /********************************************************************************
    *****    
  87.                         GET FUNCTIONS                    
  88. ********************************************************************************
    *****/
        
  89.     
  90.     public function getUserInfo(){
  91.         return $this;
  92.     }
  93.  
  94. /********************************************************************************
    *****    
  95.                         SET FUNCTIONS                    
  96. ********************************************************************************
    *****/
        
  97.  
  98.     public function setError(){
  99.         $this->is_error = 1;
  100.     }
  101.     
  102.     public function unsetError(){
  103.         $this->is_error = 0;
  104.     }
  105.  
  106.     public function setUserID($value="") {
  107.         $this->user_id = $value;
  108.     }
  109.     
  110.     public function setNick($value="") {
  111.         $this->nick = $value;
  112.     }
  113.     
  114.     public function setName($value="") {
  115.            $this->name = $value;
  116.     }
  117.     
  118.     public function setGroupID($value="") {
  119.            $this->group_id = $value;
  120.     }
  121.     
  122.     public function setPassword($value="") {
  123.            $this->password = $value;
  124.     }
  125.     
  126.     public function setEmail($value="") {
  127.            $this->email = $value;
  128.     }
  129.     
  130.     public function setGG($value="") {
  131.            $this->gg = $value;
  132.     }
  133.     
  134.     public function setPhoto($value="") {
  135.         $this->phone = $value;
  136.     }
  137.     
  138.     public function setDescription($value="") {
  139.            $this->description = $value;
  140.     }
  141.     
  142.     public function setPhone($value="") {
  143.            $this->phone = $value;
  144.     }
  145.     
  146.     
  147.     public function setUserFromSQL($table){
  148.         //set user data using result form SQL query
  149.         $this->user_id          =   $table["id_user"];
  150.         $this->nick             =   $table["nick"];
  151.         $this->name             =   $table["name"];
  152.         $this->group_id         =   $table["name"];
  153.         $this->password         =   $table["password"];
  154.         $this->password_crypt   =   md5($table["password"]);
  155.         $this->email            =   $table["email"];
  156.         $this->gg               =   $table["gg"];        
  157.         $this->photo            =   $table["photo"];
  158.         $this->description      =   $table["description"];
  159.         $this->phone            =   $table["phone"];
  160.         $this->last_error       =   "";
  161.         $this->is_error         =   "0";    //no error
  162.     }
  163.        
  164.     public function __construct() {
  165.         $this->user_id          =   "-1";
  166.         $this->nick             =   "";
  167.         $this->name             =   "";
  168.         $this->group_id         =   "0";
  169.         $this->password         =   "";
  170.         $this->password_crypt   =   "";
  171.         $this->email            =   "";
  172.         $this->gg               =   "";        
  173.         $this->photo            =   "";
  174.         $this->description      =   "";
  175.         $this->phone            =   "";
  176.         $this->last_error       =   "";        
  177.         $this->is_error         =   "0";    //no error
  178.     }
  179.     
  180.     public function clearrData() {
  181.         $this->__construct();
  182.     }
  183.  
  184. /********************************************************************************
    *****    
  185.                         SET FUNCTIONS                    
  186. ********************************************************************************
    *****/
     
  187.  
  188.     public function findAllGroupsSQL(&$groups) {
  189.         //fing all groups where belong user
  190.         //if user is set
  191.         if ($this->user_id != "-1") {
  192.             /*
  193.             $sql =   " SELECT user_id, group_id, id_groups, name, moderator_id"
  194.                     ." FROM rbx_group_users LEFT JOIN rbx_groups"
  195.                     ." ON rbx_group_users.group_id = rbx_groups.id_groups"
  196.                     ." WHERE user_id = 3";            
  197.                     */
  198.         }
  199.         else {
  200.             $this->lastError="NO_USER";
  201.             $this->setError();
  202.             return false;
  203.         }
  204.     }
  205.     
  206.     public function userExistsSQL() {
  207.         //connectToADO($db);
  208.         $sql = "select email from rbx_username where email='".$this->email."'";
  209.         //$result = $db->Execute($sql);  
  210.         if (!$result->FieldCount())
  211.             return true;
  212.             else return false;        
  213.     }
  214.     
  215.     public function createUserSQL() {
  216.     //gdy uzytkownik nie istnieje
  217.         if (!$this->userExists()) {
  218.             //connectToADO($db);
  219.             $sql = "insert into 'rbx_user' ( 'id_user' , 'name' , 'pass' , 'email' , 'gg' , 'photo' , 'description' , 'phone' )".
  220.             " values ('', '".$this->name."', '".$this->password_crypt."', '".$this->email."', '".$this->gg."', '".$this->photo."', '".$this->description."', '".$this->phone."')";
  221.             //$result = $db->Execute($sql);          
  222.             $this->is_error = false;      
  223.         }
  224.         else {
  225.             $this->is_error = true;
  226.             $this->last_error = "USER_ALREADY_EXIST";
  227.         }
  228.     }           
  229.  
  230.     public function updateUserSQL() {    
  231.         //connectToADO($db);        
  232.             $sql = "update 'rbx_user'"
  233.             ." set 'name'=".$this->name.","
  234.             ." 'pass'=".$this->password_crypt.","
  235.             ." 'email'=".$this->email.","
  236.             ." 'gg'=".$this->gg.","
  237.             ." 'photo'=".$this->photo.","
  238.             ." 'description'=".$this->description.","
  239.             ." 'phone'=".$this->phone.","
  240.             ." 'pass'=".$this->password_crypt
  241.             ." WHERE 'id_user'=".$this->user_id;            
  242.         //$result = $db->Execute($sql);          
  243.         $this->is_error = false;      
  244.     }
  245.     
  246.     public function deleteCurrentUserSQL() {
  247.         //connectToADO($db);
  248.             $sql = "delete from 'rbx_user'"
  249.             ."WHERE user_id=".$this->user_id;
  250.         //result = $db->Execute($sql);
  251.         $this->is_error = false;
  252.     }
  253.     
  254.     public function deleteUserSQL($db) {
  255.         //connectToADO($db);
  256.             $sql = "delete from 'rbx_user'"
  257.             ."WHERE user_id=".$db->user_id;
  258.         //result = $db->Execute($sql);
  259.         $this->is_error = false;
  260.     }        
  261.     
  262.         
  263. }
  264.  
  265. ?>


Ten post edytował NuLL 28.11.2005, 23:06:30
Go to the top of the page
+Quote Post
 
Start new topic
Odpowiedzi
krzysztof f.
post
Post #2





Grupa: Zarejestrowani
Postów: 18
Pomógł: 0
Dołączył: 22.11.2005

Ostrzeżenie: (0%)
-----


Witam Aztech!

Więc zaczynamy.. zajmijmy się duchem obiektowości ; ).
Pierwsza zasada: każdy obiekt w systemie powinien mieć ściśle określoną odpowiedzialność, którą da się opisać jednym zdaniem. Powinien dostarczać implementacji jakiegoś zachowania lub struktury z dziedziny problemu. Jednym słowem musimy wystrzegać się tak zwanych god classes, przeciążonych nadmierną odpowiedzialnością i kodem.

W tej kwestii poradziłeś sobie nie najgorzej, ale też daleko od ideału. Przyjrzyjmy się odpowiedzialności klasy, którą potrzebujesz stworzyć. Będzie to prawdopodobnie klasa opisująca obiekt użytkownika w Twojej aplikacji. Obiekt taki ma identyfikować pojedynczego użytkownika i dostarczać niezbędnych poprawnych danych o nim. Ponadto obiekt taki ma być utrwalany w bazie danych. W tym miejscu powinno nasunąć się już pytanie, czy obiekt użytkownika powinien też odpowiadać za utrwalanie siebie w bazie danych. Zgodnie z architekturą wielowarstwową obiekt użytkownika należy do warstwy logiki biznesowej, a zapisywanie do bazy to już odpowiedzialność warstwy danych. Aby oddzielić zupełnie te dwie odpowiedzialności musielibyśmy się zająć pojęciem ORM, którego niewielki fragment znajdziesz w przykładowym rozdziale z PoEAA http://www.awprofessional.com/articles/article.asp?p=30661.

W twoim przypadku prawdopodobnie wystarczy prostsze rozwiązanie, które w zasadzie już zastosowałeś a nazywa się Active Record http://martinfowler.com/eaaCatalog/activeRecord.html. Jest wzorzec stosowany do tworzenia obiektów dziedziny, których struktura pokrywa się ze strukturą tabeli i które reprezentują pojedynczy rekord takiej tabeli. Oprócz podstawowej niewielkiej ilości logiki biznesowej, potrafią one utrwalać się w bazie danych.

Skoro padła już nazwa jakiegoś wzorca, to możemy być pewni, że część odpowiedzialności prawdopodobnie uda nam się wyłączyć do klasy bazowej. http://www.daholygoat.com/jaws/html/index....ction=Page&id=8

Więc ustaliliśmy już kwestie utrwalania danych http://patternshare.org/default.aspx/Home.MF.ActiveRecord , postanowiliśmy wyodrębnić klasę bazową. Teraz zastanówmy się nad metodą wydobywania danych i konkretyzacji obiektów. Aby odciążyć trochę klasę użytkownika, odpowiedzialność za wyszukiwanie obiektów w bazie należałoby wyodrębnić do oddzielnego obiektu. Przy okazji zastosujemy Separatek Interface http://patternshare.org/default.aspx/Home....aratedInterface ukrywając implementację przed kodem klienta. W praktyce kod klienta będzie wyglądał mniej więcej tak:

  1. <?php
  2. $oUser = $oUserFinder->findByName( 'Krzysztof' );
  3. $oUser->setNick( 'blazej' );
  4. $oUser->update();
  5.  
  6. //dla kolekcji obiektów
  7. $aUsers = $oUserFinder->findAll();
  8. foreach ( $aUsers as $oUser )
  9. {
  10.  echo $oUser->getName() . '<br />';
  11. }
  12. ?>


Poświęćmy jeszcze chwilę tworzeniu obiektów User. Nie podoba mi się, że pozwalasz tworzyć puste obiekty nie wykorzystując konstruktora. Poza tym konstruktor jest metodą wykorzystywaną tylko w momencie inicjacji obiektu, więc wywołanie go w metodzie TUser:: clearData() jest błędem. Musisz odpowiedzieć sobie na pytanie jakie dane wymagane są do prawidłowego funkcjonowania obiektu User i zadbać o ich inicjalizację w czasie konstruowania obiektu. Prawdopodobnie nie może istnieć użytkownik, który się jakoś nie nazywa i nie ma podanego e-maila (który jest w wielu miejscach aplikacji wykorzystywany do komunikacji z nim). Konkretyzacja obiektu powinna wtedy wyglądać tak:

  1. <?php
  2. $oNewUser = new User( 'Krzysztof', 'adres@email.pl' );
  3. ?>


O enkapsulacji pamiętasz, kolejna ważna zasada zgodna z duchem obiektowości ; ). Mamy wszystkie składowe jako prywatne. Odpowiednie metody ustawiające wartości. Dodałbym jeszcze metody do pobierania potrzebnych wartości albo zrobił pożytek z metody __call() dla zmniejszenia ilości kodu (chociaż biorąc pod uwagę przejrzystość interfejsu, może jednak nie). Metody walidujące zawarł bym przy odpowiednich seterach.

Sqle do modyfikowania rekordów zgonie ze wzorcem ActiveRecord zostają w klasie User. Wyszukujące wyłączamy. Dodajemy obiekt klucza stanowiący interpretację wzorca http://martinfowler.com/eaaCatalog/identityField.html, pilnujący tożsamości obiektów i powiązanych rekordów w bazie. Przy ładowaniu obiektów z bazy rejestrujemy je w IdentityMap http://martinfowler.com/eaaCatalog/identityMap.html dostępnej za pomocą jakiegoś rejestru, zabezpieczającej naszą aplikację przed wielokrotnymi odwołaniami do bazy danych i tworzeniem wielu instancji tego samego obiektu. Na koniec dodajemy kontrolę nad modyfikacją naszych obiektów w czasie ich życia i automatyczne utrwalanie zmian w bazie danych, za co odpowiedzialnością obarczamy „obiekt jednostki pracy”. Natomiast kolekcje obiektów obsługujemy (z wykorzystaniem opóźnionego ładowania) za pomocą specjalnego obiektu implementującego interfejs jednego z iteratorów SPL (tak jak to proponuje Matt Zandstra w „php 5 Objects, Patterns, and Practice” http://www.amazon.com/exec/obidos/ASIN/159...018931-2571322)

…i czy projektowanie obiektowe nie jest poezją!? : )

Dodatkowe uwagi:
1. Obsługa błędów w Twojej klasie jest fatalna. Skoro korzystasz z PHP5 i chcesz w pełni wykorzystywać jego możliwości obiektowe, to tak jak sugerowali przedmówcy powinieneś używać wyjątków. Dobra lektura na początek: http://www.zend.com/php5/articles/php5-exceptions.php
2. Pilnuj typu zmiennych, to że luźne typowanie w php daje dużo możliwości, nie zabezpiecza go przed wieloma problemami związanymi z tym. Ułatwieniem mogą być dobrze przygotowany standardy pisania kodu. Ja na przykład jak dodaje do nazw zmiennych przedrostek wskazujący jakiego typu jest zmienna. Poza tym lepiej dokumentuj swój kod, a w szczególności API. http://phpdoc.org/
3. Dodatkowo skoro PHP5 skoro nowy model obiektowy to sugeruję PDO jako abstrakcję dostępu do danych, zamiast AdoDb.

Tak to z grubsza mogłyby wyglądać zależności klas. Tobie pozostawiam implementację i życzę miłej zabawy.

(IMG:http://blazej.niepije.org/php.pl/user.png)
Go to the top of the page
+Quote Post

Posty w temacie


Reply to this topicStart new topic
2 Użytkowników czyta ten temat (2 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 28.12.2025 - 11:07