Você está na página 1de 50

Instituto Tecnológico de Costa Rica

Diseño de Software

Apuntes 01

Arquitectura de Software

Profesora: Ericka Solano

Grupo 40

Integrantes:

Ana Rojas Rodríguez 2015127223


Isaac Rivera Coto 2013015828

II Semestre 2018
Diseño de software
El objetivo de la arquitectura es tratar de interferir en la vida del hombre
trabajando a su favor, esta debe ser la respuesta a un problema y no una
imposición. Por lo que, la principal función del arquitecto de software es ayudar al
programador a dar los pasos necesarios para la creación del software. además la
idea es utilizar algo que sea de verdad útil, ya que el poder hacerlo no justifica el
hacerlo, o visto de otra forma, no crear problemas para justificar malas soluciones.
Además una aplicación sin “plano” es caminar sin un rumbo, por eso la arquitectura
es la base de un sistema de software.

El diseño del software debe plantearse tomando en cuenta: las necesidades,


condiciones, personas, conocimiento, presupuesto , tiempo y además se debe
discernir las características importante de las que no lo son, todos estos aspectos
van a variar dependiendo del producto solicitado. El problema de no considerarlos
es que el programador puede ejecutar sus propias criterios y al darle mantenimiento
a una aplicación o realizar cambios significativos hace que sea un trabajo muy
complicado. Entonces las personas encargadas de estas tareas recurren a
reconstruir partes o duplicar código.
Entonces el diseño de software tiene el deber de que la aplicación satisfaga las
necesidades actuales del cliente, además que permita su mantenimiento y evolución
según las necesidades del negocio. La base del sistema es justamente el diseño
creado por el arquitecto, este debe ser estrictamente lo importante y crítico, nada de
agregarle al sistema funciones irrelevantes. “Lo justo es lo correcto”.

Antes se mencionó los aspectos que el arquitecto de software debe tomar en


cuenta a la hora de crear el diseño de una aplicación, los cuales son:
● Necesidades: ​es identificar los requerimientos funcionales y no funcionales,
determinando las herramientas y estrategia que debe seguir para satisfacer
los requerimientos.
● Condiciones: ​es determinar que si lo que pide el cliente es lo que realmente
necesita, reconocer las habilidades de su equipo de trabajo y establecer si las
tecnologías disponibles permiten el desarrollo del sistema.
● Conocimiento: ​es comprender todas las consideraciones a las que el
sistema está sometido y crear una propuesta acorde al presupuesto, tiempo,
un equipo de desarrollo preparado para seguir las indicaciones del arquitecto
y otras particularidades del escenario.

Los arquitectos de software utilizan los diagramas para representar el diseño y


distribución del sistema, mostrando diferentes puntos de vista y condiciones que
existen entorno a la aplicación.
Rol del programador
Los arquitectos de software son los personajes menos valorados y más
importantes de las empresas. Entre las más importantes tareas de un arquitecto de
software es crear un proyecto y acompañar en cada fase, manteniendo el control y
participando como un intermediario entre los distintos departamentos por los que el
proyecto deba pasar. Este puesto debe mantener finas habilidades técnicas
necesarias para la preparación del diseño del sistema y blandas relevantes para la
comunicación con otros departamentos, así como para tomar todas las
consideraciones y seguir centrados en lo importante del sistema. No todas las
empresas tienen o contratan un arquitecto de software, puede ser porque el
proyecto es pequeño o porque son pequeñas o medianas empresas y al tener este
puesto aumenta el costo. Sin embargo, puede que a largo plazo el proyecto sea
más caro, pues si un sistema no se puede mantener pronto se tendrá que crear uno
nuevo, los arquitectos de software tiene características que todo programador debe
poner en práctica y mantener un estándar que facilite el trabajo a todos los
interesados en el sistema y diseño creado.

El rol o posición laboral del arquitecto de software es la persona que se encarga


de los requerimientos de un sistema, los cuales es importante definir para la
creación o diseño de un sistema, cumpliendo a su vez con los diferentes estándares
de calidad, además este puede dar seguimiento al desarrollo de una respuesta.
Para las diferentes funciones que estos deben cumplir es importante que posean
ciertas características, las cuales podemos nombrar a continuación:

❖ Comprensión del dominio del problema, con tal de brindar una respuesta
acertada a una problemática.
❖ Conocimiento abstracto y la capacidad de conceptualizar.
❖ Métodos probados lo que puede consistir en la comprensión de patrones o
nuevas tecnologías probadas para la resolución de problemas de naturaleza
similar.
❖ Perspicacia técnica.

La famosa frase “todo poder conlleva una gran responsabilidad” es


perfectamente aplicada a este caso, los arquitectos de software tienen la capacidad
de tomar decisiones importantes sobre el sistema a crear, así si toman malas
decisiones y el desarrollo de la aplicación se complica son los primeros
responsables.

Habilidades blandas del arquitecto de software

Naturalmente estas son habilidades que no podemos dejar atrás, habilidades


que cualquier profesional en cualquier área debe poseer para realizar un buen
trabajo entre estas consideramos las siguientes:
1. Comunicación: esta es una habilidad fundamental para el arquitecto de
software, ya que debe transmitir su solución a diferentes personas en
diferentes etapas de proyectos.
2. Toma de decisiones: Siendo este del encargado de seleccionar tecnologías
o patrones para resolver problemas esta es una habilidad con mucha
influencia en este rol. a pesar de que muchas más pueden estar presentes
estas dos se consideran fundamentales.
Generalmente en un proyecto de software pensaríamos que lo más importante
es el código que desarrollamos, ignorando otras partes de vital importancia tales
como la documentación o la implementación de metodologías ágiles, pues esto es
un grave error que nos puede conducir a la pérdida de dinero, tiempo y otros
recursos disponibles, esto se da por el corto alcance que posee la visión del
desarrollador, situación que no está mal del todo ya que su trabajo como su nombre
lo dice es desarrollar y no ver más adelante en las etapas del proyecto, el problema
es que esto puede incurrir en errores de programación y como ya hemos
mencionado. Para solventar este problema que acabamos de exponer es que se
encuentran los arquitectos, debido a que su labor se enfoca en generar un plan a
seguir en el que los objetivos del proyecto sean prioritarios, de manera que el
programador pueda cumplir con su trabajo con plena tranquilidad y confianza de que
su tiempo no es desperdiciado y no es en vano.

