You are on page 1of 80

Ingeniería del Software 1

Curso 2010-2011

Diseño de responsabilidades
con patrones (GRASP)
ATENCIÓN
ESTE DOCUMENTO ES UN MATERIAL DE APOYO PARA LAS
CLASES DE TEORÍA, NO ESTÁ DISEÑADO COMO MATERIAL DE
ESTUDIO. SI QUIERES USARLO PARA ESTUDIAR DEBES
COMPLEMENTARLO CON LAS EXPLICACIONES DEL PROFESOR
Y/O LA BIBLIOGRAFÍA RECOMENDADA
Índice

 Introducción

 Patrones GRASP Básicos


 Creador

 Experto (en Información)

 Bajo Acoplamiento

 Controlador

 Alta Cohesión

 Patrones GRASP Avanzados


 Polimorfismo

 Fabricación Pura

 Indirección

 Protección de Variaciones
Objetivos del tema

 Comprender el uso del diagrama de clases para reflejar

conceptos a distintos niveles de abstracción

 Ser capaz de realizar un diseño software que sea

respetuoso con los principios de asignación de


responsabilidades de GRASP
Introducción
Perspectiva de análisis vs. perspectiva de diseño en
diagramas de clase UML
Introducción: Usos del diagrama de clases

 UML incluye los diagramas de clases para ilustrar clases, interfaces

y sus asociaciones.

 Éstos se utilizan para el modelado estático de objetos.

 Los diagramas de clases se pueden usar tanto desde una

perspectiva conceptual (para el modelo del dominio) como desde una


perspectiva software (para el modelado de la CAPA DE DOMINIO).
 Es común hablar de “Diagrama de clases de Análisis” para referirse al diagrama

de clases que representa el modelo de dominio, y a “Diagrama de Clases de


Diseño” para hablar del diagrama de clases que representa a las clases software,
es decir, a las clases que finalmente van a ser implementadas.

 EN LA ASIGNATURA NOS VAMOS A CENTRAR EN EL MODO DE DEFINIR LOS

MODELOS DE DISEÑO Y, DENTRO DE ELLOS, EL DIAGRAMA DE CLASES DE


DISEÑO
Introducción: Diseño de objetos

 El diseño de objetos se describe como:


Tras haber identificado tus requisitos, y haber creado un modelo de dominio,
añade operaciones a las clases y define las secuencias de mensajes entre
objetos para cubrir los requisitos

 Estas recomendaciones no son especialmente útiles, ya que hay


principios fundamentales de diseño, nada triviales, que deben ser
tenidos en cuenta a la hora de:
 Decidir qué operaciones hay que asignar a qué clases

 Cómo los objetos deberían interactuar para dar respuesta a los casos de uso

 De hecho, el artefacto más importante del flujo de trabajo de diseño


es el Modelo de Diseño, que incluye el diagrama de clases
software (no conceptuales) y diagramas de interacción.
Introducción: Diseño de objetos

Domain Model Sale


Register Captures 1
1
time
conceptual
... isComplete : Boolean
perspective
/total

Register Sale
Design Model ... time
1
isComplete : Boolean
DCD; software endSale() currentSale /total
perspective enterItem(...)
makePayment(...) makeLineItem(...)

Meta: Low Representational Gap


Introducción: Diseño de objetos

the association name, common when drawing a


domain model, is often excluded (though still legal)
when using class diagrams for a software
perspective in a DCD

Register Sale
UP Domain Model 1 Captures-current-sale 1
conceptual perspective id : Int time : DateTime

UP Design Model Register Sale


DCD 1
id: Int time: DateTime
software perspective currentSale
... ...
Introducción: Del DC de Análisis al DC de Diseño

 GENERALIZACIÓN VS HERENCIA

 En el modelo de dominio implica que la superclase es un

superconjunto y la subclase un subconjunto.

 En un DCD implica la relación de herencia de los lenguajes de

programación OO de una superclase a una subclase.

 ¡ATENCIÓN! Las relaciones de generalización no tienen

por qué traducirse en relaciones de herencia en el DCD.


 P.ej. en C++ el uso de templates puede a veces reducir el número

de clases (alternativa a la herencia).


Introducción: Del DC de Análisis al DC de Diseño

 DCD: APARICIÓN DE NUEVAS CLASES (1/2)

 Clases de Utilidad: clases que encapsulan algoritmos genéricos

que pueden ser accedidos por más de una clase


 Como veremos más adelante, añadir esos algoritmos a clases

existentes disminuiría la cohesión de esas clases

 Las clases de utilidad también se pueden utilizar para encapsular

