Page 1 sur 1

Héritage, vous avez dit héritage ? d'accord, mais où ?

Posté : 17 sept. 2009, 19:08
par colibri
Bonjour. bonsoir tout le monde.

Plus ça va, plus je me débrouille de mieux en mieux avec l'objet, pour ne pas dire bien.

Sauf que dans l'utilisation de l'objet dans une application web, je n'arrive pas à me servir vraiment de l"héritage.

Tous les exemples d'utilisation de l'objet avec héritage en php que l'on a généralement en exemple sur internet utilise tout sauf quelque chose d'orienté application web...

Et pire, enfin je ne sais pas si c'est pire, 80% de mes classes utilisent des singletons, les seuls héritages que je fait sont issus de classes abstraites.

Donc voilà, j'aimerais bien savoir si ma conception de l'objet avec php est erronée ou pas ?

Et si c'est le cas, j'aimerais bien que vous me montriez sous forme d'une simple arborescence dans quel genre d'application on pourrait se servir utilement de l'héritage dans une application basique.

Ah si, j'ai déjà vu faire de l'héritage genre pour sortir des données sous formats différents, genre y'a une classe principale et 2 autres héritent de celle-ci, ce genre d'héritage, je le trouve personnellement superflu et alourdissant le code pour pas grand chose, 2 méthodes différentes dans la classe mère (tout au plus une 50aine de ligne) faisant le même travail.

Merci de vos réponses concernant ce concept d'héritage dans une application web.

Re: Héritage, vous avez dit héritage ? d'accord, mais où ?

Posté : 18 sept. 2009, 16:08
par Cyrano
Salut,
on va partir d'un concept pratique.

Supposons que tu aies une page sur un site web qui doit pouvoir être sortie soit au format HTML pour un navigateur soit au format PDF. Bien entendu, tu pourrais créer simplement deux pages distinctes, mais tu serais obligé de répéter une partie du code dans les deux à savoir la récupération des données.

L'idée et l'intérêt de l'héritage ici serait d'avoir une classe mère mapage.php qui se charge de cette récupération des données, qui traite les paramètres reçus et tient les bonnes données à disposition des classes héritées. Cette classe mère pourrait avantageusement être abstraite puisqu'en fait tu ne l'instancieras jamais directement. Tu crées ensuite une page mapagehtml.php qui étend cette classe mère et retourne le contenu demandé vers le navigateuret ensuite (ou avant selon tes priorités) tu crées mapagepdf.php qui hérite également de cette classe abstraite : les données sont les mêmes, mais le traitement sera destiné à générer un fichier pdf.

L'intérêt majeur maintenant : imagine que dans trois mois on te demande de sortir une version de cette page de données dans un tableur au format CSV, ou encore de sortir une version web pour PDA : tu n'auras pas à te goinfrer de refaire du copier coller pour collecter les données : tu crées une nouvelle classe héritant de la classe abstraite d'origine et tu ne construis que le code de traitement pour obtenir la sortie souhaitée, tableur ou web-PDA.

Autre exemple courant : l'accès aux données. Tu verras couramment des structures d'accès aux données sous la forme de classes, une pour chaque table de la base de données. Mais toutes ces classes héritent d'une classe mère qui va contenir les méthodes qui seront de toutes façons indispensables quelle que soit la table traitée.

Voilà, je ne sais pas si ça répond correctement à ta question ? :-k

Re: Héritage, vous avez dit héritage ? d'accord, mais où ?

Posté : 18 sept. 2009, 16:13
par Sékiltoyai
Mais c'est simple !
Imaginons, tu as une classe Vehicule, tu pourras faire hériter une classe Camion et une classe Voiture. Et tu peux même encore hériter en Break ou Roadster. Elles ont toutes des méthodes en commun : avancer, reculer, faireDemiTour !
Non, c'est pas ça ? :mrgreen:

Re: Héritage, vous avez dit héritage ? d'accord, mais où ?

Posté : 18 sept. 2009, 18:06
par thehawk
Mais c'est simple !
Imaginons, tu as une classe Vehicule, tu pourras faire hériter une classe Camion et une classe Voiture. Et tu peux même encore hériter en Break ou Roadster. Elles ont toutes des méthodes en commun : avancer, reculer, faireDemiTour !
Non, c'est pas ça ? :mrgreen:
On dirait l'exemple du livre PHP 5 Avancé :D allez avoue tu la lu ;D

