Fragilidad : Es la tendencia del software a

13 

Texto completo

(1)

Principios de Diseño de Software

Ing. Jorge Luis Chuc López

08/03/2010 Ing. Jorge Luis Chuc López 1

Ing. Jorge Luis Chuc López

Instituto Tecnológico de Campeche

Síntomas de diseños en

descomposición

Rigidez

: el software es difícil de modificar,

incluso en formas sencillas. Cada cambio

causa cascadas de cambios subsecuentes en

módulos dependientes

módulos dependientes

Fragilidad

: Es la tendencia del software a

romperse en muchos lugares cada vez que es

modificado. Con frecuencia ocurre en áreas

que no tienen relación conceptual con el área

modificada

08/03/2010 Ing. Jorge Luis Chuc López 2

Síntomas de diseños en

descomposición

Inmovilidad

: es la inhabilidad para reutilizar

software de otros proyectos o de partes del

mismo proyecto.

Viscosidad

: Puede ser en una de dos formas:

Viscosidad del diseño: Se dice que la viscosidad es

alta cuando los métodos de preservación del diseño son más difíciles de emplear que realizar un parche en el software: es más fácil hacer lo incorrecto que lo correcto.

Viscosidad del ambiente: sucede cuando el

ambiente de desarrollo es lento e ineficiente.

08/03/2010 Ing. Jorge Luis Chuc López 3

Síntomas de diseños en

descomposición

Complejidad innecesaria

: Cuando al

desarrollador exagera el diseño. Cuando diseña

una clase intenta anticipar cada posible

necesidad (p. ej., el antipatrón de “navaja de la

armada suiza”). Otro caso es aplicar

armada suiza”). Otro caso es aplicar

demasiados patrones a un problema sencillo.

Repetición innecesaria

: El mismo código es

diseminado lo que propicia que sea propenso a

errores.

No hacer diseño

.

(2)

Causas de la descomposición

del diseño

Requerimientos cambiantes

Los requerimientos cambian en formas que el

diseño inicial no anticipó.

Los cambios deben realizarse rápidamente y Los cambios deben realizarse rápidamente y

puede ser que los desarrolladores no estén familiarizados con la filosofía de diseño original.

Así, aunque el cambio funciona, de alguna manera

viola el diseño original.

El diseñador debe encontrar una manera de hacer

resilientes los diseños a los cambios.

08/03/2010 Ing. Jorge Luis Chuc López 5

Causas de la descomposición

del diseño

Administración de la dependencia

Los cambios que introducen nuevas dependencias

no planeadas, son la causa principal del deterioro del diseño.

del diseño.

Cada uno de los cuatro síntomas anteriores son

causados por dependencias impropias entre los módulos del software, de manera directa o indirecta.

Para prevenir la degradación de la dependencia de

la arquitectura, deben manejarse las

dependencias entre módulos en una aplicación.

08/03/2010 Ing. Jorge Luis Chuc López 6

Principios de Diseño

Reglas a tener en cuenta siempre en el

diseño:

Aplicar siempre el sentido común.

No ser demasiado dogmático/religioso.

No ser demasiado dogmático/religioso. Cada decisión es un compromiso.

Los principios de diseño sólo son:

Directrices (pautas). “Mejores prácticas”

Es necesario analizar cuidadosamente si se deben

violar estos principios.

Principios de diseño

Principio de Responsabilidad única (Single Responsability Principle, SRP)

Principio Abierto-Cerrado (Open-Close Principle, OCP).

OCP).

Principio de sustitución de Liskov (Liskov Substitution Principle, LSP).

Principio de Inversión de dependencia (Dependency Inversion Principle, DIP).

(3)

Principio de Responsabilidad Única

(Single Responsability Principle, SRP)

Nunca deberá haber más de una razón

para que una clase cambie.

También conocido como

Alta Cohesión

.

Una clase deberá ser cohesiva; esto es,

Una clase deberá ser cohesiva; esto es,

deberá tener sólo un único propósito para

que sus objetos vivan, y todos sus

métodos deberán trabajar juntos para

ayudar a lograr un objetivo.

08/03/2010 Ing. Jorge Luis Chuc López 9

Principio de Responsabilidad Única

(Single Responsability Principle, SRP)

08/03/2010 Ing. Jorge Luis Chuc López 10 La clase Rectangulo tiene dos responsabilidades, actúa como un modelo matemático de la geometría del rectángulo y dibuja el rectángulo sobre una interfaz de usuario gráfica.