clases de librería o aplicaciones/funciones que no son OO


(proporcionan una interfaz OO a dichos módulos)

 Librerías: referencias a librerías proporcionadas por el entorno

(p.ej. STL)
Introducción: Del DC de Análisis al DC de Diseño

 DCD: APARICIÓN DE NUEVAS CLASES (2/2)

 Interfaces: abstracción de comportamiento mediante interfaces

que realizan las clases

 Clases de ayuda: clases que asisten a una clase existente para la

realización de una tarea, al mismo tiempo que permiten aumentar


la cohesión de la clase origen.
 Ejemplo: ManageEmployeeForm: clase que gestiona cosas como

formateo de números de identidad. ¿Pero quién gestiona el control de


que el número de la Seguridad Social es un número válido de la
empresa?
Introducción: Responsabilidades

 UML define una responsabilidad como “un contrato u

obligación de un clasificador”.
 Obligaciones de un objeto en términos de su comportamiento.

 Las responsabilidades se asignan a clases software durante el

diseño de objetos
Introducción: Tipos de responsabilidades

 Hacer:
 Hacer algo él mismo (e.g. crear un objeto, realizar un cálculo)

 Iniciar la acción en otros objetos.

 Controlar y coordinar actividades en otros objetos.


 Ejemplo: Un tablero es responsable de crear sus casillas”

 Saber o Conocer:
 Conocer sus datos privados (encapsulados).

 Conocer los objetos con los que se relaciona.

 Conocer las cosas que puede derivar o calcular.


 Ejemplo: “Un barco es responsable de conocer cuándo está hundido”

 Las responsabilidades relacionadas con „conocer‟ son normalmente


inferibles del Modelo de Dominio (debido a los atributos y
asociaciones que éste ilustra)
Introducción: Responsabilidades vs. métodos

 La complejidad del proceso de traducción de


responsabilidades a clases y operaciones (implementadas
mediante métodos) está influenciada por la granularidad de la
responsabilidad.
 Ejemplo: la responsabilidad “proporciona acceso a BD relacional”
puede involucrar docenas de clases y cientos de métodos, mientras
que “crear Venta” podría involucrar sólo uno o un pequeño número de
métodos.

 Una responsabilidad no es un método, pero los métodos se


implementan para cubrir responsabilidades.
 Los métodos pueden colaborar con otros métodos y objetos
para cubrir una determinada responsabilidad.
Introducción: Responsabilidades y D. Interacción

 Podemos pensar sobre cómo :Pieza


asignar responsabilidades
cuando modelamos o cuando getCasilla(…)
codificamos
get(…)
:Casilla

 Dentro de los artefactos UML,


un contexto común donde estas  Las piezas tienen la
responsabilidades responsabilidad de saber en
qué casilla están colocadas, y
(implementadas como
la manejan con el método
métodos) se consideran es getCasilla().
durante la creación de  El cumplimiento de esta
responsabilidad requiere llamar
diagramas de interacción.
al método get del atributo
Casilla
Patrones GRASP
GRASP: Introducción

 GRASP es un acrónimo para General Responsibility Assignment

Software Patterns.

 Describen 9 principios fundamentales del diseño de objetos y de

la asignación de responsabilidades, expresado en términos de


patrones.

 Se dividen en dos grupos:

BÁSICOS AVANZADOS
 Creador  Polimorfismo
 Experto (en Información)  Fabricación Pura
 Bajo Acoplamiento  Indirección
 Controlador  Protección de Variaciones
 Alta Cohesión
GRASP: Introducción
 Para ilustrar los patrones vamos a suponer que queremos modelar un
monopoly
 Dibujad su modelo de dominio (perspectiva conceptual)
 Nota: asume dos dados
 Nota: comienza modelando el tablero, las casillas, y el acto de hacer una
tirada y mover las piezas por parte de un jugador
Patrones GRASP básicos
Creador
Experto (en información)
Bajo acoplamiento
Controlador
Alta cohesión
GRASP: Creador

 En el Monopoly, ¿quién debería ser el responsable de

crear Casillas?
GRASP: Creador

 Problema: ¿Quién debería ser el responsable de crear


una nueva instancia de alguna clase?
 Solución: Asigna a la clase B la responsabilidad de crear
una instancia de la clase A si una o más de las siguientes
afirmaciones es cierta:
1) B agrega objetos de tipo A.
2) B contiene objetos de tipo A.
3) B graba objetos de tipo A.
4) B tiene datos inicializadores que serán pasados a A cuando sea
necesario crear un objeto de tipo A (por tanto B es un Experto
con respecto a la creación de A).
GRASP: Creador

 “Creator” busca el objeto creador que requiere estar conectado


