Structure de classes

[Résolu]
Signaler
Messages postés
1123
Date d'inscription
mardi 8 janvier 2002
Statut
Modérateur
Dernière intervention
21 avril 2009
-
Messages postés
1123
Date d'inscription
mardi 8 janvier 2002
Statut
Modérateur
Dernière intervention
21 avril 2009
-
Bonjour à tous,
j'ai un petit soucis de structure, histoire de bien faire.

Alors voila, j'ai une classe abstraite (qui me permet d'utiliser différents types) pour une base de donnée.
J'ai les classes filles mysql, mssql, postgreesql, etc.

Maintenant, j'aimerai ajouter des fonctions spécifique à ces classes.
Je pensais les étendres (extends mysql), car en théorie, ca ajouterai les fonctionnalités.
Le probleme, c'est que je ne veux pas faire une classe étendue pour chaque classes filles, c'est un peu ... bête.
Et je ne peux pas non plus étendre la classe abstraite sans hériter des classes filles...

Enfin bref, je suis un peu coincé.

Quelqu'un d'entre vous aurait des idées ?

Merci à vous ! :)

12 réponses

Messages postés
10839
Date d'inscription
lundi 24 février 2003
Statut
Modérateur
Dernière intervention
2 mars 2010
25
Relis (ou lis), mes tutops sur les design patterns ;-) Tu auras toutes ces notions.
Par "client", j'entends une classe qui se charge de te fournir une interface (dans le sens : interface utilisateur, pas dans le sens : interface objet) simple et unique pour utiliser des classes très différentes.
C'est ce que je t'expliquais avec mon petit exemple :
pour la classe mail, la méthode output::finish() sera un truc du genre : mail(...); Pour une classe file, ce sera : file_put_contents(...); par exemple.
Toi, tu utiliseras toujours : finish();
C'est une interface utilisateur simple pour gérer de la même manière, avec les mêmes appels, des méthodes très différentes.
Messages postés
2483
Date d'inscription
jeudi 30 novembre 2006
Statut
Membre
Dernière intervention
14 janvier 2011
18
Malalam : ça s'appelle une API Application Programming Interface.

Codefalse : ça permet de fournir à l'utilisateur des fonctions qui sont toujours les mêmes quelque soit le support, le moteur, le type de fichier, le moyen de communication, etc.

Bon alors je vais essayer de faire plus clair. Au moins Malalam m'a compris et semble ne pas me contredire, donc je ne fais pas trop fausse route.

Il te faut deux interfaces (objet) : une iImageHandler et une iImage.
La première définit quelles sont les méthodes qui sont accessibles au développeur qui utilise ton package.
La seconde définit quelles sont les méthodes qui sont utilisées par la première pour gérer une image d'un type spécifique.


Une classe abstraite permet de regrouper les méthodes communes à tous les types d'images. Que tu manipules des images gif, png, jpg, xmp, ico, bmp ou autre, tu as toujours besoin de charger l'image depuis le fichier pour pouvoir la manipuler avec la librairie (GD ou Imagick). Tu as toujours besoin d'une méthode pour écrire le fichier, ou afficher l'image sur la sortie standard (le navigateur, pour générer des images à la volée). Quelle que soit la lib utilisée, quel que soit le type de l'image, tu utiliseras toujours la même méthode (fonction) pour faire une rotation, inverser, obtenir un négatif. Ces méthodes, elles sont définies soit en tant que méthodes abstraites (de l'interface ou de la classe abstraite), soit en tant que concrètes (et donc abstraites dans l'interface) dans la classe abstraite, pour regrouper tout ce qui est commun à tous les types de fichiers.

Une classe Image par type de fichier, mais qui implémentent les mêmes méthodes, mais spécifiquement à chaque fichier.
Le chargement d'un gif ou d'un jpg ne se fait pas avec la même fonction de GD. cependant, il se fait avec le même paramètre (le nom d'un fichier) et aboutit au même résultat (une resource GD).

L'instance de la classe Image est stockée dans une propriété de la classe ImageHandler (puisqu'elle sert à ça : manipuler des images...). Quand on fait par exemple :
$oImageHandler -> rotate('-180');

ImageHandler::rotate() va exécuter la fonction imagerotate de GD. Elle pourrait ressembler, vaguement (en crade non testé et particulièrment incomplet) à ça :

abstract class ImageHandlerGD {

// Contient une instance de la classe Image
private $oSource;

public function __construct($sFichier) {
$sImageType = $this -> TrouveMoiLeTypeMimeDeCetteImage($sFichier);
$sClassType = 'Image'.$sImageType;
$this -> oSource = new $sClassType($this -> $sFichier);
}

protected function TrouveMoiLeTypeMimeDeCetteImage($sFichier) {
// Fait ce qu'il faut pour trouver le type miome de cette image
return $sType;
}

public function rotate($fAngle) {
imagerotate($this -> oSource -> getImage(), $fAngle);
}
}

Exemple de classe image:
class ImagePNG {
private $Content;

public function __construct($sFichier) {
// Vérifie que le fichier existe, etc
$this -> Content = imagecreatefrompng($sFichier);
}

public function getImage() {
return $this -> Content;
}
}


Il faut encore rajouter à la classe ImagePNG de quoi enregistrer l'image sur disque dur (ou l'envoyer au navigateur), et tout ce qui est propre au PNG. Toutes les méthodes des classes spécialisées (gif, png, jpg) doivent avoir les mêmes méthodes, quitte à ce qu'elles ne fassent rien (puisque jpg ne gère pas la transparence par exemple).
Messages postés
10839
Date d'inscription
lundi 24 février 2003
Statut
Modérateur
Dernière intervention
2 mars 2010
25
Hello,

