Quand l'abstraction d'interface interfere la clareté du PHP

Bonjour à tous et à toutes,

ça faisait longtemps que je m’étais pas cassé la tête au point de venir demander de l’aide ici.
Dans le désir de me faire chier pour rien ™, j’ai décidé pour la nouvelle mouture de mon site de baser chaque module sur une classe abstraite, qui interface une interface (mais je sais plus du coup, osf à la limite).

Je me dis tient, si je faisais une interface pour ce qui est messagerie …

[code]<?php

interface Messagerie
{
public function From($emeteur);
public function To($destinataire);
public function Title($titre);
public function Body($body);
public function Send();
}

?>[/code]

(je vous fait grace des licences, sympa non ?)

Bon bah ya plus qu’a en faire une classe Mail et message privé !
Hum … on doit pouvoir factoriser un peu de code entre les deux, j’ai qu’a faire une autre classe abstraite (je sais c’est pas mon blog B))

[code]<?php

// Ajouter secureinput toussa
Abstract class Msg implements Messagerie
{
private $Dest;
private $From;
private $Title;
private $Body;

public function __construct()
{
	$this->Dest = array();
	$this->From = array();
	$this->Title = NULL;
	$this->Body = NULL;	
}

public function From($emeteur)
{
	$this->From = $emeteur;
}
	
public function To($destinataire)
{
	if(is_array($destinataire))
		$this->Dest = $destinataire;
	else
		$this->Dest[]= $destinataire;
}

public function Title($titre)
{
	$this->Title = $titre;
}

public function Body($body)
{
	$this->Body = $body;
}

public function Send();

}

?>[/code]

Bon je vous passe l’héritage pour transformer en classe mail et message privé.

Arrivé là, jme dis merde !
Faut aussi hériter de BaseModule …
Mais on peut pas faire d’héritage multiple en PHP …

Donc Segmentation Fault (core dumped) dans ma tête.
Des idées ?

Merci d’avance

C’est quoi BaseModule (jamais programmé en PHP), pourquoi ne pas faire hérité Msg de BaseModule ?

En fait base module est une classe abstraite qui implémente une interface de classique ‘ajouter supprimer modifier’, et qui définie des paramettres communs à chaque module :
un nom, une instance de classe de gestion d’exception, un niveau d’alerte (low, medium, high), et des paramettres de configuration, mais qui sont principalement définies par la classe qui en hérite.

Exemple, une classe ModuleSample :

[code]class ModuleSample extends BaseModule
{
protected $query;

public function __construct()
{
	parent::__construct('NomDuModule', 'MonModule.conf.xml');
	$this->query = NULL;
}

}[/code]

Tu peux implémenter une interface tout en héritant d’une classe (ou alors j’ai pas compris ton problème) :

Y a un truc qui m’embête dans ton code, c’est ce mélange français / anglais. Pourquoi tu ne te décide pas une fois pour toute ? B) Aussi, je te conseille plutôt de nommer tes méthodes setTitle(), setBody(), pour savoir plus facilement ce qu’elles font, et pour pouvoir les distinguer des méthodes “get” (getTitle(), getBody()…) que tu implémenteras surement à un moment.

Attention à ta méthode To() aussi. Si tu ne lui passe qu’un destinataire il sera ajouté à la liste déjà existante (sans rien écraser) alors que si tu lui files un tableau il va tout écraser. Je ne suis pas sûr que ce soit ce que tu veux. B)

Implémenter n’est pas hériter.

[quote=« remouk, post:4, topic: 44724 »]Tu peux implémenter une interface tout en héritant d’une classe (ou alors j’ai pas compris ton problème) :

Abstract class Msg implements Messagerie extends BaseModule

Y a un truc qui m’embête dans ton code, c’est ce mélange français / anglais. Pourquoi tu ne te décide pas une fois pour toute ? B) Aussi, je te conseille plutôt de nommer tes méthodes setTitle(), setBody(), pour savoir plus facilement ce qu’elles font, et pour pouvoir les distinguer des méthodes « get » (getTitle(), getBody()…) que tu implémenteras surement à un moment.

Attention à ta méthode To() aussi. Si tu ne lui passe qu’un destinataire il sera ajouté à la liste déjà existante (sans rien écraser) alors que si tu lui files un tableau il va tout écraser. Je ne suis pas sûr que ce soit ce que tu veux. :D[/quote]

En fait je me suis rendu compte à mi-parcours que ça allait merder, donc j’ai pas vraiment terminé mon implémentation, mais pour le To() c’est pas génant génant (je veux dire je sais a peu pres ce que je veux, mais pas encore trop) va falloir ajouter des methodes, mais j’hésite à les ajouter aux interfaces …
Pour l’anglait français, je sais … mais eu … B) disons que … >_> …