al objeto creado en cualquier evento.
 Esto soporta “bajo acoplamiento”.
Discusión  Es común que el “Creator” se encuentre buscando la clase que
tiene los datos de inicialización que serán pasados como
parámetro al objeto creado. Tablero tiene los datos de posición
para el objeto Casilla.
 La creación podría ser compleja (por ejemplo “creación
condicional”, “reciclamiento de instancias”, o que la creación de
una instancia sea a partir de una familia de clases similares
Contraindicaciones basadas en una propiedad externa). En este caso use el patrón
“Factory” (GoF).

 Favorece el “bajo acoplamiento”, implicando con ello un nivel


bajo de dependencias de mantenimiento y alta oportunidad de
reuso.
Beneficios
GRASP: Experto (en información)

 En el Monopoly, imaginad que necesitamos ser capaces de

referenciar una casilla particular, dado su nombre (la calle que


representa). ¿A quién le asignamos la responsabilidad?
GRASP: Experto (en información)

 Problema: ¿cuál es el principio general de asignación de

responsabilidades a objetos?

 Solución: Asigna cada responsabilidad al experto de

información, i.e. la clase que tiene (la mayor parte de) la


información necesaria para cubrir la responsabilidad.
GRASP: Experto (en información)

 ¡ATENCIÓN!
A veces hay que aplicar el
Information Expert en cascada.
 Ejemplo: Casa de apuestas

Suponed que queremos asignar


la responsabilidad de calcular la
ganancia/pérdida neta de un
usuario en el siguiente diagrama.

Dibujad el diagrama de
secuencia e indicad los cambios
en el diagrama de clases.
GRASP: Experto (en información)

 Los objetos hacen tareas relacionadas con la información que


poseen.
 Principio de “animación”. (caricatura animada).
Discusión
 Tal como en el mundo real: “damos responsabilidades a quien
tiene la información”.

 Problemas de cohesion y acoplamiento: ¿Quién debería salvar


en la db las apuestas? No debería ser la clase Apuesta
(incohesivo).
Contraindicaciones

 Mantiene encapsulamiento de información (bajo acoplamiento)


 Motiva clases “ligeras” distribuyendo responsabilidades.
Beneficios
GRASP: Bajo acoplamiento

 Asume que necesitamos modelar el acto de tirar los dos dados de un

jugador en el monopoly. ¿A quién asignamos la responsabilidad?


GRASP: Bajo acoplamiento

 Si el jugador tira los

makePayment() dados, es necesario


4: mueve()
acoplar al Jugador con
1: create()
:Monopoly : Jugador conocimiento sobre los
3: tiraDados() Dados (acoplamiento
2:create() que antes no existía)
d:Dados

Jugador also coupled to


knowledge of Dados.
GRASP: Bajo acoplamiento

 Una solución alternativa


es que sea el propio
Juego el que tire los
makePayment() dados y envíe el valor
que ha salido al
1: create() Jugador.
:Monopoly : Jugador
4: mueve(r)  Se disminuye el
acoplamiento entre
2:create() Jugador y Dados y,
d:Dados
3: r=tiraDados() desde el punto de vista
del “acoplamiento” es un
mejor diseño.
GRASP: Bajo acoplamiento

 Problema: ¿Cómo soportar una baja dependencia, un

bajo impacto de cambios en el sistema y un mayor reuso?

 Solución: Asigna una responsabilidad de manera que el

acoplamiento permanezca bajo


GRASP: Bajo acoplamiento

 Acoplamiento: medida que indica cómo de fuertemente un


elemento está conectado a, tiene conocimiento de, o depende
de otros elementos.
 Una clase con un alto acoplamiento depende de muchas otras clases
(librerías, herramientas, etc.)

 Problemas del alto acoplamiento:


 Cambios en clases relacionadas fuerzan cambios en la clase afectada
por el alto acoplamiento.
 La clase afectada por el alto acoplamiento es más difícil de entender
por sí sola: necesita entender otras clases.
 La clase afectada por el alto acoplamiento es más difícil de reutilizar,
porque requiere la presencia adicional de las clases de las que
depende.
GRASP: Bajo acoplamiento

 En general, valora siempre la necesidad de incluir una


nueva relación de dominio en el D. Clases
GRASP: Bajo acoplamiento

 ¿Cómo podríamos desacoplarnos del hecho de que el

monopoly está usando dos dados?


GRASP: Bajo acoplamiento

 Tipos de acoplamiento

El acoplamiento dentro de los D. Clases puede ocurrir por