El arquitecto debe ser capaz de transmitir satisfactoriamente su plan, ideas,


soluciones y demás información que pueda afectar al proyecto.El arquitecto de
software no son tan bien valorados en varias empresas debido a la falta de
presupuesto o de toma de decisiones. Pero son los más importantes en cuanto al
desarrollo de proyectos y productividad de la empresa.
Definiendo Diseño
Como anteriormente lo expresamos, el diseño es una forma de solucionar
problemas, por eso a la hora de diseñar tomamos en cuenta diferentes factores o
nociones con el fin de brindar una respuesta que satisfaga todas las alternativas,
restricciones y objetivos del proyecto.

Esta disciplina tiene un papel clave en el desarrollo de software debido a que le


permite al desarrollador software a realizar diversos modelos que se caracterizan
por factores como:
● Pueden ser analizados y evaluados con el fin de confirmar que su respuesta
satisface adecuadamente el problema.
● Facilitan el examen y evaluación de alternativas.
● Sirve para planificar las siguientes etapas del desarrollo.
Perspectiva del proceso: El diseño busca delimitar o definir la diferentes
componentes que llegan a constituir un proyecto, siempre tomando en cuenta los
requisitos para producir una descripción de la estructura interna que pueda ser
utilizada como base para su construcción dando como resultado un conjunto de
modelos y artefactos que registran las principales decisiones.

Perspectiva del resultado: El diseño es el resultado de dicho esfuerzo, el cual


describe la arquitectura del software, las interfaces entre dichos componentes y los
componentes, a un nivel de detalles que permita su construcción.

se identifican dos tipos de diseño de software:


1. Arquitectónico: ​describe la estructura y organización de alto nivel.
2. Detallado:​ describe cada componente y su comportamiento detallado.
Propósito de la arquitectura :
1. Identificar el alto nivel de descomposición sin entrar en detalles de
especificación de interfaces.
2. Crear modelos conceptuales para comunicar a diferentes stakeholders.
3. Crear una arquitectura informal base para su refinamiento.

Diseño arquitectónico: este es el primer paso del diseño de un sistema, su resultado


se conoce como arquitectura de software. durante el diseño arquitectónico es
necesario adoptar decisiones:
● ¿existe una arquitectura genérica que pueda ser usada?
● ¿cómo será distribuido el sistema?
● ¿que estilos arquitectónicos son apropiados?
● ¿que aproximación se utilizará para estructurar el sistema?
● ¿como se descompondrá el sistema en módulos?
● ¿que estrategia de control se utilizará?
● ¿cómo se evaluará el diseño arquitectónico resultado?
● como se documentará la arquitectura?
Apunte 02: Repaso Conceptos Básicos OO
IC6821 Diseño de Software – Grupo 40
II Semestre, 2018

José Ceciliano Granados - 2016087245


Iván López Saborío - 2016116517
Silvia Calderón Navarro - 2015099393
IC6821 Diseño de Software
Apunte 02: Repaso Conceptos Básicos OO

Tabla de contenido
Programación Orientada a Objetos (POO)............................................................................................................................. 2
Pilares de la POO.............................................................................................................................................................. 2
Abstracción.................................................................................................................................................................. 2
Encapsulamiento ......................................................................................................................................................... 2
Herencia ...................................................................................................................................................................... 3
Polimorfismo ............................................................................................................................................................... 3
Clases abstracta e interfaces ............................................................................................................................................ 4
Clases abstractas ......................................................................................................................................................... 4
Interfaces .................................................................................................................................................................... 4
Diagrama de clases............................................................................................................................................................... 6
Contenidos de diagrama de clases.................................................................................................................................... 8
Clases .......................................................................................................................................................................... 8
Modelo de Estudio ............................................................................................................................................................. 11
Problema ....................................................................................................................................................................... 11
Solución ......................................................................................................................................................................... 11
Model ........................................................................................................................................................................ 12
View .......................................................................................................................................................................... 13
Controller .................................................................................................................................................................. 14
Código ........................................................................................................................................................................... 15
Referencias ........................................................................................................................................................................ 16

Página 1 de 16
IC6821 Diseño de Software
Apunte 02: Repaso Conceptos Básicos OO

Repaso Conceptos Básicos OO


Programación Orientada a Objetos (POO)
Forma de resolver, en un enfoque de interacción de elementos, como un concepto de objetos los cuales representan
métodos (acciones) y atributos (características).

FIGURA 1. E JEMPLO DE CLASE

Pilares de la POO

FIGURA 2. LOS 4 PILARES DE LA POO

Abstracción
Pensamiento genérico, es decir que un objeto puede repetirse e interactuar con otros para lograr “objetos” más complejos.

Encapsulamiento
Empacar las clases, es decir generar genéricos que modifican o realizan determinada acciones a los objetos.

Página 2 de 16
IC6821 Diseño de Software
Apunte 02: Repaso Conceptos Básicos OO

Herencia
Un objeto añadido en otro contexto y aumentar características básicas. Un odontólogo, y un estudiante tienen atributos
similares muy a pesar de que se comportan distinto, por lo que podemos tener similitudes y diferencias sin necesidad de
crear dos objetos distintos (Todo lo igual se copia no se crea).

Polimorfismo
Un objeto puede responder de alguna forma diferente dependiendo, del entorno.
Podemos aplicarlo a una forma genérica que lo explica mejor:

FIGURA 3. E XPLICACIÓN POO

La importancia es la reusabilidad de los objetos, para generar un código limpio en menos cantidad de instrucción.
Cohesión es decir que todo esté encapsulado al buscar un objeto, y que están realmente relacionadas, un objeto no
funciona sin otro, generando un alto acople.
Un objeto es una cosa con significado, es decir que tenemos bien limitado cuales son las funciones que realiza y tiene:
• Estado: Es decir puede cambiar constantemente de estados para realizar un fin común.
• Comportamiento: Se comporta de determinada forma y realiza una única acción.
• Identidad: Se identifica de los demás, porque tienen una identidad y un comportamiento distinto a los demás, de
la misma forma un identificador único que suele ser el nombre.