heu...pourquoi tu ne rajoutes pas ces méthodes dans les classes filles directement...?
Messages postés
2483
Date d'inscription
jeudi 30 novembre 2006
Statut
Membre
Dernière intervention
14 janvier 2011
18
Salut,

Et pourquoi ne pas utiliser mon système de plugins ? Ca sert justement à ça : ajouter des fonctions à des classe, de manière dynamique.
Messages postés
1123
Date d'inscription
mardi 8 janvier 2002
Statut
Modérateur
Dernière intervention
21 avril 2009
1
@malalam : l'idée, c'est de pouvoir augmenter les fonctionalités de mes classes par des étendues (je suppose que c'est une des meilleurs solutions) : J'ai une classe de base (qui hérite d'une classe abstraite) et ensuite j'étends la classe de base pour lui ajouter de nouvelles fonctionnalités.

@neigedhiver : Ou se trouve ton systeme de plugins ?

Merci de vos réponses :)
Messages postés
2483
Date d'inscription
jeudi 30 novembre 2006
Statut
Membre
Dernière intervention
14 janvier 2011
18
Dans mes sources :)
Messages postés
1123
Date d'inscription
mardi 8 janvier 2002
Statut
Modérateur
Dernière intervention
21 avril 2009
1
ralala quelle question :p

Bon, j'ai regardé, mais je prefere tout coder moi-même (oui je sais, j'abuse :p) mais c'est histoire de bien comprendre ce que je fait.

En fait, pour avoir un exemple plus concret, je réfléchis à la source postée recement sur la réduction d'image.
Justement, neigedhiver, tu parle d'une possibilité de structures de classes pour des images.

J'envisage de me faire un petit moteur de news, donc avec l'histoire de la bdd. En fait, je pense que la meilleur solution c'est de travailler sur deux classes différentes :
la classe abstraite DB
les classes filles mysql, mssql, etc

et faire une autre classe à coté, news :
news
    + add (titre, contenu)
    + update (id, titre, contenu)
    + delete

puis faire une classe newsExtended
    + add (titre, date, contenu, auteur, image)
    + upadte (id, titre, date, contenu, auteur, image)
    // pas de delete car elle ira sur la classe mere
    + withImage
    + etc

Bon c'est qu'un exemple, mais pour revenir à mon premier probleme, je pense qu'il ne fallait pas mettre à la suite, mais faire du travail à coté.
Qu'en pensez-vous ?

@neigedhiver :
Je repense à ce que tu a dit sur la fonction de redimenssionnement sur la page d'accueil :
faire une classe abstraire imageHandler
avec des classes filles pour gd, imagemagick, etc
et ensuite de faire des classes à coté pour du png, gif, jpeg, etc
Ma question, c'est, à ton avis, ne serait-il pas plus intéressant d'implémenter directement la gestion du type dans les fonctions de la classe abstraite ? genre
createImage($sType = 'png', $etcValues);
et ce pour les fonctions qui demandent un typage ?