varios motivos:
 Definición de Atributos: X tiene un atributo que se refiere a una

instancia Y.
 Definición de Interfaces de Métodos: p.ej. un parámetro o una

variable local de tipo Y se encuentra en un método de X.


 Definición de Subclases: X es una subclase de Y.

 Definición de Tipos: X implementa la interfaz Y.

¿CUÁL ES MEJOR?
GRASP: Bajo acoplamiento

 No hay una medida específica para el acoplamiento, pero en


general, clases que son genéricas y candidatas al reuso
DEBERÍAN asegurar un bajo acoplamiento
Discusión
 Siempre habrá algo de acoplamiento entre objetos, ya que de
otro modo no habría colaboración

 Rara vez es un problema tener alto acoplamiento con elementos


estables y con elementos “pervasive” (de uso amplio).
 Ej.- Una aplicación J2EE puede acoplarse de forma segura con
Contraindicaciones las bibliotecas propias de Java (java.util, etc.), ya que ellas son
estables y de uso amplio.
 No vale la pena “desgastarse” en disminuir acoplamiento
cuando no exista real motivación.
 No existen “efectos colaterales” debido a cambios en otros
componentes.
 Simple de entender aisladamente.
Beneficios
 MUY conveniente para el reuso.
 La mayoría de los patrones favorecen EL BAJO
ACOPLAMIENTO!! E.g experto, creador, …
GRASP: Controlador

 En el Monopoly puede haber


System múltiples operaciones del

initialize(nop) sistema, que en un principio se


playGame() podrían asignar a una clase
...
System.
 Sin embargo esto no significa
que finalmente deba existir una
clase software System que
satisfaga este requisito; es
mejor asignar las
responsabilidades a uno o más
Controllers.
GRASP: Controlador

 ¿En el monopoly, quién debería ser el Controlador para

los eventos de sistema, tales como playGame e initialize?


GRASP: Controlador

 Existen dos posibilidades:

 MonopolyGame

 ProcessInitializeHandler (esta solución requiere que haya otros

controladores como ProcessPlayGameHandler, etc.).

playGame() :MonopolyGame

playGame()
:ProcessPlayGameHandler
GRASP: Controlador
GRASP: Controlador

 Problema: ¿Quién debería ser el responsable de manejar un


evento de entrada al sistema?
 ¿Quién es el primer objeto de la capa de dominio que recibe los
mensajes de la interfaz?

 Solución: Asigna la responsabilidad de recibir o manejar un


evento del sistema a una clase que represente una de estas
dos opciones:
 El sistema completo (Control „fachada‟).

 Un escenario de un Caso de Uso.

 Estandariza nomenclatura: ControladorRealizarCompra,


CoordinadorRealizarCompra, SesionRealizarCompra,
ControladorSesionRealizarCompra.
GRASP: Controlador
System Register
 El Controlador del
endSale()
enterItem()
...
que habla este
endSale()
patrón es un objeto
makeNewSale()
makePayment() enterItem()
makeNewSale()
makeNewReturn()
enterReturnItem()
makePayment()
que NO pertenece a
. . . makeNewReturn()
enterReturnItem() la capa de interfaz y
que define el método
. . .

system operations allocation of system para la operación del


discovered during system operations during design,
behavior analysis using one facade controller sistema.
 Normalmente
ProcessSale HandleReturns
ventanas, applets etc
System Handler Handler
reciben eventos
endSale() ... ...
enterItem()
makeNewSale() endSale() enterReturnItem()
mediante sus propios
makePayment() enterItem()
makeNewSale()
makeNewReturn()
. . . controladores de
interfaz, y los
enterReturnItem() makePayment()
makeNewReturn()

DELEGAN al tipo de
. . .

allocation of system
operations during design,
using several use case
controlador del que
controllers
hablamos aquí.
GRASP: Controlador

 Mal diseño

presses button

Player

actionPerformed( actionEvent )

It is undesirable for an interface


layer object such as a window to get
Interface Layer :MonopolyJFrame involved in deciding how to handle
domain processes.

Business logic is embedded in the


presentation layer, which is not useful.

1: playGame()
Domain Layer :Board

MonopolyJFrame should not


send this message.
GRASP: Controlador

 Buen diseño

presses button

: Jugador

actionPerformed( actionEvent )

Es el run()
system event message
Interface Layer :MonopolyJFrame de las
prácticas
de POO
1: playGame()

controller

Domain Layer 1.1: playGame()


:Monopoly :Board
GRASP: Controlador

 El Controlador recibe las solicitudes de servicios desde la

capa UI y coordina su realización, generalmente