Principio de Responsabilidad Única

(Single Responsability Principle, SRP)

08/03/2010 Ing. Jorge Luis Chuc López 11

Principio de Responsabilidad Única

(Single Responsability Principle, SRP)

Cuando una clase tiene más de una

responsabilidad (esto es, razón para

cambiar) estas responsabilidades están

acopladas.

acopladas.

Esto ocasiona que la clase sea:

Más difícil de entender (complejidad

innecesaria).

Más difícil de cambiar (rigidez).

Más difícil de reutilizar (inmovilidad).

(4)

Principio de Responsabilidad Única

(Single Responsability Principle, SRP)

Lo difícil de aplicar este principio es

lograr la granularidad correcta para una

responsabilidad.

Si no es posible separar las

responsabilidades en clases separadas,

al menos considere separarlas en

diferentes interfaces (Principio de

Segregación de Interfaz).

08/03/2010 Ing. Jorge Luis Chuc López 13

Principio Abierto-Cerrado

Acuñado por Bertrand Meyer. También conocido como:

Variaciones protegidas

Lo que David L. Parnas acuñó como “ocultamiento de

información”. información”.

Las entidades de software (clases, módulos, funciones, etc.) deberán estar abiertos para la extensión, pero cerrados para la modificación.

Deberán diseñarse las clases para que no cambien.

08/03/2010 Ing. Jorge Luis Chuc López 14

Principio Abierto-Cerrado

Si no se sigue el OCP, un cambio en un

programa puede resultar en una

cascada de cambios en los módulos

dependientes.

dependientes.

El programa se hace frágil, rígido,

impredecible, y no facilita su reutilización.

Principio Abierto-Cerrado

Las clases que se apegan a el OCP tienen dos

atributos principales:

Están “abiertos para la extensión”:

Esto significa que el comportamiento del código puede ser extendido, haciendo que se comporten de una forma nueva extendido, haciendo que se comporten de una forma nueva o diferente.

Están “cerrados para la modificación”:

El código fuente del módulo no debe ser modificado. ¿Es una contradicción? ¿Cómo extender algo sin

modificarlo?

(5)

Principio Abierto-Cerrado

08/03/2010 Ing. Jorge Luis Chuc López 17

Principio Abierto-Cerrado

// Ejemplo que viola el principio Abierto-Cerrado.

class GraphicEditor {

public void drawShape(Shape s) { if (s.m_type==1)

drawRectangle(s); drawRectangle(s); else if (s.m_type==2)

drawCircle(s); }

public void drawCircle(Circle r) {....}

public void drawRectangle(Rectangle r) {....}

}

08/03/2010 Ing. Jorge Luis Chuc López 18

Principio Abierto-Cerrado

class Shape { int m_type; }

class Rectangle extends Shape { Rectangle() {

super.m_type=1; super.m_type=1; }

}

class Circle extends Shape { Circle() {

super.m_type=2; }

}

08/03/2010 Ing. Jorge Luis Chuc López 19

Principio Abierto-Cerrado

(6)

Principio Abierto-Cerrado

// Ejemplo que sigue el principio

// abierto-cerrado