[Okay les private Joke c'est mal :D]
Bye Hawk

Re: Héritage, vous avez dit héritage ? d'accord, mais où ?

Posté : 18 sept. 2009, 19:32
par Sékiltoyai
Mais c'est simple !
Imaginons, tu as une classe Vehicule, tu pourras faire hériter une classe Camion et une classe Voiture. Et tu peux même encore hériter en Break ou Roadster. Elles ont toutes des méthodes en commun : avancer, reculer, faireDemiTour !
Non, c'est pas ça ? :mrgreen:
On dirait l'exemple du livre PHP 5 Avancé :D allez avoue tu la lu ;D
Non, en fait j'ai horreur des livres qui traitent de l'informatique, donc non, je ne l'ai pas lu :)

Re: Héritage, vous avez dit héritage ? d'accord, mais où ?

Posté : 20 sept. 2009, 13:29
par Hywan
Hey :-),

Autre exemple plus concret que celui de Cyrano : les utilisateurs. Tu auras un super-utilisateur, un simple visiteur, un membre etc. Là tu as un bien une hiérarchie qui se dessine non ? Pareil pour le gestionnaire de droits, tu as pas mal d'héritage dans tout ça.

Ensuite, si tu as en effet beaucoup de singletons, il faut te poser des questions. Une classe qui admet un singleton doit plus être vue comme un service ou un composant qu'un objet … Bon, c'est un peu de la philosophie, mais la POO est essentiellement une vue de l'esprit non ;-) ?

Re: Héritage, vous avez dit héritage ? d'accord, mais où ?

Posté : 20 sept. 2009, 18:48
par Cyrano
Autre exemple plus concret que celui de Cyrano
:shock: En quoi mon exemple est-il moins concret ????

Re: Héritage, vous avez dit héritage ? d'accord, mais où ?

Posté : 20 sept. 2009, 19:16
par Hywan
Pardon, j'aurais du dire : « plus fréquent » :-).

Re: Héritage, vous avez dit héritage ? d'accord, mais où ?

Posté : 01 nov. 2009, 14:48
par Sn3b
Salut,

Je vois pas mal de réponses mais aucun code, alors je vais coller mon code pour que vous puissiez me dire si je fais les choses correctement :) et qui sait, si je vais bien les choses peut être que colibri aura un réponse un peu plus colorée ;)

Voici ma classe de base:
class Entity
{
	# Fields
	protected $_id;
	protected $_isSaved;
	
	# Universal getter and setter
	// Derived classes should override the setter for read-only properties
	public function __get( $propertyName )
	{
		// converts Property into _property 
		$fieldName = '_' . strtolower( substr( $propertyName, 0, 1 ) ) . substr( $propertyName, 1 );

		if( property_exists( $this, $fieldName ) )
			return $this->$fieldName;
		else
			throw new Exception( 'Unknow property ' . $propertyName );
	}
	public function __set( $propertyName, $value )
	{
		// converts Property into _property 
		$fieldName = '_' . strtolower( substr( $propertyName, 0, 1 ) ) . substr( $propertyName, 1 );
		
		switch( $fieldName )
		{
			// The following has been commented out because Id needs to be settable from outside.
			// Normally it should be internally settable, so the manager class which is in the
			// same namespace is able to modify it, but the internal keyword doesn't exists in
			// PHP, therefore we simply make it public.
//			case '_id':
//				throw new Exception( 'Property Id is read-only' );
//				break;
			case '_isSaved':
				$this->_isSaved = $value;
				break;
			default:
				if( property_exists( $this, $fieldName ) )
				{
					if( $this->$fieldName != $value )
					{
						$this->$fieldName = $value;
						$this->ModifyEntity();
					}
				}
				else
					throw new Exception( 'Unknow property ' . $attribute );

				break;
		}
	}
	