delegando a otros objetos.

 Los Controladores permiten también implementar la

“secuencia de operaciones” o mantener el “estado del


caso de uso”.
 P.ej.- initialize( ) no se puede realizar a menos que el juego acabe

de empezar.

 ¿Cuándo pensáis que es mejor utilizar cada tipo de

Controlador? (controlador único o para cada caso de uso)


GRASP: Alta cohesión

 ¿Cómo reescribiríais el siguiente código del monopoly?


Monopoly::PlayGame(){
turno=random(numJug);
Dados d[2]=cub.getDados();
int punt=0;
for (i=1 to 2)
punt+=dado[i].tirarDado();
Casilla cAct=jug[turno].getCasillaAct();
Casilla cNueva=cAct+punt;
jug[t].colocaEnCasilla(cNueva)

turno=turno+1 mod numJug;

}
GRASP: Alta cohesión
GRASP: Alta cohesión

 Cohesión: medida de cómo de fuertemente se relacionan

y focalizan las responsabilidades de un elemento. Los


elementos pueden ser clases, subsistemas, etc.

 Una clase con baja cohesión hace muchas actividades

poco relacionadas o realiza demasiado trabajo.

 Problemas causados por un diseño con baja cohesión:

 Difíciles de entender.

 Difíciles de usar.

 Difíciles de mantener.

 Delicados: fácilmente afectables por el cambio.


GRASP: Alta cohesión

 Problema: ¿Cómo mantener la complejidad manejable?

 Solución: Asigna las responsabilidades de manera que la

cohesión permanezca alta.


 Clases con baja cohesión a menudo representan abstracciones

demasiado elevadas, o han asumido responsabilidades que


deberían haber sido asignadas a otros objetos.
GRASP: Alta cohesión

 Existen diversos grados de cohesión funcional

 Cohesión muy baja: clase responsable de muchas cosas en muchas

áreas distintas.
 ej.: una clase responsable de hacer de interfaz con una base de datos y con un

servicio web.

 Baja cohesión: clase responsable de una tarea compleja en un área

funcional.
 ej.: una clase responsable de interactuar con una base de datos completa

 Alta cohesión: clase que tiene responsabilidades moderadas en un área

funcional y colabora con otras clases para realizar una tarea.


 ej.: una clase responsable de una parte de la interacción con la BD.
GRASP: Alta cohesión

 Regla general:

Discusión Una clase con alta cohesión tiene un número relativamente bajo
de operaciones, con funcionalidad altamente relacionada, y no
hace demasiado trabajo, sino que colabora y delega.
 Existen pocos casos que contraindiquen la “Alta cohesión”.
Algunos ejemplos podrían ser:
 Agrupar responsabilidades o código en una sola clase o
componente para simplificar el mantenimiento por una sola
persona (cuando el mapeo objeto relacional lo hace un
experto en SQL, pero novato en OO).
Contraindicaciones
 Servidor de objetos distribuidos, debido a las impliciaciones
de “overhead” o “performance” asociadas a objetos
remotos y a la comunicación remota. A veces es deseable
crear pocos objetos servidores, menos cohesivos que
ofrezcan una interfaz para muchas operaciones. Esto es
relativo al patrón Interface Remota de Grano grueso.
 Clases más fáciles de entender, de usar, de mantener, y menos
Beneficios sensibles al cambio
GRASP: Cohesión vs. acoplamiento

 Cohesión y acoplamiento

son el “yin and yang” de la


ingeniería del software,
debido a su interdependencia
e influencia.
Patrones GRASP avanzados
Polimorfismo
Fabricación pura
Indirección
Protección de variaciones
GRASP: Polimorfismo

 Vamos ahora a incluir el concepto de tipos de casillas en el Monopoly.

 Distintos tipos de casillas. En función del tipo de casilla, el

comportamiento del método caerEn() varía:


 Casillas de suerte: coger una carta de la suerte

 Casillas de propiedades: depende de si la casilla tiene propietario o no, y

si lo tiene si soy yo o es un contrincante del juego

 Casilla de Vaya a la Cárcel: ir a la cárcel

 Casilla de Tasas: pagar tasas

 …

 ¿A quién asigno la responsabilidad caerEn()?¿Cómo lo implemento?


GRASP: Polimorfismo

 Problemas:

 ¿Cómo manejar alternativas basadas en un tipo sin usar sentencias

condicionales if-then o switch que requerirían modificación en el código?

 ¿Cómo crear componentes software “conectables”?

 Viendo los componentes software en una relación cliente-servidor ¿Cómo

reemplazar un componente servidor con otro sin afectar al cliente?

 Solución:

 Cuando alternativas o comportamientos relacionados varían por el tipo