Sinon le fond du probleme est que baseModule est DEJA une classe (abstraite) …

Donc je pense que ma derniere solution c’est : à bas l’encapsulation, tout en public et vive la composition !!
Stadire une instance de Msg dans le ModuleMessagerie qui LUI heriterait de BaseModule.

Mais bon tout de suite je trouve ça moins cool.
Ce qui est assez con pour du code prototype censé etre jmecasselatete :smiley:

Ah j’oubliais : sympa le lien au logo pommesque.

Ce n’est pas un problème, d’ailleurs si ce n’était pas une classe (abstraite ou non) tu ne pourrais pas en hériter ! A mon avis tu mélanges un peu les différents concepts. Je te conseille de lire un bon bouquin ou une bonne doc sur la programmation objet (may Google be with you). Ca fait de mal à personne et tout te semblera beaucoup plus clair. B)

T’as sans doute raison, je pige plus rien : j’ai lu pas mal de choses justment là dessus et je vois pas (plus) ce qui cloche …
Bon je regarde dans la journée et je reposte si besoin.

Merci en attendant B)

Attention au propre pour faire du propre (comme un peu décrit ici). Normalement le principe de base de l’objet, c’est de réduire la complexité en “cachant” les implémentations, et l’héritage est un outil très puisant pour y arriver. Le problème c’est qu’il est également facile de faire un truc TROP complexe, et de se perdre dedans. Là j’ai l’impression que tu nages en plein dans ce problème.
Donc +1 pour la lecture sur le sujet.

Là rapidement, je dirai qu’il y a un problème de découpage. Pour moi, Msg ne doit pas être capable de s’envoyer, ce n’est pas sa responsabilité. On peut imaginer un truc comme ça :


|ISender|----Utilise------|Message|


|Send() | | |


/\
||

implémente
||


|SmtpSender| |MsmqSender| etc etc


Ensuite je ne suis sûr de bien avoir saisi ta notion de module, mais comme ça intuitivement, je dirai que ça me parait une mauvaise idée de faire hériter Messagerie (ou ISender ici), de baseModule, j’ai l’impression que tu mélanges plusieurs notions qui n’ont rien à voir au sein des mêmes classes.

Quand tu écris une classe, il faut se poser des questions sur sa responsabilité, ce qui doit être visible ou pas, et si chaque élément que tu exposes respecte l’abastraction que tu décris (en gros est-ce qu’une classe “Personne” doit exposer une méthode qui permet de “Démarrer” une voiture? )

EDIT: chiote la mise en page par un peu en sucette ^^ Sinon puisqu’on parle bouquin je conseil Code complete, très bon résumé (de 800 pages ^^ ) de pratiques de code ou de design. Rien que la partie sur les classes vaut le détour.

Merci à vous, vu que j’ai pas été là de l’aprem, je vais essayer de décrire un peu plus ce à quoi j’avais « pensé », pour voir si je fais fausse route.

Un module à la base, c’est une partie du site : dans mon cas, un forum, des news, une messagerie privée, et une faq (hop ya d’autres trucs mais on s’en tape).
Pour ce genre de choses, on a toujours 3 actions redondantes : ajouter, supprimer, modifier.
J’ai donc pensé à une interface, qui définie ces trois méthodes (en anglais B))
(il peut y avoir une quatrieme, mais j’attends d’avoir un joli dé pour savoir si je la fait ou pas, un truc genre doit())

Autre point commun, chaque « module » possede(ra) son ptit fichier de config et/ou de définitions des erreurs (un xml).
J’ai donc fait une classe abstraite, la fameuse baseModule, qui a pour but de définir et « construire » (au sens initialiser par le biais du constructeur + setters et getters) quelques variables, comme :

  • le nom du module,
  • le niveau d’acces (genre s’il faut etre loggé ou pas, admin ou pas),
  • un niveau d’erreur (en gros si c’est HIGH, on envoie des mails aux admin, et si c’est LOW on se contente d’écrire dans le journal d’évenement),
  • une instance d’une classe ModuleException (qui hérite de Exception) qui permet grossomodo de se démerder avec une référence vers l’objet et un code d’erreur
  • et je crois que c’est a peu pres tout

C’est en voyant ces points communs entre mes modules (maintenant on sait à peut pres ce que c’est) que j’ai eu l’idée (farfelue) d’en faire une classe elle meme, puis comme c’est une classe qui sert à rien sans héritage, hop abstract.
Une classe et pas une interface « juste » pour le constructeur, getters, setters.

C’est un peu dans la meme logique (voir exactement la meme) que j’ai fait la classe de Messagerie de base (non instanciable car ne sert à rien sans héritage) puisque c’est quasi la meme chose pour un mail que pour un message privé, voir un sms ou un message télépatique.
N’empeche que mon module de messagerie est un module donc doit surcharger BaseModule pour pouvoir etre dréssé comme tout le monde.