	# Constructors
	public function __construct()
	{
		$numArgs = func_num_args();
		$args = func_get_args();

		if( $numArgs == 0 )
			call_user_func_array( array( $this, '__constructNewEntity' ), null );
		else
			call_user_func_array( array( $this, '__constructExistingEntity' ), $args );
	}
	private function __constructNewEntity()
	{
		$this->_id = -1;
		$this->_isSaved = false;
	}
	private function __constructExistingEntity( $id )
	{
		// Because of this all classes that derive from this one should have their ID
		// property set first in the list of arguments
		$this->_id = $id; 
		$this->_isSaved = true;
	}

	# Private Methods
	private function ModifyEntity()
	{
		$this->_isSaved = false;
	}
}
La classe contient:
- une propriété ID qui sera égal à l'ID sauvée dans la DB, ou alors -1 si l'objet n'a pas encore été sauvé dans la DB
- une propriété isSaved, qui me permet de savoir si oui ou non l'objet a été sauvé dans la DB
- un getter universel que les classe dérivée pourront utiliser, en 2 mots elle me permet d'appeler $[Object]->[Propriété] et la classe cherchera automatiquement une propriété sous la forme "_propriété" qui correspond, la raison est que je viens du C# et que je n'aime pas avoir des methods pour aller chercher les valeurs de mes proriétés
- un setter universel qui doit etre overridé pour les propriétés read only
- 3 constructeurs, 1 de base qui défini le prochain à appeler, 1 qui construit un nouvel objet et 1 qui construit un objet qui a été fetché de la DB.

Et voici une classe qui dérive de ma classe de base:
class Reference extends Entity
{
	# Fields
	// In order for the base class to have access to those properties they should all be
	// declared as protected
	protected $_name;
	protected $_email;
	protected $_company;
	protected $_position;
	protected $_message;
	protected $_password;
	protected $_createdAtGmt;
	protected $_deletedAtGmt;
	
	# Universal setter
	public function __set( $propertyName, $value )
	{
		// converts Property into _property 
		$fieldName = '_' . strtolower( substr( $propertyName, 0, 1 ) ) . substr( $propertyName, 1 );
		
		switch( $fieldName )
		{
			case '_createdAtGmt':
				throw new Exception( 'Property CreatedAtGmt is read-only' );
				break;
			case '_deletedAtGmt':
				throw new Exception( 'Property DeletedAtGmt is read-only' );
				break;
			default:
				$args = func_get_args();
				call_user_func_array( array( $this, 'parent::__set' ), $args );
				break;
		}
	}
	
	# Constructors
    public function __construct()
    {
        $num = func_num_args();
        $args = func_get_args();

		switch( $num )
		{
			case 0:
				call_user_func_array( array( $this, 'parent::__construct' ), null );
				call_user_func_array( array( $this, '__constructNewReference' ), null );
				break;
			case 9:
				call_user_func_array( array( $this, 'parent::__construct' ), $args );
				call_user_func_array( array( $this, '__constructExistingReference' ), $args );
				break;
			default:
				throw new Exception( 'Number of arguments invalid' );
		}
	}
	private function __constructNewReference()
	{
		$this->_name = '';
		$this->_email = '';
		$this->_company = '';
		$this->_position = '';
		$this->_message = '';
		$this->_password = '';
		$this->_createdAtGmt = gmdate( 'Y-m-d H:i:s' );
		$this->_deletedAtGmt = null;
	}
	private function __constructExistingReference(
		$id, // Still present in the list of arguments but ignored by this constructor
		$name,
		$email,
		$company,
		$position,
		$message,
		$password,
		$createdAtGmt,
		$deletedAtGmt )
	{
		$this->_name = $name;
		$this->_email = $email;
		$this->_company = $company;
		$this->_position = $position;
		$this->_message = $message;
		$this->_password = $password;
		$this->_createdAtGmt = $createdAtGmt;
		$this->_deletedAtGmt = $deletedAtGmt;
	}
}
Cette classe contient:
- pas mal de propriétés, toutes accessible via le setter universel de la classe de base, je peux donc appeler $ref->Company pour obtenir la valeur sauvé dans $_company
- un override du setter universel qui défini que 2 de mes propriétés sont read only, et appel le setter universel de base pour les autres
- et encore une fois 3 constructeurs, 1 qui appel le constructeur de base et défini le prochain à appeler, 1 pour les nouveaux objets et 1 pour les objets fetchés de la DB

Voilà pour mon "architecture" qu'en pensez-vous?