(clase), asigna la responsabilidad del comportamiento usando


“operaciones polimórficas” a los tipos para los cuales el comportamiento
varía.

 Corolario: No preguntes por el tipo del objeto usando lógica condicional

para realizar las alternativas variantes basadas en el tipo.


GRASP: Polimorfismo
GRASP: Polimorfismo
GRASP: Polimorfismo

 ¿Cuándo usar interfaces (y no clases abstractas)? Si hay una


jerarquía de clases con una superclase abstracta C1, considera
Discusión
hacer una interfaz I1 de las signaturas de los métodos públicos
de C1 y después declara que C1 implementa I1.
 Diseñar con polimorfismo cuando no se tiene la certeza de la
Contraindicaciones posible variación es “tiempo y esfuerzo perdido”. Evalúe la
necesidad y diseñe con verosimilitud.
 Extensiones para el manejo de las variaciones son fáciles de
manejar.
Beneficios  Se pueden introducir nuevas implementaciones sin afectar a los
clientes.

 GoF (Adapter, Command, Composite, Proxy, State, Strategy)


Patrones
relacionados

 Choosing Message, Don’t Ask “What Kind?”


También conocido
como
GRASP: Fabricación pura

 Es necesario guardar instancias del Monopoly en una base de datos


relacional. ¿Quién debería tener esa responsabilidad?
 Por Expert la clase Monopoly debería tener esta responsabilidad, sin
embargo:
 La tarea requiere un número importante de operaciones de base de datos, ninguna
relacionada con el concepto de Monopoly, por lo que Monopoly resultaría
incohesiva.
 Monopoly quedaría acoplado con la interface de la base de datos (ej.- JDBC en
Java, ODBC en Microsoft) por lo que el acoplamiento aumenta. Además el
acoplamiento sería a una tecnología específica de base de datos, no a otro objeto
del dominio (que sería menos grave).
 Guardar objetos en una base de datos relacional es una tarea muy general para la
cual se requiere que múltiples clases le den soporte. Colocar éstas en Monopoly
sugiere pobre reuso o gran cantidad de duplicación en otras clases que hacen lo
mismo.
GRASP: Fabricación pura

 Solución: Crear una clase (PersistentStorage) que sea

responsable de guardar objetos en algún tipo de


almacenamiento persistente (tal como una base de datos
relacional).

PersistentStorage

By Pure Fabrication

insert( Object )
update( Object )
...
GRASP: Fabricación pura

 Problemas resueltos:

 La clase Monopoly continua bien definida, con alta cohesión y bajo

acoplamiento.

 La clase PersistentStorage es, en sí misma, relativamente

cohesiva, tiene un único propósito de almacenar o insertar objetos


en un medio de almacenamiento persistente.
 La clase PersistentStorage es un objeto genérico y reusable.
GRASP: Fabricación pura

 Problema: ¿Qué objeto debería tener la responsabilidad, cuando no


se desean violar los principios de “Alta Cohesión” y “Bajo
Acoplamiento” o algún otro objetivo, pero las soluciones que sugiere
Expert (por ejemplo) no son apropiadas o cuando no es apropiado
asignarlo a una clase software inspirada a partir de una clase
conceptual?
 Solución: Asigne un conjunto “altamente cohesivo” de
responsabilidades a una clase artificial conveniente que no
represente un concepto del dominio del problema, algo producto de
la “imaginación” para soportar “high cohesion”, “low coupling” y reuso.
 Éste es precisamente el ppio que se aplica cuando se introducen clases
„Helper‟ y clases „Utility‟
GRASP: Fabricación pura

 En sentido amplio, los objetos pueden dividirse en dos grupos:

 Aquellos diseñados por/mediante descomposición

representacional. (Ej.- Monopoly representa el concepto


“partida”)
 Aquellos diseñados por/mediante descomposición

conductual. (Ej.- Para agrupar comportamientos o


algoritmos; clases sin nombre ni propósito relacionado con el
mundo real, e.g. TableOfContentsGenerator). Este es el caso
más común para objetos Fabricación pura.
GRASP: Fabricación pura

 El principio de descomposición conductual para objetos


Fabricación Pura en ocasiones es sobreutilizado por
novatos en diseño y tienden a dividir el software en
términos de funciones.

Exagerando: las funciones se convierten en objetos.


(Clases “functoides”).
¡Tened cuidado con esto si continuamente estáis pasando
objetos como parámetros para que sean procesados por
métodos!
GRASP: Indirección

 Ejemplo: Cubilete

 El objeto Cubilete que hemos comentado cuando hablábamos de