class GraphicEditor {

public void drawShape(Shape s) {

public void drawShape(Shape s) {

s.draw();

}

}

08/03/2010 Ing. Jorge Luis Chuc López 21

Principio Abierto-Cerrado

class Shape {

abstract void draw();

}

class Rectangle extends Shape {

public void draw() {

// Dibuja el rectángulo

}

}

08/03/2010 Ing. Jorge Luis Chuc López 22

Implicaciones del OCP

(ejemplos)

Utilizar modificadores privados para los

miembros de las clases.

Abstenerse de agregar getters

Abstenerse de agregar getters

automáticamente.

Utilizar abstracciones.

Extender mediante herencia y delegación.

Inversión de Control (IoC).

Principio de Sustitución de

Liskov (LSP)

Enunciado por Bárbara Liskov en 1987:

Si para cada objeto o1de tipo Sexiste un objeto o2 de tipo Ttal que para todos los programas P definidos en términos de T, el comportamiento

de Pno cambia cuando o1 es sustituido por o de Pno cambia cuando o1 es sustituido por o

o2 entonces Ses un subtipo de T.

Enunciado por Robert C. Martín como:

Las funciones que utilizan apuntadores o referencias a clases base deben ser capaz de

(7)

Principio de Sustitución de

Liskov (LSP)

En pocas palabras, el LSP establece que

en jerarquías de clases deberá ser

posible tratar a un objeto especializado

como si fuera un objeto de la clase

como si fuera un objeto de la clase

base.

08/03/2010 Ing. Jorge Luis Chuc López 25

Ejemplo de violación de LSP

Class Rectangulo { private double base; private double altura;

public setBase(double b) {base = b;} public setBase(double b) {base = b;} public double getBase() {return base;} public setAltura(double a) {altura = a;} public double getAltura() {return altura;} }

08/03/2010 Ing. Jorge Luis Chuc López 26

Ejemplo de violación de LSP

class Cuadrado extends Rectangulo { public setBase(double b) {

base = b; altura = b; altura = b; }

public setAltura(double a) { altura = a;

base = a; }

}

08/03/2010 Ing. Jorge Luis Chuc López 27

Ejemplo de violación de LSP

Podemos crear un objeto Cuadrado de la

siguiente manera:

Cuadrado c;

c.setBase(10);

c.setBase(10);

c.setAltura(20);

Ahora, considere el siguiente método:

Public void f(Rectangulo r) {

r.setBase(30);

}

(8)

Ejemplo de violación de LSP

¿Qué pasa si enviamos a f una variable de

referencia a un objeto de la clase Cuadrado?

¿Cambiará su altura?

No. Existe una violación al LSP.

No. Existe una violación al LSP.

LSP es una extensión de OCP y significa que

debemos asegurarnos de que las nuevas

clases derivadas están extendiendo las clases

bases sin cambiar su comportamiento.

08/03/2010 Ing. Jorge Luis Chuc López 29

Principio de Inversión de

Dependencia (DIP)

En una aplicación frecuentemente encontramos clases de bajo nivel, que implementan operaciones básicas y primarias.

También encontramos clases de alto nivel que También encontramos clases de alto nivel que

encapsulan lógica compleja y se apoyan en las clases de bajo nivel.

Una forma natural de implementar estas estructuras es escribir primero las clases de bajo nivel y

posteriormente las clases complejas de alto nivel. Sin embargo este diseño no es flexible ¿qué pasa si se requiere reemplazar a una clase de bajo nivel?

08/03/2010 Ing. Jorge Luis Chuc López 30

Principio de Inversión de

Dependencia (DIP)

Copy

ReadKeyboard WritePrinter

Estructura de un programa que copia los caracteres escritos en el teclado hacia una impresora.

Nótese que ReadKeyboard y WritePrinter son totalmente reutilizables. ¿Copy es reutilizable?

Principio de Inversión de

Dependencia (DIP)

void Copy() {

int c;

while ((c = ReadKeyboard()) != EOF)

while ((c = ReadKeyboard()) != EOF)

WritePrinter(c);

}

(9)

Principio de Inversión de

Dependencia (DIP)

¿Qué tendriamos que hacer si deseamos que el

programa copie desde el teclado a un archivo?

enum OutputDevice {printer, disk}; void Copy(outputDevice dev) {

int c;

while ((c = ReadKeyboard()) != EOF) if (dev == printer)

WritePrinter(c); else

WriteDisk(c); }

08/03/2010 Ing. Jorge Luis Chuc López 33

Principio de Inversión de

Dependencia (DIP)

A.

Los módulos de alto nivel no

deberán depender de los módulos

de bajo nivel, ambos deberán

depender de abstracciones.

depender de abstracciones.

B.

Las abstracciones no deberán

depender de los detalles. Los

detalles deberán depender de las

abstracciones.

08/03/2010 Ing. Jorge Luis Chuc López 34

Principio de Inversión de

Dependencia (DIP)

Copy

Reader Writer

abstract abstract

08/03/2010 Ing. Jorge Luis Chuc López 35 KeyboardReader PrinterWriter

abstract abstract

Principio de Inversión de

Dependencia (DIP)

class Reader {

public:

virtual int Read() = 0;

};

};

class Writer {

public:

virtual void Write(char) = 0;

};

(10)

Principio de Inversión de

Dependencia (DIP)

void Copy(Reader& r, Writer& w) {

int c;

while((c=r.Read()) != EOF)

while((c=r.Read()) != EOF)

w.Write(c);

}

08/03/2010 Ing. Jorge Luis Chuc López 37