FIGURA 4. E JEMPLO DIFERENCIA ENTRE CLASE Y OBJETO

Página 3 de 16
IC6821 Diseño de Software
Apunte 02: Repaso Conceptos Básicos OO

*Un objeto se puede generalizar casi de cualquier objeto físico o no.


Un objeto, tiene distintas direcciones de memoria, lo que permite generar N cantidad de objetos que se comportan de
maneras distintas, a pesar de que vengan del mismo objeto base.

Clases abstracta e interfaces

Clases abstractas

Una clase abstracta no se puede instanciar y sus métodos no tienen implementación, por lo general se denotan con la
palabra “abtract” y para UML utilizamos itálica para definirla
Cuando deseamos definir una abstracción que englobe objetos de distintos tipos y queremos hacer uso del polimorfismo.

Figura
#X #Y

Cuadrado Círculo
-lado -radio
+área() +área()

Figura es una clase abstracta (nombre en cursiva en UML) porque no tiene sentido calcular su área, pero sí la de un
cuadrado o un
círculo. Si una subclase de Figura no redefine area() solamente la implementa, deberá declararse también como clase
abstracta.

Interfaces

Una interfaz es una clase completamente abstracta (una clase sin implementación)
En el ejemplo anterior, si no estuviésemos interesados en conocer la posición de una Figura, podríamos eliminar por
completo su implementación y convertir Figura en una interfaz:

Página 4 de 16
IC6821 Diseño de Software
Apunte 02: Repaso Conceptos Básicos OO

Figura

+área()

Cuadrado Círculo
-lado -radio
+área() +área()

En la declaración de una interfaz, lo único que puede aparecer son declaraciones de métodos (su nombre y signatura, sin su
implementación) y definiciones de constantes simbólicas.
Una interfaz no encapsula datos, sólo define cuáles son los métodos que han de implementar los objetos de aquellas clases
que implementen la interfaz.

Herencia múltiple de interfaces

Una clase puede implementar varios interfaces simultáneamente, podemos ver un ejemplo a continuación.

Página 5 de 16
IC6821 Diseño de Software
Apunte 02: Repaso Conceptos Básicos OO

Diagrama de clases
Primero que todo estos diagramas se modelan siguiendo una estructura, la cual suele ser UML que es ante todo un lenguaje
que nos permite seguir un estándar en las representaciones gráficas de las clases y los objetos así como sus relaciones.

FIGURA 5. LOGO UML

El UML tiene principios básicos:


• Visualizar: permite expresar de una forma gráfica un sistema de forma que otro lo puede entender.
• Especificar: permite especificar cuáles son las características de un sistema antes de su construcción.
• Construir: A partir de los modelos especificados se pueden construir los sistemas diseñados.
• Documentar: Los propios elementos gráficos sirven como documentación del sistema desarrollado que pueden
servir para su futura revisión.

Página 6 de 16
IC6821 Diseño de Software
Apunte 02: Repaso Conceptos Básicos OO

Los 9 diagramas de UML como ejemplo gráfico:

FIGURA 6. TIPOS DE DIAGRAMAS UML

Página 7 de 16
IC6821 Diseño de Software
Apunte 02: Repaso Conceptos Básicos OO

Contenidos de diagrama de clases


Como pudimos ver un diagrama de clases tiene:

Clases
• Tipos

FIGURA 7. TIPOS DE CLASES

• Visibilidad

Página 8 de 16
IC6821 Diseño de Software
Apunte 02: Repaso Conceptos Básicos OO

• Relaciones
a. Asociación
i. Asociación simple

FIGURA 8. E JEMPLO ASOCIACIÓN SIMPLE


ii. Asociación con dirección

FIGURA 9. E JEMPLO ASOCIACIÓN CON DIRECCIÓN


b. Agregación
Es una relación en la que una de las clases representa un todo y la otra representa una parte de ese todo.

FIGURA 10. E JEMPLO A GREGACIÓN

Página 9 de 16
IC6821 Diseño de Software
Apunte 02: Repaso Conceptos Básicos OO

c. Composición
Es una forma más fuerte de agregación, en la que el todo no puede existir sin sus partes. En el siguiente
diagrama se muestra la clase grupo, donde se puede mostrar que un grupo deja de existir sin el profesor o
el curso, sin embargo si uno de los estudiantes se va, el grupo continúa existiendo.

FIGURA 11. E JEMPLO 1 DE C OMPOSICIÓN

FIGURA 12. E JEMPLO 2 DE C OMPOSICIÓN


d. Dependencia
Representa un tipo de relación muy particular en la que una clase es instanciada. El uso más particular de
este tipo de relación es para denotar la dependencia de USO que tiene una clase de otra.

e. Generalización
Indica que una subclase hereda los métodos y atributos especificados por una super clase. La subclase
además de poseer sus propios métodos y atributos poseerá las características y atributos visibles de la
super clase (public y protected).

Página 10 de 16
IC6821 Diseño de Software
Apunte 02: Repaso Conceptos Básicos OO

Modelo de Estudio
Se plantea el siguiente modelo de estudio con el objetivo de poner en práctica los conceptos, vistos anteriormente, en un
problema concreto.

FIGURA 13. D IAGRAMA DE CLASES SISTEMA ADMINISTRADOR DE USUARIOS

Problema
• Crear un sistema administrador de usuarios con POO.
• Hacer un diseño detallado de un modelo de tipo estático.

Solución
Se utilizó un estilo de arquitectura MVC (Model View Controller), de manera que se tenga bajo acoplamiento entre los
módulos por medio de la descomposición en 3 tipos de responsabilidades. Estas responsabilidades se dividen en:
• El Modelo que contiene una representación de los datos que maneja el sistema, su lógica de negocio, y sus
mecanismos de persistencia.
• La Vista, o interfaz de usuario, que compone la información que se envía al cliente y los mecanismos interacción
con éste.
• El Controlador, que actúa como intermediario entre el Modelo y la Vista, gestionando el flujo de información entre
ellos y las transformaciones para adaptar los datos a las necesidades de cada uno.

Página 11 de 16
IC6821 Diseño de Software
Apunte 02: Repaso Conceptos Básicos OO

FIGURA 14. MVC

A continuación se explica el detalle de cada capa.