enfin c'est mon avis, et j'aimerai bien le tiens :)
Messages postés
2483
Date d'inscription
jeudi 30 novembre 2006
Statut
Membre
Dernière intervention
14 janvier 2011
18
La gestion du type dans la classe abstraite, cela va de soi. Tout dépend comment il est géré au niveau du code.

Pour la gestion d'images, si tu te fends d'une classes d'abstraction de traitement complète, voilà comment je vois les choses :
- une interface qui fixe le cadre de la classe instanciée
- une classe abstraite qui va gérer les méthodes communes (gestion de la taille du fichier, du chemin : elle peut, au passage, étendre SplFileInfo).
- une classe abstraite par logiciel (GD, ImageMagick, etc), qui étend la classe sus-citée
- une classe ImageHandler qui étend la classe précédente
- une classe concrète par type mime (png, jpeg, gif) puisque les fonctions à utiliser ne sont pas les mêmes. Cette classe n'étend aucune classe : elle est instanciée par ImageHandler, qui va l'utiliser tout simplement.

Ainsi, quand on instancie une nouvelle image, on spécifie le logiciel utilisé (à moins que ce ne soit dans les options, mais autant le passer en paramètre avec un choix par défaut) et l'image.
La classe abstraite vérifie le type de l'image (type mime de préférence, ou extension du fichier en cas d'échec) et charge une instance de la classe Image en fonction de ce type, qu'elle stocke dans une propriété. C'est cette propriété qu'elle va manipuler.

Ainsi, pour redimensionner une image, on n'a plus du tout besoin de se soucier du type d'image (c'est géré par la classe ImageHandler) ni du logiciel utilisé (GD ou ImageMagick) : on se contente d'appeler la méthode redimensionne() avec les bons paramètres. Certaines valeurs par défaut pouvant d'ailleurs être gérées par ImageHandler (dans le cas où ImageHandler sert à gérer une collection d'images avec les mêmes paramètres, comme la taille maxi, etc).

On peut ensuite rajouter tout un tas de méthodes pratiques à cette classe ImageHandler :
- assombrir
- inverse
- rotation
- etc

Ces méthodes étant principalement liées au logiciel, pas au type d'image. Le type d'image, lui, sert pour créer une image GD à partir d'un fichier, l'enregistrer en tant que tel type d'image, etc.

J'espère que c'est clair et que je dis pas (trop) de conneries (je compte sur malalam pour me taper sur les doigts le cas échéant).
Messages postés
1123
Date d'inscription
mardi 8 janvier 2002
Statut
Modérateur
Dernière intervention
21 avril 2009
1
mais tu t'abstrait pas du type d'image aussi ?
ta classe concrete pour chaque mime type, comment tu fait pour les fonctions
je m'explique, pour png, tu a createFromPng, jpg: createFromJpeg, etc, mais ca, c'est par rapport aux fonctions gd.
C'est là que je bloque dans ta classe en fonction du mime type, elle utilise les fonctions de la classe abstraite ? mais alors pourquoi ne pas l'étendre ?

Parce que comme tu le dit, les fonctions ne sont pas les memes, le but, c'est de généraliser l'usage., pourquoi ne pas faire "aussi" une classe abstraite mimetype (par exemple), qui contient les fonctions de type createImage
Chaques classes (png, jpeg, gif) étends mimetype, et la fonction abstraite createImage, appel une fonction fille _createImage qui contient createFromJpeg  pour jpeg, createFromPng pour png, etc.

De plus, tu dit
- une classe abstraite par logiciel (GD, ImageMagick, etc), qui étend la classe sus-citée

- une classe ImageHandler qui étend la classe précédente

mais alors il faut faire n classe imageHandler, une pour chaque logiciel, vu que c'est une classe abstraite ? (la base de mon probleme :p)

Enfin bref pour moi c'est pas clair du tout :p désolé mais si t'es motivé tu peux expliquer plus ? :p
T'a compris là ou je bloque ? (au cas ou : c'est que le type doit être intimement lié à la classe abstraite car les fonctions dépendent du logiciel, et comme chaque type à des fonctions unique, il faut séparer les types aussi pour s'en abstraire) (d'apres-moi)

Ton avis ?
Messages postés
10839
Date d'inscription
lundi 24 février 2003
Statut
Modérateur
Dernière intervention
2 mars 2010
25
Re,