Ou alors jme goure et je dois juste utiliser de la composition pour que BaseModule devienne une « information » en tant que membre de toute classe module.
Et comme j’ai pas 3 semaines pour lire un bouquin, et accéssoirement pas de fric en ce moment, je continue de vous embeter avec ça.

En quise de synthese sur mon « archi », le but de tout ça est de pouvoir finallement instancier des modules comme suit :

[code]try
{
$monModule = new Module(<<fichier de config, passé par magie>>);
if(isset($_POST[‹ done ›]))
{
$monModule->Ajouter($_POST);
echo ‹ Action réalisée avec succes. ›;
}
else
$monModule->DisplayForm(); // Voilà pourquoi je parle (plus bas) de l’interfaçage du html, pour savoir où le situer dans quelle classe ou interface.

catch (ModuleException $e)
{
try
{
echo $e;
}
// par convention, si l’on rencontre un probleme dans les exceptions, on lance une exception « basique » pour plus de simplicité.
catch(Exception $ex)
echo $ex;
}
// Des fois que quelque chose ne soit pas capturé
catch (Exception $e)
echo $e;[/code]

En se disant qu’aucune érreur ne peut passer ici, puisque les exceptions relevent tout.
( Je veux pas de remarque sur le bien fondé d’utiliser des exceptions ou pas, en develweb tout ce qui est « sale » en software est trop hype en web :smiley: )

Enfin la derniere « problématique » c’est peut être de savoir comment « interfacer » (cf article zend ci dessous et l’interface xmlable, moi je pensais à un htmlable, pdfable … ) mon htmleuh…
je m’explique en partant de ce design pattern (bridge (pas d’ancre sur la page)) et de cette histoire de xmlable, je me suis dit que ça pourrait être sympa de faire une page qui soit générée par une classe basé un peu sur ce principe : du xml, ou du html, ou du pdf par exemple. (mais ceci est quand meme une tout autre question, mais par principe je n’ouvre pas d’autre sujet)

Je pense avoir tout mis à plat, j’espere que ça pourra vous aider à m’aider à y voir plus clair B)

Merci d’avoir pris le temps de lire tout ça !

Bon clairement tu as un problème de design B) Je n’ai pas la science infuse, donc je ne peux pas sortir la solution miracle (d’autant plus qu’elle n’existe pas). Déjà premier point, je ne trouve pas sal d’utiliser les exceptions :smiley:

Ceci dit, j’ai plusieurs remarques. J’ai l’impression que tu veux mettre en place un truc super clean, sans vraiment connaître les fondements de l’objet. Donc je ne peux que lancer un gros « attention danger », et réitérer le conseil de bien se documenter sur le sujet en général avant de s’attaquer à un truc aussi ambitieux.

Rapidement il y a quelques règles de base qui aident bien: quand on se demande si on veut faire hériter une classe, il faut qu’elle respecte le principe de substitution de liskov (ouah ça pète comme phrase B) ). Il faut en faut se poser la question si une classe enfant EST une classe parent(genre employé qui hérite de Personne, est ce qu’un employé est une personne?. ). Ensuite, chaque classe doit avoir un but et un seul clairement définit, donc le coup de baseModule qui gère la configuration, le CRUD, les exceptions… mmm ça send pas bon.

Bon je pourrai écrire des pages, mais je vais me faire taper si je ne rejoins pas ma copine à table donc j’essaierai de poster plus de détails demain du boulot :smiley:

Huhu toi aussi t’es martyrisé par ta copine :smiley:

Tout cas merci de tes conseil masta object (c’est l’impression que tu semble refleter apres avoir lu quelques threads / billets sur ton blog) B)
Pour ma part j’ai pas vraiment d’experience en POO donc effectivement, je suis sans doute dans le bancal quand à l’utilisation « sémantique ».

J’essaye de me trouver un ptit site, pi sinon ça sera BU :smiley:

Bonne soirée

[quote=« fser, post:12, topic: 44724 »]Tout cas merci de tes conseil masta object (c’est l’impression que tu semble refleter apres avoir lu quelques threads / billets sur ton blog) :smiley:
Pour ma part j’ai pas vraiment d’experience en POO donc effectivement, je suis sans doute dans le bancal quand à l’utilisation « sémantique ».[/quote]

Hihi merci ça fait plaisir même si mon site est pourri par des bots et que je poste une fois tous les 36 du mois B)
Enfin en attendant, je te souhaite alors la bienvenue dans le merveilleux monde(sans pitié) de la conception orientée objet :D, ou personne ne tombe jamais d’accord sur les bonnes pratiques à appliquer B)

( pff et moi qui m’attendais à un récit de tête code complete … B) )