Model
En el modelo se crearon las 5 clases de información que se necesita conservar de manera permanente, Usuario, Estado,
Bitácora, Rol y Permiso. Cabe destacar que Estado y Permiso son valores de tipo enumerado, esto se debe a que ambas
tienen un rango finito de valores posibles. Al diseñarlos como enumeraciones se cumple con el principio de abstracción y se
evitan errores a la hora de ingresar los datos.
Usuario:
• La clase usuario cuenta con un atributo estado de tipo Estado. La relación entre Estado y Usuario es de tipo
dependencia, donde Usuario usa a Estado para obtener los posibles valores de su atributo.
• Se da una relación de agregación donde el Usuario el TODO y el Rol una PARTE. Un rol puede pertenecer a
varios usuarios, puede existir aunque no haya usuarios que lo contengan, puede cambiar de usuario y por
último, al eliminar un usuario no se elimina el rol al que pertenece. Debe tener al menos un rol el usuario.
Rol:
• Un Rol está compuesto por una serie de permisos. La serie particular de permisos asignados a un rol solo
pertenecen a dicho rol y al eliminar el rol sus permisos también son eliminados. Puede tener de 0 a n
permisos.
• Implementa la interfaz ITransaccional para que realice las funciones CRUD de permisos con el formato
estándar.
Bitácora:
• La Bitácora recibe mensajes de un Usuario, donde el Usuario cumple el rol de autor del registro que se guarda
en la Bitácora. La relación es un tipo de asociación unidireccional.

Página 12 de 16
IC6821 Diseño de Software
Apunte 02: Repaso Conceptos Básicos OO

FIGURA 15. M ODEL


View
Se tiene un framework de usuarios que se dedica a mostrar datos al usuario y recolectar información de este. La capa de la
vista interactúa solamente con el modelo y se muestra como una sola clase pensando en la escalabilidad del sistema.
Esta clase se relaciona con el controlador por medio de una asociación, ya que se da un simple paso de mensajes.

Página 13 de 16
IC6821 Diseño de Software
Apunte 02: Repaso Conceptos Básicos OO

FIGURA 16. VIEW

Controller
En el controlador se diseñó siguiendo el principio de descomposición al crear subsistemas para la administración de roles,
bitácoras y usuarios. En total está compuesto por 5 clases y dos interfaces.
ITransaccional
• La interfaz ITransaccional se creó para ser un molde de las funciones CRUD, de esta manera el diseño cumple
con el principio de separación de interfaz e implementación. Por medio de esta interfaz se aíslan los detalles
de como cada componente realiza las funciones y se estandariza lo que conocen otros componentes acerca de
esta.
IValidable
• En el caso de la interfaz IValidable sirve como plantilla para la validación de objetos. Aunque en el sistema
actual solo la utiliza una vez se justifica su creación en por la escalabilidad. Al igual que en el caso anterior,
esta interfaz ayuda a separar la interfaz de las implementaciones.
GestorGeneral:
• Es una clase abstracta en la que se ignoran las posibles diferencias entre los gestores para enfocarse en su
similitud.
• Implementa la interfaz ITransaccional para que realice las funciones CRUD con el formato estándar.
GestorBitácora:
• Se da una relación de herencia con el GestorGeneral.
• Depende de la clase Bitácora, que se encuentra en el modelo, para realizar sus funciones.
GestorRoles:
• Se da una relación de herencia con el GestorGeneral.

Página 14 de 16
IC6821 Diseño de Software
Apunte 02: Repaso Conceptos Básicos OO

• Depende de la clase Rol, que se encuentra en el modelo, para realizar sus funciones.
GestorUsuarios:
• Se da una relación de herencia con el GestorGeneral.
• Depende de la clase Usuario, que se encuentra en el modelo, para realizar sus funciones.
• Implementa la interfaz IValuable para validar el acceso de los usuarios.
Controlador:
• Recibe mensajes del GestorBitácora, GestorRoles, GestorUsuarios para sus funciones.
• Realiza todas las funciones de los gestores uniéndolas con la información que devuelve la vista.

FIGURA 17. C ONTROLLER

Código
Tanto el diagrama como el código del sistema se encuentran disponibles en el TecDigital en Semana 3.

Página 15 de 16
IC6821 Diseño de Software
Apunte 02: Repaso Conceptos Básicos OO

Referencias
Universidad de Alicante. (s.f.). Modelo vista controlador (MVC). Servicio de Informática ASP.NET MVC 3 Framework.
Obtenido de Universidad de Alicante: https://si.ua.es/es/documentacion/asp-net-mvc-3/1-dia/modelo-vista-
controlador-mvc.html

Página 16 de 16
Instituto Tecnológico de Costa Rica.

Ingeniería en Computación.

Diseño de Software IC6821

Grupo 40

Apuntes #3 – SOLID

Profesora: Ericka Solano Fernández.

Estudiantes:

Jorge Gamboa Ching (2015061325)

Steven Paniagua Aguilar (2015061322)

Erick Francisco Quesada Fonseca. (2015125198)

Agosto.

II Semestre 2018.

pág. 1
Índice

Principios SOLID………………………………………………………………………3

SRP: Single Resposibility Principle………………………………………………….4


OCP: Open/Closed Principle…………………………………………………………6
LSP: Liskov Substitution Principle…………………………………………………..10
ISP: Interface Segregation Principle………………………………………………..14
DIP: Dependency Inversion Principle……………………………………………….17
Bibliografía……………………………………………………………………………..23

pág. 2
Principios SOLID
Los principios SOLID son cinco principios enunciados por Robert C. Martin
alrededor del año 2000 enfocados a la elaboración de software de calidad. Cuando
estos principios se aplican en conjunto es más probable que un desarrollador cree
un sistema que sea fácil de mantener y ampliar en el tiempo. Los principios SOLID
son guías que pueden ser aplicadas en el desarrollo de software para eliminar
código sucio provocando que el programador tenga que refactorizar el código fuente
hasta que sea legible y extensible. Forma parte de la estrategia global del desarrollo
ágil de software y programación adaptativa.

Esta definición es perfectamente válida y se podría resumir diciendo que son cinco
principios que hay que tener siempre presentes si queremos desarrollar un software
de calidad legible, entendible y fácilmente testeable.

Estos principios fueron creados para dar una mejor solución a problemas como los
siguientes:

• Rigidez
• Fragilidad
• Inmovilidad
• Viscosidad
• Complejidad innecesaria
• Repetición innecesaria
• Opacidad

Las iniciales de SOLID significan los siguientes principios:

• SRP: Single Resposibility Principle


• OCP: Open/Closed Principle
• LSP: Liskov Substitution Principle
• ISP: Interface Segregation Principle
• DIP: Dependency Inversion Principle

A continuación, se abordarán todos y cada uno de los principios mencionados


anteriormente:

pág. 3
SRP: Single Resposibility Principle
Una clase debería concentrarse sólo en hacer una cosa de tal forma que cuando
cambie algún requisito en mayor o menor medida dicho cambio sólo afecte a dicha
clase por una razón.

Divide las clases que sean muy grandes y que tengan muchas funcionalidades en
otras más pequeñas, pero que de igual manera cumplan con las funciones.

Ejemplo: Supongamos que tenemos un paquete de software para la gestión de


correos electrónicos. En algún lugar de dicho paquete, antes del envío de los
correos en sí, queremos establecer los distintos parámetros que, por ahora, son:
Emisor, Receptor y Contenido.

UML

La implementación sería la siguiente:

pág. 4
¿Porqué incumple el principio de única responsabilidad?

SRP dice que sólo debería haber un motivo para cambiar una clase o una interfaz
y, por ende, las clases que la implementan. En éste caso la traducción literal es
clara, si cambiara el contenido habría que modificar el código cada vez que
añadamos algún tipo de contenido o si, cambiara el protocolo y quisiéramos añadir
más campos también habría que modificar la clase email. Por tanto hay más de un
motivo por el que tendríamos que modificar ésta clase.

UML

La implementación sería la siguiente:

pág. 5
Si en vez de un contenido String utilizamos una interfaz IContenido que suponga un
contrato para hacer viable cualquier tipo de contenido cada vez que nos pidan que
añadamos un tipo de contenido sólo tendremos que modificar dicha interfaz y/o las
clases que lo implementen, a lo que afecte el cambio, y no a la clase
CorreoElectronico y a su interfaz.

OCP: Open/Closed Principle


ENTIDADES DE SOFTWARE (CLASES, MÓDULOS, FUNCIONES, ETC.) DEBE ESTAR
ABIERTO PARA LA EXTENSIÓN, PERO CERRADO PARA MODIFICACIÓN.

En este caso un único cambio presente en algún programa puede llegar a dar como
resultado una cascada de cambios en aquellos módulos que sean dependientes,
por otro lado, dicho programa puede llegar a exhibir los atributos indeseables, los
cuales se pueden asociar con un mal diseño. Por otro lado, este programa se puede
volver frágil, rígido, impredecible y no pueda ser reutilizado, por eso este abierto
principio ataca esto de una manera muy directa. En este se establece que se deben
diseñar módulos que nunca deben cambiar cuando los requisitos lo hacen,
extendiendo el comportamiento de dichos módulos con la agregación de nuevo
código, entonces no se debería cambiar el código anterior que ya funciona.

Descripción

● Está abierto para la extensión: Esto significa que el comportamiento del


módulo se puede llegar a extender en determinado momento, por ende se
puede hacer que el módulo se comporte de forma nueva y a su vez diferente,
dependiendo de los requisitos de la aplicación a cambiar, o bien para
satisfacer las necesidades de nuevas aplicaciones.
● Está cerrado por modificación: En este caso el código fuente de dicho
módulo es inviolable, por lo que nadie está autorizado a realizar algún tipo de
cambio a dicho código fuente.

Pareciera que estos dos atributos se encuentran en desacuerdo uno del otro, la
forma natural en la que se puede extender el comportamiento de un módulo en
específico es realizando algún tipo de cambio en el. Por esta situación, un módulo
que no puede cambiarse normalmente se piensa que tiene un comportamiento fijo.

pág. 6
El uso más común de extensión es por medio de la herencia y también al volver a
implementar métodos, existe otra alternativa, la cual consiste en usar métodos que
puedan aceptar una interfaz de manera que podemos ejecutar cualquier clase que
sea usada por dicha interfaz. Se puede decir que prácticamente en todos los casos,
el comportamiento de la clase cambia sin que se haya hecho una sola modificación
en el código interno. Puede llegar un momento en el cual las necesidades sean tan
imprevisibles que los métodos definidos en la interfaz o en los métodos que sean
extensibles, no sean suficientes para cubrir las necesidades, por lo que se deberá
romper este principio y refactorizar.

La abstracción es la clave

Las abstracciones son clases base abstractas, a su vez en estas se puede llegar a
representar un grupo ilimitado de comportamientos posibles, esto en todas las
clases derivadas de ser posible. Un módulo en específico podría llegar a manipular
una abstracción, en donde este módulo puede llegar a cerrarse para algún tipo de
modificación ya que puede estar dependiendo de una abstracción fija, sin embargo,
el comportamiento de dicho módulo puede llegar a extenderse mediante la creación
de nuevos derivados de la abstracción.

Ejemplo

Tenemos la clase vehicle, podríamos tener la necesidad de dibujarlos en pantalla.


Imaginemos que tenemos una clase con un método que se encarga de dibujar un
vehículo por pantalla. Por supuesto, cada vehículo tiene su propia forma de ser
pintado. Nuestro vehículo tiene la siguiente forma:

pág. 7
Básicamente es una clase que especifica su tipo mediante un enumerado, podemos
tener por ejemplo un enum con un par de tipos:

Mientras no necesitemos dibujar más tipos de vehículos ni veamos que este switch
se repite en varias partes de nuestro código, en mi opinión no debes sentir la
necesidad de modificarlo. Incluso el hecho de que cambie la forma de dibujar un
coche o una moto estaría encapsulado en sus propios métodos y no afectaría al
resto del código.

Pero puede llegar un punto en el que necesitemos dibujar un nuevo tipo de vehículo,
y luego otro. Esto implica crear un nuevo enumerado, un nuevo case y un nuevo
método para implementar el dibujado. En este caso sería buena idea aplicar el
principio Open/Closed.

Si lo solucionamos mediante herencia y polimorfismo, el paso evidente es sustituir