"C'est là que je bloque dans ta classe en fonction du mime type, elle
utilise les fonctions de la classe abstraite ? mais alors pourquoi ne
pas l'étendre ?"
=> c'est une sorte de client, ton interface. Elle utilise les autres classes, en te fournissant une interface unique, sans que tu aies besoin d'aller instancier LA classe qu'il faut en fonction du type d'image.

"Parce que comme tu le dit, les fonctions ne sont pas les memes, le but,
c'est de généraliser l'usage., pourquoi ne pas faire "aussi" une classe
abstraite mimetype (par exemple), qui contient les fonctions de type
createImage
Chaques classes (png, jpeg, gif) étends mimetype, et la
fonction abstraite createImage, appel une fonction fille _createImage
qui contient createFromJpeg  pour jpeg, createFromPng pour png, etc."
=> Justement, de la manière proposée par Neige, tu n'as même pas besoin de sélectionner le type. Pourquoi passer par une classe abstraite pour le mime type ? Tu n'auras aucune méthode commune, et si tu en as, elles se situeront dans ta première classe abstraite. La manière que tu proposes est plus lourde, simplement. Si ce n'était que pour imageCreateFromJpeg et cie, ça ne servirait à rien de toute manière, ni la méthode de Neige ni la tienne (dans cet exemple précis hein...mais ça s'applique à d'autres!) : on pourrait générer les méthodes dynamiquement avec create_function(). Mais il y a d'autres problèmes : la gestion de la transparence par exemple...totalement inutile pour les jpeg.

"mais alors il faut faire n classe imageHandler, une pour chaque
logiciel, vu que c'est une classe abstraite ? (la base de mon probleme
:p)"
=> Oui, il y a erreur de conceptualisation, là. La classe handler ne doit pas étendre ces classes abstraites. Elle doit fonctionner comme une usine (factory): tu lui donnes en paramètre l'extension à utiliser (GD, IMAGICK), et elle va la "charger". Il FAUT que tes classes abstraites implémentent les mêmes méthodes.
Un exemple tout con, imagine cette interface :
interface output {
    public function write($sContents);
    public function read($sContents);
    public function create($sCreation);
    public function finish($sFinishName = null);
}
Cette interface s'adapte à...la création d'un document PDF, DOC, TXT, etc...à un email (avec son envoi!), à une entrée dans une base de données, à la création d'un flux virtuel (échange entre 2 systèmes de type xml ou autre) etc...
Bon faudrait la peaufiner hein...mais le principe est là. Le but est d'avoir une interface commune pour des classes distinctes.

Il y a d'autres solutions :
http://www.phpcs.com/tutoriaux/DESIGN-PATTERNS_735.aspx
Regarde le design pattern DECORATOR.
Messages postés
1123
Date d'inscription
mardi 8 janvier 2002
Statut
Modérateur
Dernière intervention
21 avril 2009
1
rololo !!
je comprends pas grand chose au début de ton explication Malalam :/
"c'est une sorte de client, ton interface. Elle utilise les autres
classes, en te fournissant une interface unique, sans que tu aies
besoin d'aller instancier LA classe qu'il faut en fonction du type
d'image."
Qui est l'interface ? qui est le client ?

Ce serait possible d'avoir le débat sous forme de diagramme ? histoire que je saisisse un peu mieu l'idée de neigedhiver, mais en corrigé de Malalam ? car là je plane totalement
Moi je le vois comme ca

une classe abstraite imageMain qui implémente toutes (a peu pres) fonctions de gd (et donc de imagemagick (à peu de choses pres)), dont createFromJpeg, createFromPng, etc.
Une classe fille libGd, libIm, etc

Ensuite on aura une classe imageHandler, qui prends en paramètre le type de fichier et une instance d'une fille de imageMain
Cette classe imageHandler effectuera les actions sur imageMain en fonction du type donné.

Est-ce bien comme cela que vous le voyez ?
Moi je le vois, ... flllllooouuuuuuu ;)

Merci de votre aide:)
Messages postés
1123
Date d'inscription
mardi 8 janvier 2002
Statut
Modérateur
Dernière intervention
21 avril 2009
1
okayyyy !!!!!!!!!!!!!

(ca va comme réponse ?)

Ouah c'était dur ! :p
Merci beaucoup beaucoup ! de votre aide ! :)

L'affaire est résolue (à mon gout)

Merci encore à vous deux !