Principio de Segregación de

Interfaz (ISP)

Considere un sistema de seguridad, donde hay

dos objetos

Door

que pueden estar o no

cerradas con llave.

class Door {

public:

virtual void Lock() = 0;

virtual void Unlock() = 0;

virtual bool IsDoorOpen() = 0;

};

08/03/2010 Ing. Jorge Luis Chuc López 38

Principio de Segregación de

Interfaz (ISP)

Door es una clase abstracta.

Los objetos usuarios de Door no dependerán

de implementaciones particulares de Door.

Considere una implementación de Door, de

Considere una implementación de Door, de

nombre TimedDoor que requiere sonar una

alarma cuando se ha dejado la puerta abierta

mucho tiempo.

Para lograr este efecto, TimedDoor se apoya

en otro objeto llamado Timer.

Principio de Segregación de

Interfaz (ISP)

class Timer { public:

void Register(int timeout, TimerClient* client);

}; };

class TimerClient {

public:

(11)

Cuando un objeto desee ser informado

que ha transcurrido un periodo de

tiempo, debe llamar a la función

Register() de un Timer.

Register() de un Timer.

Los argumentos a la función Register

son el tiempo que debe transcurrir y un

apuntador a un objeto TimerClient a

cuya función TimeOut se llamará

cuando el tiempo haya transcurrido.

08/03/2010 Ing. Jorge Luis Chuc López 41

Solución común: polución de

interfaz

TimerClient • La clase Door depende de la clase TimerClient.

• Sin embargo, nótese que no todas las variedades de Door requieren temporización.

• De hecho la abstracción original

08/03/2010 Ing. Jorge Luis Chuc López 42 Door

TimedDoor

• De hecho la abstracción original Door no tiene nada que ver con temporizaciones.

• Con esta solución, para crear clases Door sin temporización la

implementación del método TimeOut de TimerClient deberán ser nulos.

Polución de interfaz

Es cuando una clase se ha contaminado

con una interfaz que no requiere.

Por lo general esto ocurre para beneficiar

a una de sus subclases.

a una de sus subclases.

Si se sigue esta práctica, cada vez que

una subclase requiera una interfaz, se

deberá agregar a la clase base.

La interfaz de la clase base terminará

siendo “obesa”.

08/03/2010 Ing. Jorge Luis Chuc López 43

Polución de interfaz

Más aún, cada vez que se agregue una

nueva interfaz a la clase base, esa

interfaz deberá ser implementada por

las clases derivadas.

las clases derivadas.

Puede ocasionar problemas de

mantenimiento y reutilización.

(12)

Principio de Segregación de

Interfaz (ISP)

Los clientes no deberán estar

obligados a depender de interfaces

que no utilizan.

08/03/2010 Ing. Jorge Luis Chuc López 45

Principio de Segregación de

Interfaz (ISP)

Door y TimerClient representan interfaces

que son utilizadas por clientes

completamente diferentes: Timer y los

clientes de Door.

clientes de Door.

Estas dos interfaces deben ser

implementadas en en mismo objeto puesto

que la implementación de ambas interfaces

manipula los mismos datos.

¿Cómo nos apegamos al principio de

segregación de interfaz?

08/03/2010 Ing. Jorge Luis Chuc López 46

Delegar es la respuesta

TimerClient

Abstract

Door Abstract

TimedDoor DoorTimerAdapter

Principio de Segregación de

Interfaz (ISP)

Cuando el objeto TimedDoor quiere

registrar una petición de temporización

con un objeto Timer, crea un

DoorTimerAdapter y lo registra con el

DoorTimerAdapter y lo registra con el

Timer.

(13)

Principio de Segregación de

Interfaz (ISP)

class TimedDoor : public Door

{

public:

virtual void DoorTimeOut(int

virtual void DoorTimeOut(int

timeOutId);

};

08/03/2010 Ing. Jorge Luis Chuc López 49

Principio de Segregación de

Interfaz (ISP)

class DoorTimerAdapter : public TimerClient {

public:

DoorTimerAdapter(TimedDoor& theDoor) : itsTimedDoor(theDoor) {}

: itsTimedDoor(theDoor) {}

virtual void TimeOut(int timeOutId) { itsTimedDoor.DoorTimeOut(timeOutId); }

private:

TimedDoor& itsTimedDoor; };

Figure

Actualización...

Referencias

Actualización...

Related subjects :