ese enumerado por clases reales, y que cada clase sepa cómo pintarse:

Ahora nuestro método anterior se reduce a:

pág. 8
Añadir nuevos vehículos ahora es tan sencillo como crear la clase correspondiente
que extienda de Vehicle:

pág. 9
LSP: Liskov Substitution Principle
El Principio de Sustitución de Liskov fue acuñado por Barbara Liskov en el año 1987
durante una conferencia sobre Jerarquía y Abstracción de datos. Su principal
cometido es la de asegurar en la herencia entre clases de la Programación
Orientada a Objetos que una clase derivada no únicamente es sino que debe
comportarse como la clase base.

FUNCIONES QUE UTILIZAN INDICADORES O REFERENCIAS PARA BASE


LAS CLASES DEBEN PODER USAR OBJETOS DE CLASES DERIVADAS SIN
SABERLO.

Este principio se puede definir también de la siguiente manera:

Si por cada objeto o1 del tipo S existe un objeto o2 del tipo T tal que para todos los
programas P definidos en términos de T y el comportamiento de P permanece
invariable cuando o1 es sustituido por o2, entonces S es un subtipo de T.

La importancia de este principio se vuelve obvia cuando se consideran las


consecuencias de violarlo, por ejemplo, si hubiera una función que no se ajusta al
LSP, entonces esa función utiliza un puntero o referencia a una clase base, pero
debe conocer todas las derivadas de dicha clase. Tal función viola el principio de
Open-Closed porque debe ser

modificado cada vez que se crea una nueva derivada de la clase base.

Diseño por Contratos

Por otro lado, Bertrand Meyer quien es un investigador, escritor y consultor en el


campo de los lenguajes de ordenador, añadió el Diseño por Contratos (DbC) en el
que se especifica un contrato a nivel de métodos para establecer los valores/tipos
aceptables de entrada y de salida, las condiciones de valor o de tipo para los errores
y excepciones que puedan ocurrir, la precondición y postcondición y la invariantes.

Para cumplir con el Principio de Substitución de Liskov, la implementación de la


clase derivada debe:

● Ser menos restrictivas en la precondición.


● Ser más restrictivas en la postcondición.
● Preservar la invariancia.

pág. 10
Por otro lado, este principio no dicta tanto el “qué” sino el “cómo" debe heredarse
tipos. El uso de DbC está estrechamente ligado y puede ser de utilidad en herencias
de tipos más complejos para preservar el comportamiento en el tipo derivado. El
impacto de este principio en los nuevos lenguajes de desarrollo los podemos ver
por ejemplo los argumentos de métodos para los subtipos o la covariancia para el
retorno, tanto en delegados como en interfaces.

Ejemplo
En la vida real tenemos claro que un cuadrado es un rectángulo con los dos lados
iguales. Si intentamos modelar un cuadrado como una concreción de un rectángulo,
vamos a tener problemas con este principio:

Se crea un test que comprueba el área:

pág. 11
La definición del cuadrado sería la siguiente:

Probando el test a cambiar el rectángulo por un cuadrado, este test no se cumple,


el resultado sería 16 en lugar de 20. Estamos por tanto violando el principio de
sustitución de Liskov.

¿Cómo lo solucionamos?
Hay varias posibilidades en función del caso en el que nos encontremos. Lo más
habitual será ampliar esa jerarquía de clases. Podemos extraer a otra clase padre
las características comunes y hacer que la antigua clase padre y su hija hereden de
ella. Al final lo más probable es que la clase tenga tan poco código que acabes
teniendo un simple interfaz, esto no supone ningún problema en absoluto:

pág. 12
Pero para este caso en particular, nos encontramos con una solución mucho más
sencilla, la razón por la que no se cumple que un cuadrado sea un rectángulo, es
porque estamos dando la opción de modificar el ancho y alto después de la creación
del objeto. Podemos solventar esta situación simplemente usando inmutabilidad. La
inmutabilidad es un tema muy interesante, consiste en que una vez que se ha
creado un objeto, el estado del mismo no puede volver a modificarse. La
inmutabilidad tiene múltiples ventajas, entre ellas un mejor uso de memoria (todo su
estado es final) o seguridad en múltiples hilos de ejecución. Pero ciñéndonos al
ejemplo, ¿cómo nos ayuda aquí la inmutabilidad? De esta forma:

Desde el momento de la instanciación del objeto, todo lo que hagamos con él será
válido, ya usemos un rectángulo o un cuadrado. El problema que había detrás de
este ejemplo es que la asignación de una parte del estado modifica mágicamente
otro campo. Sin embargo, con este nuevo enfoque, al no permitir las modificaciones,
el funcionamiento de ambas clases es totalmente predecible.

pág. 13
ISP: Interface Segregation Principle
Este principio se basa en evitar el uso de las interfaces "gordas". Las clases que
tienen interfaces "gordas" son clases cuyas interfaces no son cohesivas. En otras
palabras, las interfaces de la clase se pueden dividir en grupos de funciones
miembro. Cada grupo sirve a un conjunto diferente de clientes. Por lo tanto, algunos
clientes usan un grupo de funciones miembro, y otros clientes usan los otros grupos.
El ISP reconoce que hay objetos que requieren interfaces no cohesivas; sin
embargo, sugiere que los clientes no deberían saber sobre ellos como una sola
clase. En cambio, los clientes deben saber acerca de las clases base abstractas
que tienen interfaces cohesivas.

Interfaz gorda o contaminada

Para que una clase pueda implementar una interface debe programar todos los
métodos que esta contiene, sin embargo, si existen métodos que una clase no sabe
de qué manera implementarlos y, por lo tanto, se tienen que dejar vacíos o que
tengan que lanzar una excepción, hacen que dicha interface se convierta en una
interface gorda o contaminada, un ejemplo es el siguiente:

Primero tenemos una interface llamada Product, que tiene métodos para
obtener el nombre, cantidad en el stock, números de discos y la fecha de
lanzamiento. Esta interface es implementada por la clase CD, de momento, no se
está incumpliendo con el principio, pero resulta que también se quieren vender
películas DVD y estos necesitan tener la clasificación por edad, lo que normalmente
hacen es agregar el método que necesita la nueva clase y pueda seguir usando
esta interface sin problemas.

