Las clases abstractas en la programación orientada a objetos son clases de las que no se pueden crear instancias/objetos directamente de ella.
¿Para qué defino una clase como abstracta si no puedo crear objeto de ella?
Si puedes crear instancias pero solo de clases que hereden dicha clase (que no sean abstractas por supuesto). El objetivo principal de la herencia es mejorar la clase heredada. Así que sabiendo esto, las clases abstractas en su más pura esencia son clases mejorables!
Una clase abstracta puede tener métodos y propiedades como cualquier otra clase.
¿Cuando debo definir una clase abstracta?
Cuando puedas crear diferentes clases que tengan propiedades y métodos comunes, y aún sabiendo que en el futuro que puedes crear más clases del mismo tipo.
En la clase abstracta se definirían las propiedades y métodos comunes, para que las clases que la hereden no tengan la necesidad implementar esos métodos o definir las propiedades.
Métodos Abstractos
Así como hay clases abstractas, también hay métodos abstractos. Estos métodos no son implementados en su definición, pero obliga a las clases que hereden el método a implementarlo.
Un método abstracto se puede definir en casi cualquier clase, no necesariamente debe ser una clase abstracta. Un ejemplo en las que no se deberían definir métodos abstractos serian en las clases finales, pues no se podrían implementar.
Las clases finales: son las clases engreídas en la POO su lema principal: ¡Nadie me puede mejorar!. Es decir, que no se pueden heredar. Entonces se podría decir que son las opuestas a las clases abstractas que son la de baja autoestima su lema: "Soy inútil sola, mejorame por favor ".
Redimensionar imágenes en PHP usando clase abstracta
Definimos una clase abstracta llamada ImageBase. Todas las clases que hereden esta clase tendrán:
- Las propiedades width y height, con sus respectivos setters y getters.
- Además de dos metodos comunes los cuales son: gdFromFile y gdToFile.
- Obliga a la clase que le herede a implementar el metodo save
Código :
abstract class ImageBase { protected $width; protected $height; public function setWidth($w) { // gd solo maneja enteros, ergo obligamos que ancho sea entero. $w = (int) $w; // ancho debe ser mayor que 0 if ($w > 0) $this->width = $w; } public function getWidth() { return $this->width; } public function setHeight($h) { // gd solo maneja enteros, ergo obligamos que alto sea entero $h = (int) $h; // alto debe ser mayor que 0 if ($h > 0) $this->height = $h; } public function getHeight() { return $this->height; } /** * Genera una imagen gd del archivo con nombre $filename * Retorna FALSE si ocurrior algun error, por ejemplo: el tipo no es soportado * * @param string $filename nombre del archivo * @param int $type Tipo de imagen para saber que funcion usar * @return resource Una imagen gd. */ protected function gdFromFile($filename, $type) { $gd = false; switch ($type) { case IMAGETYPE_PNG: $gd = imagecreatefrompng($filename); break; case IMAGETYPE_JPEG: $gd = imagecreatefromjpeg($filename); break; case IMAGETYPE_GIF: $gd = imagecreatefromgif($filename); break; } return $gd; } /** * Guarda una imagen gd en el archivo de nombre $filename * * @param resource $gd La imagen a guardar * @param string $filename nombre del archivo * @param int $type Tipo de imagen para saber que funcion usar * @return bool TRUE en caso de exito, FALSE en caso contrario * */ protected function gdToFile($gd, $filename, $type) { $success = false; // si $filename es nulo las funciones posteriores imprimiran en la salida directamente // aqui tratamos de evitar eso $filename = (string) $filename; if (trim($filename) != "") { // no tiene sentido verificar si el archivo existe, pues si no existe se creara // las siguientes funciones retornan false si ocurrio algun error, true en caso de exito switch ($type) { case IMAGETYPE_PNG: $success = imagepng($gd, $filename); break; case IMAGETYPE_GIF: $success = imagegif($gd, $filename); break; case IMAGETYPE_JPEG: $success = imagejpeg($gd, $filename); break; } } return $success; } // Obligamos a que las clases que hereden esta clase implementen este método /** * La intencion de este metodo es que guarde la imagen creada en un archivo * * @param string $filename Nombre del archivo * @return bool TRUE en caso de exito, FALSE en caso contrario */ abstract public function save($filename); }
Creamos ImageResize que hereda ImageBase.
Código :
class ImageResize extends ImageBase { private $src; private $origWidth; private $origHeight; private $origType; private $hasError = false; public function __construct($src) { $this->setSrc($src); } private function setSrc($src) { if (is_file($src)) { // getimagesize retorna un arreglo si tuvo exito con la informacion de la imagen // false en caso contrario $info = getimagesize($src); if ($info !== FALSE) { $this->src = $src; $this->origWidth = $info[0]; // ancho de la imagen $this->origHeight = $info[1]; // alto de la imagen $this->origType = $info[2]; // constante de php que tiene el tipo de imagen, un entero // por defecto usaremos las dimensiones de la imagen original $this->resize($this->origHeight, $this->origHeight); } else { $this->throwError("$src is not an image file", E_USER_ERROR); } } else { $this->throwError("$src is not file valid", E_USER_ERROR); } } /** * Asigna los valores a los que se redimensionara la imagen * * @param int $w ancho * @param int $h alto */ public function resize($w, $h) { if ($w < 1) $this->throwError("Ancho debe ser mayor que 0", E_USER_NOTICE); if ($h < 1) $this->throwError("Alto debe ser mayor que 0", E_USER_NOTICE); $this->setWidth($w); $this->setHeight($h); } /** * Redimensiona la imagen con el ancho y alto asignado en resize * y la guarda en el archivo de nombre $filename * * @param string $filename nombre del archivo * @return bool TRUE en caso de exito, FALSE si algo salio mal */ public function save($filename) { $success= false; // obtenemos la imagen en gd del archivo $orig = $this->gdFromFile($this->src, $this->origType); if ($gd !== FALSE) // si lo obtuvimos { // creamos una imagen vacia con ancho y alto, servira de contenedor $base = imagecreatetruecolor($this->width, $this->height); // aqui redimensionamos la imagen // la imagen redimensionada queda en $base, esta funcion retorna TRUE si tuvo exito, FALSE en caso contrario $resized = imagecopyresampled($base, $orig, 0, 0, 0, 0, $this->width, $this->height, $this->origWidth, $this->origHeight); if ($resized) // pudimos redimensionar { // guardamos gd en el archivo $filename if (!$this->gdToFile($base, $filename, $this->origType)) { $this->throwError("Archivo no generado", E_USER_WARNING); } else { // todo salio bien $success = true; // liberamos los recursos gd imagedestroy($base); imagedestroy($orig); } } } else { $this->throwError("Gd no fue generado.", E_USER_WARNING); } return $success; } private function throwError($msg, $level) { trigger_error($msg, $level); } } ?>
Uso de ImageResize
Código :
<?php $src = "archivo.jpg"; $dest = "archivo150x150.jpg"; $resize = new ImageResize($src); $resize->resize(150,150); if (!@$resize->save($dest)) { echo "Archivo no fue generado. Error: " . error_get_last(); } else { echo "Archivo $dest generado."; } ?>
¿Que otra clase pudiera heredar ImageBase?
¿Que tantas clases puede crear con la libreria gd de php?
- Captcha: una clase que genere captcha.
- Gráficos: una clase que genere gráficos.
- Thumbs: si no te gusta ImageResize, mejorala o crea tu propia clase.
¿Se te ocurre una otra? Comenta aquí.
Nota: la implementación de ambas clases se pueden mejorar. La única intención de estas implementaciones es mostrar un ejemplo real de como se deben usar y lo útil que son las clases abstractas.
¿Sabes SQL? ¿No-SQL? Aprende MySQL, PostgreSQL, MongoDB, Redis y más con el Curso Profesional de Bases de Datos que empieza el martes, en vivo.
No estoy seguro porque deba ser abstracta Maik, bien puede ser una utileria que se invoca con el path de la imagen y las dimensiones, no estoy seguro que convenga que sea abstracta. Pero bueno, es algo que podemos charlar despues.
Y te recomiendo que en lugar del trigger error, dispares excepciones.
Código :
El trigger_error es mas del viejo php, las excepciones son mas OOP pro. =)
saludos
Dano :
Creo que Maikel lo hace más como para ilustrar el ejemplo a que tenga que hacerse precisamente así, de todas formas en programación por lo general hay muchas maneras "buenas" de resolver un problema.
Por ejemplo en mi clase de imágenes yo me fui más por el patrón de opciones, mi constructor recibe un nombre de archivo de imagen y crea la imagen de acuerdo a si es jpg o png, etc. y un solo metodo: resampleImage recibe el nombre de la nueva ruta de la imagen y un array "$imgValues" que contiene diferentes opciones como new_width, new_height, watermark incluso "method" que permite definir si se debe cortar la imagen para que quede del new_width y new_height exacto o a escalarla solamente, etc.
Ahora si Maikel me dijera que debo trabajar con su clase
le diría a maikel que apestatomaría mi metodo y lo dividiría en varias clases de manera de tener algo así:class ImageResize
class ImageSmartResize
class ImageThumb
class ImageCrop
class ImageWatermark
Y si quisiera redimensionar y colocarle watermark a una imagen usaría 2 clases (ahora que lo pienso debería haber una forma de que una clase que herede de Image reciba otra clase para hacer algo así como el Decorator Pattern)
Código :
***
Otra cosa de las clases abstractas no es sólo que sean mejorables es que a veces no tiene sentido crear una instancia de una clase, por ejemplo una clase Controller no tiene sentido si no es un Controlador de un modulo especifico, por ej UsuariosController tiene sentido, por lo tanto los Controladores podrían ser clase Abstracta. También puede aplicar esto a un modelo, por ej en Doctrine, Doctrine_Record es abstracta, dado que por sí solo un Record no tiene sentido si no tiene fields (usuario, password, etc.) Entonces Doctrine_Record implementa las funcionalidades y en una clase que extienda de esta por ej "Usuarios" se implementan los campos que le dan sentido a la clase...
Creo que mi comentario ya está más largo que el tip así que ya no sigo =P
(Espero haber ayudado)
Por Aoyama el 10 de Octubre de 2010
daz_angie :
thanks
Aoyama :
Cool! Eso es lo que yo buscaba con los códigos que tomaran una idea y la mejoraran haciendo sus propias clases. Si necesitas ayuda en algo para implementar tu idea estoy a la orden
saludos
Por siddharta el 12 de Octubre de 2010
Por alexx855 el 19 de Marzo de 2012
Por Pepe el 04 de Abril de 2012
Dándole vueltas a la cabeza se me ocurrió una idea que posiblemente os ayudaría:
//Obtenemos la lista de getimagesize del ancho y el alto sobre el archivo subido
list($ancho, $altura, $tipo, $atr)=getimagesize($nombreCompleto);
//Comprobamos que el ancho es mayor que 800 y que el ancho es mayor que la altura
if ($ancho>800 && $ancho>$altura)
{
while ($ancho>800)
{
$ancho=$ancho*95/100;
$altura=$altura*95/100;
}
}
//Hacemos lo mismo con la altura
if ($altura>600 && $altura>$ancho)
{
while ($altura>600)
{
$ancho=$ancho*95/100;
$altura=$altura*95/100;
}
}
Por Pepe el 04 de Abril de 2012
$resize = new ImageResize($src);
$resize->resize($ancho, $altura);
Por juan el 03 de Julio de 2012
Por JOSE FERNANDEZ el 07 de Agosto de 2014
Por elkaso el 15 de Octubre de 2018
Fatal error: Class 'ImageResize' not found in C:\AppServ\www.....
desde ya, muchas gracias
Por solisarg el 15 de Octubre de 2018
elkaso: estas incluyendo la clase (via include o use) desde el archivo en que lo estas usando?
Por elkaso el 16 de Octubre de 2018
un saludo desde españa.