J'écris une application qui aura une Image
entité et j'ai déjà du mal à décider à qui incombera chaque tâche.
J'ai d'abord la Image
classe. Il a un chemin, une largeur et d'autres attributs.
Ensuite , je créé une ImageRepository
classe, pour récupérer des images avec une méthode simple et testé, par exemple: findAllImagesWithoutThumbnail()
.
Mais maintenant, je dois aussi pouvoir createThumbnail()
. Qui devrait s'en occuper? Je pensais avoir une ImageManager
classe, qui serait une classe spécifique à l'application (il y aurait aussi un composant réutilisable de manipulation d'image tiers de choix, je ne réinvente pas la roue).
Ou peut-être que ce serait 0K pour laisser le Image
redimensionnement lui-même? Ou laissez le ImageRepository
et ImageManager
être la même classe?
Qu'est-ce que tu penses?
la source
Réponses:
La question posée est trop vague pour avoir une vraie réponse car elle dépend vraiment de la façon dont les
Image
objets vont être utilisés.Si vous n'utilisez l'image qu'à une seule taille et que vous redimensionnez car l'image source n'est pas de la bonne taille, il peut être préférable que le code lu effectue le redimensionnement. Demandez à votre
createImage
méthode de prendre une largeur / hauteur, puis de renvoyer l'image ayant été redimensionnée à cette largeur / hauteur.Si vous avez besoin de plusieurs tailles, et si la mémoire n'est pas un problème, il est préférable de conserver l'image en mémoire telle qu'elle a été lue à l'origine et d'effectuer un redimensionnement au moment de l'affichage. Dans un tel cas, vous pouvez utiliser plusieurs modèles différents. L'image
width
etheight
serait fixe, mais vous auriez soit unedisplay
méthode qui prendrait une position et une hauteur / largeur cible, soit une méthode qui prendrait une largeur / hauteur qui renvoyait un objet qui, quel que soit le système d'affichage que vous utilisez. La plupart des API de dessin que j'ai utilisées vous permettent de spécifier la taille cible au moment du dessin.Si les exigences entraînent un dessin de différentes tailles suffisamment souvent pour que les performances soient un problème, vous pouvez avoir une méthode qui crée une nouvelle image d'une taille différente basée sur l'original. Une alternative serait d'avoir votre
Image
classe cache différentes représentations en interne afin que la première fois que vous appelezdisplay
avec la taille de la vignette, il redimensionne tandis que la deuxième fois, il dessine simplement la copie en cache qu'il a enregistrée la dernière fois. Cela utilise plus de mémoire, mais il est rare que vous ayez plus de quelques redimensionnements courants.Une autre alternative consiste à avoir une seule
Image
classe qui conserve l'image de base et à ce que cette classe contienne une ou plusieurs représentations. UnImage
lui-même n'aurait pas de hauteur / largeur. Au lieu de cela, il commencerait par celuiImageRepresentation
qui avait une hauteur et une largeur. Vous dessinez cette représentation. Pour «redimensionner» une image, vous demanderiezImage
une représentation avec certaines mesures de hauteur / largeur. Cela la ferait alors contenir cette nouvelle représentation ainsi que l'original. Cela vous donne beaucoup de contrôle sur exactement ce qui traîne en mémoire au prix d'une complexité supplémentaire.Personnellement, je n'aime pas les classes qui contiennent le mot
Manager
parce que "manager" est un mot très vague qui ne vous dit pas grand-chose sur ce que fait la classe. Gère-t-il la durée de vie des objets? Se situe-t-il entre le reste de l'application et la chose qu'il gère?la source
Gardez les choses simples tant qu'il n'y a que quelques exigences et améliorez votre conception lorsque vous le devez. Je suppose que dans la plupart des cas réels, il n'y a rien de mal à commencer avec un design comme celui-ci:
Les choses peuvent devenir différentes lorsque vous devez transmettre plus de paramètres à
createThumbnail()
, et ces paramètres ont besoin d'une durée de vie qui leur est propre. Par exemple, supposons que vous allez créer des miniatures pour plusieurs centaines d'images, toutes avec une taille de destination, un algorithme de redimensionnement ou une qualité donnés. Cela signifie que vous pouvez déplacer lecreateThumbnail
dans une autre classe, par exemple, une classe de gestionnaire ou uneImageResizer
classe, où ces paramètres sont passés par le constructeur et sont donc liés à la durée de vie de l'ImageResizer
objet.En fait, je commencerais par la première approche et refaçonnerais plus tard quand j'en aurais vraiment besoin.
la source
ImageResizer
classe en premier lieu? Quand déléguez-vous un appel au lieu de déplacer la responsabilité dans une nouvelle classe?Image thumbnail = img.createThumbnail(x,y)
.Je pense que cela devrait faire partie de la
Image
classe, car le redimensionnement dans une classe externe nécessiterait que le redimensionneur connaisse la mise en œuvre duImage
, violant ainsi l'encapsulation. Je suppose queImage
c'est une classe de base, et vous vous retrouverez avec des sous-classes distinctes pour les types d'images concrètes (PNG, JPEG, SVG, etc.). Par conséquent, vous devrez soit avoir des classes de redimensionnement correspondantes, soit un redimensionneur générique avec uneswitch
instruction qui se redimensionne en fonction de la classe d'implémentation - une odeur de conception classique.Une approche pourrait être de demander à votre
Image
constructeur de prendre une ressource contenant l'image, les paramètres de hauteur et de largeur et de se créer de manière appropriée. Le redimensionnement peut alors être aussi simple que la création d'un nouvel objet à l'aide de la ressource d'origine (mise en cache dans l'image) et des nouveaux paramètres de taille. Par exemplefoo.createThumbnail()
, tout simplementreturn new Image(this.source, 250, 250)
. (avecImage
le type concret defoo
, bien sûr). Cela garde vos images immuables et leurs implémentations privées.la source
Image
. Tout ce dont il a besoin, c'est de la source et des dimensions cibles.Je sais que la POO consiste à encapsuler des données et un comportement ensemble, mais je ne pense pas que ce soit une bonne idée pour une image d'avoir la logique de redimensionnement intégrée dans ce cas, car une image n'a pas besoin de savoir comment se redimensionner pour être une image.
Une vignette est en fait une image différente. Peut-être pourriez-vous avoir une structure de données qui détient la relation entre une photographie et sa miniature (qui sont toutes deux des images).
J'essaie de diviser mes programmes en choses (comme des images, des photographies, des miniatures, etc.) et des services (comme PhotographRepository, ThumbnailGenerator, etc.). Obtenez vos bonnes structures de données, puis définissez les services qui vous permettent de créer, manipuler, transformer, persister et récupérer ces structures de données. Je ne mets pas plus de comportement dans mes structures de données que de m'assurer qu'elles sont créées correctement et utilisées de manière appropriée.
Par conséquent, non, une image ne doit pas contenir la logique de création d'une miniature. Il devrait y avoir un service ThumbnailGenerator qui a une méthode comme:
Ma plus grande structure de données pourrait ressembler à ceci:
Bien sûr, cela pourrait signifier que vous faites des efforts que vous ne voulez pas faire lors de la construction de l'objet, donc je considérerais aussi quelque chose comme ça:
... dans le cas où vous souhaitez une structure de données avec évaluation paresseuse. (Désolé, je n'ai pas inclus mes vérifications nulles et je ne l'ai pas rendu thread-safe, ce que vous voudriez si vous essayiez d'imiter une structure de données immuable).
Comme vous pouvez le voir, l'une ou l'autre de ces classes est construite par une sorte de PhotographRepository, qui a probablement une référence à un ThumbnailGenerator obtenu via l'injection de dépendances.
la source
Vous avez identifié une seule fonctionnalité que vous souhaitez implémenter, alors pourquoi ne devrait-elle pas être distincte de tout ce que vous avez identifié jusqu'à présent? C'est ce que le principe de responsabilité unique suggérerait est la solution.
Créez une
IImageResizer
interface qui vous permet de passer une image et une taille cible, et qui renvoie une nouvelle image. Créez ensuite une implémentation de cette interface. Il existe en fait des tonnes de façons de redimensionner les images, vous pouvez donc vous retrouver avec plusieurs!la source
Je suppose que ces faits sur la méthode, qui redimensionne les images:
Sur la base de ces faits, je dirais qu'il n'y a aucune raison pour que la méthode de redimensionnement d'image fasse partie de la classe d'image elle-même. Il serait préférable de l'implémenter comme méthode auxiliaire statique de classe.
la source
Une classe de traitement d'image peut être appropriée (ou Image Manager, comme vous l'avez appelé). Passez votre image à une méthode CreateThumbnail du processeur d'image, par exemple, pour récupérer une image miniature.
L'une des raisons pour lesquelles je suggère cette voie est que vous dites que vous utilisez une bibliothèque de traitement d'images tierce. La suppression de la fonctionnalité de redimensionnement de la classe Image elle-même peut vous permettre d'isoler plus facilement tout code spécifique à une plate-forme ou tiers. Donc, si vous pouvez utiliser votre classe Image de base sur toutes les plates-formes / applications, vous n'avez pas à la polluer avec du code spécifique à la plate-forme ou à la bibliothèque. Tout cela peut être localisé dans le processeur d'image.
la source
Fondamentalement, comme Doc Brown l'a déjà dit:
Créez une
getAsThumbnail()
méthode pour la classe d'image, mais cette méthode devrait en fait simplement déléguer le travail à uneImageUtils
classe. Donc, cela ressemblerait à quelque chose comme ceci:Et
Cela permettra un code plus facile à consulter. Comparez les éléments suivants:
Ou
Si ce dernier vous semble bon, vous pouvez également vous limiter à créer cette méthode d'assistance quelque part.
la source
Je pense que dans "Image Domain", vous avez juste l'objet Image qui est immuable et monadique. Vous demandez à l'image une version redimensionnée et elle renvoie une version redimensionnée d'elle-même. Ensuite, vous pouvez décider si vous souhaitez vous débarrasser de l'original ou conserver les deux.
Maintenant, les versions miniature, avatar, etc. de l'image sont un autre domaine entièrement, qui peut demander au domaine image différentes versions d'une certaine image à donner à un utilisateur. Habituellement, ce domaine n'est pas aussi énorme ou générique, vous pouvez donc probablement le conserver dans la logique d'application.
Dans une application à petite échelle, je redimensionnais les images au moment de la lecture. Par exemple, je pourrais avoir une règle de réécriture apache qui délègue à php un script si l'image 'http://my.site.com/images/thumbnails/image1.png', où le fichier sera récupéré en utilisant le nom image1.png et redimensionné et stocké dans «vignettes / image1.png». Ensuite, à la prochaine demande de cette même image, apache servira l'image directement sans exécuter le script php. Apache répond automatiquement à votre question sur findAllImagesWithoutThumbnails, à moins que vous n'ayez besoin de faire des statistiques?
Dans une application à grande échelle, j'enverrais les toutes nouvelles images à un travail d'arrière-plan, qui prend soin de générer les différentes versions de l'image et l'enregistre aux endroits appropriés. Je ne prendrais pas la peine de créer un domaine ou une classe entière car ce domaine est très peu susceptible de se transformer en un terrible gâchis de spaghettis et de mauvaise sauce.
la source
Réponse courte:
Ma recommandation ajoute cette méthode à la classe d'images:
L'objet Image est toujours immuable, ces méthodes renvoient une nouvelle image.
la source
Il y a déjà pas mal de bonnes réponses, donc je vais développer un peu une heuristique derrière la façon d'identifier les objets et leurs responsabilités.
La POO diffère de la vie réelle en ce que les objets de la vie réelle sont souvent passifs et en POO, ils sont actifs. Et c'est un noyau de la pensée objet . Par exemple, qui redimensionnerait une image dans la vraie vie? Un humain, intelligent à cet égard. Mais en POO, il n'y a pas d'humains, donc les objets sont plutôt intelligents. Une façon de mettre en œuvre cette approche «centrée sur l'humain» dans la POO consiste à utiliser des classes de service, par exemple, des
Manager
classes notoires . Ainsi, les objets sont traités comme des structures de données passives. Ce n'est pas une façon OOP.Il y a donc deux options. La première, la création d'une méthode
Image::createThumbnail()
, est déjà envisagée. La seconde consiste à créer uneResizedImage
classe. Il peut être un décorateur d'unImage
(cela dépend de votre domaine de conserverImage
ou non une interface), bien que cela entraîne des problèmes d'encapsulation, car ilResizedImage
devrait avoir uneImage
source. Mais unImage
ne serait pas submergé par le redimensionnement des détails, le laissant à un objet de domaine séparé, agissant conformément à un SRP.la source