pág. 14
Sin embargo, que pasaría con la clase CD, le tocaría implementar el método
edad de recomendación (getRecommendedAge()), pero resulta que normalmente
los CD no tienen una edad de recomendación, por lo tanto, se debería de dejar que
no haga nada o lanzar una excepción de la siguiente manera:

Aquí es donde Product comienza a convertirse en una interface gorda, ya que


empiezan a aparecer clases que no saben cómo implementar ciertos métodos y
poniendo excepciones hará que estas tengan que ser manipuladas o simplemente
se detenga el programa y muestre errores, lo cual no es agradable para el usuario.
Lo correcto en estos casos es crear una nueva interface con el método que se
necesita.

Y que la clase CD siga implementando la interface Product y la clase DVD


implemente 2 interface, Product y AgeAware.

Clases separados significan interfaces separadas

Si la interface es implementada por clases que son muy diferentes entre ellos,
clases que son totalmente aparte, las interfaces también deberían estar aparte, es
decir, que cada uno tenga su propia interface, ya que pueden surgir algún cambio y
diferentes partes pueden verse afectadas.

pág. 15
Los cambios pueden aparecer porque el sistema necesita un parámetro más en
uno de los métodos de la interface, entonces, algunas clases que usan estas
interfaces pueden tener problemas, pueden ser que no todas necesitan dicho
parámetro y por consiguiente se tendrán errores. Otra forma de que aparezca un
cambio es que más bien sea a solicitud de uno de los clases, es decir, una clase
determinada necesita que la interface realice ciertas modificaciones en su
definición, de manera que las demás clases también se podrían ver afectados, por
ello, es importante que si se trata de clases distintas sus interfaces estén separadas
y no tener esa dependencia.

Depender de interfaces que no usan

Si una clase depende de otra clase que contiene una interface y la primera clase
no la usa, aun así, se tiene una dependencia de esa interface y por lo tanto,
cualquier cambio que pueda ocurrir en la interface también puede afectar a esta
primera clase, ya que si la interface cambia afecta a la clase que la implementa y
por consiguiente la primer clase puede verse afectada.

Formas de separar interface

En algunos casos las interfaces no pueden separarse porque necesitan


manipular los mismos datos, pero son clases diferentes, entonces ¿cómo cumplir
con el principio de segregación?, para ellos se puede hacer de dos maneras:

1. Separación por medio de la delegación: Esta solución utiliza el patrón


Adapter, el cual consiste en crear un objeto que permita dar la misma
funcionalidad de la interface, por lo tanto, la primera clase se puede conectar
a la interface, mientras que la segunda clase se conecta al objeto adaptador.

pág. 16
Door y TimerCliente son interfaces y la clase TimedDoor necesita de ambas
interfaces ya está heredando Door, y una solución para acceder a la otra
interface es definir el objeto DoorTimerAdapter que básicamente lo contendrá
TimedDoor en su definición, de manera que con esa instancia puede invocar
los métodos que tienen el adapter, los cuales son los mismos que TimerClient
ya que hereda de este. Sin embargo, se dice que esta no es la solución más
adecuada.

2. Separación por medio de herencia múltiple: La mejor solución es usar


herencia múltiple, es decir, que la clase TimedDoor herede de Door y
TimerClient, esto produce modelo más claro y fácil de programar, la solución
anterior involucraba crear un objeto más para delegar los métodos. El
ejemplo anterior con herencia múltiple queda de la siguiente forma:

DIP: Dependency Inversion Principle


Es muy común encontrar software con malos diseños, a pesar de que cumplas
con los requerimientos solicitados, suelen ser sistemas muy rígidos, frágiles e
inmóviles, de manera que no permite la reutilización y darle mantenimiento resulta
ser muy complicado.

¿Qué es lo que hace que un diseño sea rígido, frágil e inmóvil? Es la


interdependencia de los módulos dentro de ese diseño. Un diseño es rígido si no se
puede cambiar fácilmente. Tal rigidez se debe al hecho de que un solo cambio a un
software altamente interdependiente comienza una cascada de cambios en los
módulos dependientes. Cuando el alcance de esa cascada de cambio no puede ser
predicho por los diseñadores o mantenedores, el impacto del cambio no puede ser
estimado. Esto hace que el costo del cambio sea imposible de predecir.

pág. 17
La fragilidad es la tendencia de un programa a romperse en muchos lugares
cuando se produce un solo cambio hecho. A menudo, los nuevos problemas se
encuentran en áreas que no tienen una relación conceptual con el área que fue
cambiada. Un diseño es inmóvil cuando las partes deseables del diseño son
altamente dependientes sobre otros detalles que no son deseados, si el diseño es
altamente interdependiente, entonces esos diseñadores también se sentirán
intimidados por la cantidad de trabajo necesario para separar la parte deseable del
diseño, en estos casos a veces resulta mejor diseñarlo de nuevo. Lo veremos con
un ejemplo:

Se tiene un módulo Copy el cual hace una copia de caracteres y los escribe en
una impresora, Copy funciona por medio ReadKeyboard y WritePrinter, el primero
permite leer caracteres de teclado, el segundo escribe los caracteres con una
impresora, nótese que estos últimos módulos puede ser reutilizables para accesos
al teclado o impresora, pero el módulo Copy como tal no puede ser reutilizables ya
que funciona únicamente con un teclado y una impresora, es decir, hay una
dependencia de esos dispositivos.

Algunas de las soluciones usadas es que agregue un if y controle según el


dispositivo que llega, pero esto agrega más interdependencias y con el paso del
tiempo llegan más dispositivos, entonces el módulo Copy dependerá de muchos
módulos de nivel inferior, esto hará que se vuelva cada vez más frágil y rígido.
Agregando el if quedaría de la forma:

pág. 18
El principio de inversión de dependencias, busca que las clases que están en
un alto nivel no tengan dependencia de las clases de niveles inferiores, ambas
clases deberían de depender de abstracciones, entonces si regresamos al ejemplo,
vemos que la clase Copy depende de sus clases de nivel inferior lo cual está
incumpliendo el principio, por lo tanto, se tiene invertir esa dependencia, para ello,
se debe encontrar alguna forma de que el módulo Copy funcione independiente de
los detalles que controla. Una posible solución es la siguiente:

Se define la clase Copy y esta va a tener una agregación de Reader y Writer la


cual es una clase abstracta, de manera que Copy funciona sin importar los detalles,
esto es invertir una dependencia, así los diferentes dispositivos de lectura pueden
heredar de la clase Reader, como en este caso KeyboardReader, la clase Copy solo
sabe que tiene una instancia de una clase que es de tipo Reader, pero no importa
exactamente el tipo original, es decir, solo se asocia con el tipo de la clase Padre.
De igual manera el caso de Writer con cualquier dispositivo, note que estas clases
que heredan de las clases abstractas ya son más detalladas y, aun así, la
dependencia es solo de la abstracción, por lo tanto, se cumple con el principio de
inversión de dependencias.

pág. 19
La programación del diseño queda de la siguiente manera:

Se declara las clases Reader y Writer que tendrán un método virtual, para que
las clases que hereden puedan programar dicho método de la forma que
corresponda.

En este caso, se declara Copy como un método, por lo tanto, tiene que recibir
las dos agregaciones y lo que hace este método es invocar el método Read y Write
de los objetos recibidos sin importar de los detalles, Copy también se pudo haber
hecho como una clase y en su constructor recibir los dos objetos.

Consideraciones importantes y ejemplo

Los módulos de alto nivel no tienen que depender de sus niveles inferiores, los
de alto nivel contienen las decisiones políticas importantes, modelos comerciales y
son la identidad de una aplicación. Si lo módulos inferiores cambian, pueden verse
afectados los superiores y esos cambios obligan a que estos también tengan que
cambiar para que se adapten al funcionamiento del sistema, lo cual es absurdo. Son
los módulos de alto nivel los que deberían estar forzando a los módulos de bajo
nivel para cambiar. Son los módulos de alto nivel los que deberían tener prioridad
sobre los módulos de nivel inferior, además, siempre son los módulos de alto nivel
los que se desean reutilizar, por lo tanto, no debe de existir esa dependencia.

Ejemplo

Considere el caso de un objeto Button y un objeto Lamp. El objeto Button


detecta el entorno externo. Puede determinar si un el usuario lo "presionó". No
importa lo que sea el mecanismo de detección. Podría ser un botón, icono en una
GUI, un botón físico presionado por un dedo humano, o incluso un detector de
movimiento.

pág. 20
Supongamos que se trata de un sistema de seguridad para el hogar. El objeto
Button detecta que un usuario ha activado o lo desactivó. El objeto de la lámpara
afecta el entorno externo. Al recibir un TurnOn mensaje, ilumina una luz de algún
tipo. Al recibir un mensaje TurnOff, se extingue esa luz. El mecanismo físico no es
importante. Podría ser un LED en una computadora consola, un vapor de mercurio
lámpara en un estacionamiento, o incluso el láser en una impresora láser.

Un diseño simple es el siguiente.

El código de la derecha
representa la programación del
diseño, donde se tiene la clase
Lamp con sus métodos y luego
la clase Button como vemos es
una agregación por lo tanto, se
recibe por parámetro una
instancia de Lamp, y al inicio se
encuentra el “include lamp.h”
esto es lo que representa un
dependencia de la case Lamp,
es decir, Button es una clase de
alto nivel y necesita de una
clase inferior, por lo que se está
violentando con el principio de
inversión de dependencias.

pág. 21
La abstracción subyacente de este caso es detectar un gestor de encendido /
apagado de un usuario y transmitir ese gesto a un objeto objetivo ¿Qué mecanismo
se usa para detectar el gesto del usuario? ¡Irrelevante! ¿Cuál es el objeto objetivo?
¡Irrelevante! Estos son detalles que no afectan la abstracción. Por lo tanto, debemos
aislar esta abstracción de los detalles del problema, entonces nosotros debemos
dirigir las dependencias del diseño tal que los detalles dependan de las
abstracciones.

La solución es de la siguiente manera:

Primeramente, la clase Button es una


clase abstracta y de ella hereda una clase
que hará la implementación del botón,
también tiene una agregación de
ButtonClient de manera que la clase
Button no depende de ningún detalle, sino
solo sabe que tiene una implementación y
un ButtonClient que podrá ser cualquier
especialización como, en este caso:
Lamp.

El código que representa el diagrama es el siguiente:

pág. 22
Este modelo es reutilizable con cualquier tipo de botón, y con cualquier tipo de
dispositivo que necesita ser controlado. Además, no se ve afectado por cambios en
los mecanismos de bajo nivel. Por lo tanto, es robusto en presencia de cambios,
flexible y reutilizable.

Bibliografía

Leiva, A. (s.f.). Principio de segregación de interfaces. Obtenido de devexperto.com:


https://devexperto.com/principio-de-segregacion-de-interfaces/

UncleBob. (1996). The Dependency Inversion. Obtenido de http://butunclebob.com:


https://drive.google.com/file/d/0BwhCYaYDn8EgMjdlMWIzNGUtZTQ0NC00ZjQ5LTkwYzQt
ZjRhMDRlNTQ3ZGMz/view

UncleBoB. (1996). The Interface Segregation Principle. Obtenido de http://butunclebob.com:


https://drive.google.com/file/d/0BwhCYaYDn8EgOTViYjJhYzMtMzYxMC00MzFjLWJjMzYtO
GJiMDc5N2JkYmJi/view

Martin, R. (2005). PrinciplesOfOod. agosto 21, 2018, de butunclebob Sitio web:


http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod

UncleBob. (1996). The Single Responsibility Principle. Obtenido de http://butunclebob.com:


https://drive.google.com/file/d/0ByOwmqah_nuGNHEtcU5OekdDMkk/view

UncleBob. (1996). The Open Closed Principle. Obtenido de http://butunclebob.com:


https://drive.google.com/file/d/0BwhCYaYDn8EgN2M5MTkwM2EtNWFkZC00ZTI3LWFjZT
UtNTFhZGZiYmUzODc1/view

UncleBob. (1996). The Liskov Substitution Principle. Obtenido de http://butunclebob.com:


https://drive.google.com/file/d/0BwhCYaYDn8EgNzAzZjA5ZmItNjU3NS00MzQ5LTkwYjMt
MDJhNDU5ZTM0MTlh/view

pág. 23

Você também pode gostar