Bajo Acoplamiento es un ejemplo de indirección: un elemento que


no existía en el juego real pero que introducimos para aislarnos del
número de dados que usa el juego.
GRASP: Indirección

 Problema:

 ¿Dónde asignar una responsabilidad para evitar acoplamiento

directo entre dos o más cosas? ¿Cómo desacoplar objetos de tal


manera que el bajo acoplamiento se soporte y el reuso potencial
se mantenga alto?

 Solución:

 Asignad la responsabilidad a un objeto intermedio que medie

entre otros componentes o servicios, de tal manera que los objetos


no estén directamente acoplados. El objeto intermedio crea una
indirección entre los componentes.
GRASP: Indirección

 “Muchos problemas en ciencias de la computación


pueden resolverse mediante otro nivel de indirección”
es un viejo adagio con relevancia particular en diseño
orientado a objetos (David Wheeler).
 Así como muchos patrones de diseño son especializaciones de
Fabricación Pura, muchos otros también lo son de
Indirección (Adapter, Facade, Observer, entre otros).
 Además muchas Fabricaciones Puras son generadas por
causa de Indirección.
 La motivación principal es el bajo acoplamiento; por lo que
un intermediario se agrega para desacoplar otros componentes
o servicios.
GRASP: Protección de variaciones

 Imaginad que sois una empresa de creación de juegos de

mesa por internet. Os han pedido que implementéis el


Monopoly, pero sabéis que en breve os van a pedir
también „La isla del tesoro‟, y posiblemente más adelante
otros, como „En busca del imperio cobra‟.

Cada juego utiliza distintos tipos de casilla (variación


entre juegos).
¿Cómo protegeríais el diseño de esa variación?
GRASP: Protección de variaciones

 Solución: Usar polimorfismo para abstraer los tipos de casilla

ilustraría el concepto de Protección de Variaciones.


 El punto de inestabilidad por variaciones son las diferentes interfaces

o APIs de los distintos tipos de casillas de los distintos juegos.

 Mediante un nuevo nivel de Indirección, una interfaz y usando

polimorfismo con varias implementaciones Casilla, se logra la


protección de variaciones dentro del sistema a partir de las
variaciones en las APIs externas.

 Las partes del sistema comunes colaboran con una interfaz estable;

las implementaciones de las casillas encapsulan las variaciones en


función del juego.
GRASP: Protección de variaciones

 Problema:

 ¿Cómo diseñar objetos, subsistemas y sistemas de tal

manera que las variaciones o inestabilidad en estos


elementos no tenga un impacto indeseable sobre otros
elementos?

 Solución:

 Identifique los puntos de variación o inestabilidad;

asigne responsabilidades para crear una interfaz


estable a su alrededor.
Principios de diseño motivados
por la protección de variaciones
Robert C. Martin. Agile Software Development,
Principles, Patterns, and Practices. 2002.
Principios de diseño motivados por PV

 Encapsulación
 Diseñar operaciones de manera que consultan o
modifican, pero no hacen ambas cosas a la vez
 Separación Modelo-Vista: objetos del modelo no deberían
conocer objetos de presentación, para promocionar bajo
acoplamiento de otras capas hacia la capa de interfaz
(que es la que más cambia)
 Principio de Sustitución de Liskov: Una instancia de una
clase derivada debe ser capaz de tomar el lugar de una
instancia de una clase base. Por ejemplo, si un método
tiene un objeto de una clase como argumento, el mismo
método debe ser capaz de trabajar con una instancia de
una clase derivada.
Principios de diseño motivados por PV

 Principio OPEN-CLOSE (Bertrand Meyer)

 Los módulos software (paquetes, métodos, clases, etc.) deberían

estar ABIERTOS a la extensión y CERRADOS a la modificación


 Diseña el software de manera que puedas extender su capacidad

(añadir nuevas funcionalidades) tocando lo menos posible el código que


ya existe. La mayoría de los cambios se materializan en la adición de
métodos o clases al sistema

 No siempre es posible seguir este principio de manera completa,

pero mientras más lo sigas más fácil será después acomodar


nuevos (y quizás inesperados) requisitos.
Principios de diseño motivados por PV
 PRINCIPIO DE “INVERSIÓN” DE DEPENDENCIA
 Los módulos de alto nivel no deberían depender de módulos de bajo nivel.
Tanto unos como otros deberían depender de abstracciones
 Las abstracciones no deberían depender de detalles, sino al contrario: los
detalles deberían depender de las abstracciones.
 Esto implica que el acoplamiento entre los objetos que usan y los objetos que
son usados se debería hacer siempre a nivel conceptual (en términos de
„servicios‟ que unos requieren de los otros), sin tener en cuenta los detalles
concretos de las implementaciones.
 Ojo! Este principio “invierte” la idea convencional de que módulos de alto nivel
deberían depender de módulos de bajo nivel.
 Llevar este principio al extremo supondría asumir que…
 No deberían existir variables que referenciasen clases concretas
 Ninguna clase debería derivar de una clase concreta
 Ningún método debería sobreescribir un método implementado de una de
sus clases base
 En la práctica no es útil llegar a este extremo, pero sí ser consciente de que
cuando se violan estos principios estamos añadiendo acoplamiento
Principios de diseño motivados por PV

 Interface Segregation Principle: Clients should not be forced to


depend upon interfaces that they do not use. Many client-specific
interfaces are better than a general-purpose one.
 Reuse/Release Equivalency Principle: The granule of reuse is the
same as the granule of release. Only components that are released
through a tracking system can be effectively reused.
 Common Reuse Principle: Classes that are not reused together
should not be grouped together.
 Common Closure Principle: Classes that change together, belong
together.
 Stable Abstractions Principle: The more stable a category of classes
is, the more it should consist of abstract classes. A completely stable
category should consist of only abstract classes.
Principios de diseño motivados por PV

 Least Astonishment Principle: When two elements of an interface


conflict or are ambiguous, the behavior should be that which least
surprises the software engineer at the time of the conflict, because
the least surprising behavior must be usually the correct one.
 Deep Abstract Hierarchies Principle: Class hierarchies should be
deep and abstract.
 The Acyclic Dependencies Principle: There should be no cycles in the
dependency graph.
 The Stable Dependencies Principle: Depend in the direction of
stability. The dependencies between components in a design should
be in the direction of stability. A component should only depend upon
components that are more stable than it is.
Principios de diseño motivados por PV
 Don’t Talk to Strangers (Ley de Demeter): Cada unidad
(clase, método) sólo debería utilizar un conjunto limitado de
otras unidades, y sólo entre aquéllas que están fuertemente
relacionadas con la unidad actual
 Este principio restringe a qué objetos debería enviárseles
mensajes dentro de un método:
 Al objeto this (o self).
 A un parámetro del método.
 A un atributo de this.
 A un elemento de una colección que es un atributo de this.
 A un objeto creado dentro del método. (local al método).
 La intención es evitar acoplamiento con objetos indirectos.
Los objetos directos son los “familiares del cliente” los
indirectos son los “extraños”.
 El cliente debe hablar con los “familiares” no con los
“extraños”.
 Protege contra cambios estructurales
Principios de diseño motivados por PV
 ID to Objects: convertir claves e ID‟s de objetos en objetos
verdaderos lo más pronto posible (normalmente en cuanto el
ID entra en la capa de dominio del modelo de diseño), para a
partir de ahí trabajar con el objeto.
 ¿Por qué?: Tener un objeto real, con información y
responsabilidades (y no simplemente un ID) flexibiliza la
aplicación según el diseño crece, ya que es probable que
necesidades no percibidas originalmente surjan durante la
evolución del sistema.
 Pasar objeto agregado como parámetro: cuando una
operación requiere como parámetros objetos que están
agregados dentro de otros, pasar el objeto agregado, y no los
objetos „hijos‟.
 ¿Por qué? Pasar el objeto agregado aumenta flexibilidad del
sistema, al permitir que la operación colabore con el objeto
agregado en modos que en un principio no habíamos
previsto.
EJERCICIO

 Volved al ejercicio que realizasteis en el tema anterior, en

el que identificasteis, sobre un diagrama de clases de


vuestra elección, el conjunto de elementos que podrían
ser patrones (una buena práctica que deberíais repetir si
os encontráseis ante un problema similar)

 Revisar vuestra conclusión inicial a la luz de los patrones

Larman
EJERCICIO

 EJERCICIO PARA ENTREGAR (OBLIGATORIO)


 Bajáos el documento con las reglas completas del Monopoly

 Realizad un Modelo de Dominio y un Modelo de Diseño (al menos


el diagrama de clases de diseño) del juego que permita implementar
dicho juego en modo simulación.
 Realizad un comentario de vuestro modelo indicando qué principios
GRASP habéis aplicado en las distintas decisiones de asignación de
responsabilidades

 A partir de ahora, tened esta versión del juego siempre a mano,


para poder irla flexibilizando según vayamos aprendiendo nuevos
patrones.
¿Dudas?
Ingeniería del Software 1 - Curso 2010/2011

You might also like