Anda di halaman 1dari 26

03/06/2015

Lic. Ariel Trellini

Departamento de Ciencias e Ingeniería de la Computación


Universidad Nacional del Sur

Principios de Diseño SOLID


Diseñando soluciones “larga vida”

Arquitectura y Diseño de Sistemas • 2 Lic. Ariel Trellini • DCIC • UNS

1
03/06/2015

Principios SOLID

Bibliografía

Design Principles and Design Patterns


Robert C. Martin
http://www.objectmentor.com/resources/articles/Principles_and_Patterns.pdf

Agile Principles, Patterns, and Practices in C#


Martin, Micah
2006 – Prentice Hall

Arquitectura y Diseño de Sistemas • 3 Lic. Ariel Trellini • DCIC • UNS

Principios SOLID

Olores del Diseño


 Rigidez
 Es la tendencia del software a ser difícil de cambiar, aun ante cambios
simples.
 Ejemplo: Un único cambio causa una cascada de cambios subsecuentes en
módulos dependientes. Cuantos más módulos se deben cambiar, más rígido
es el diseño.
 Fragilidad
 Es la tendencia de un sistema a romperse en varios lugares cuando se hace
un cambio.
 Frecuentemente los nuevos problemas surgen en áreas que no tienen
relación conceptual con el área que fue cambiada.
 Inmobilidad
 Un diseño es inmóvil cuando contiene partes que podrían se útiles en otros
sistemas, pero el esfuerzo y riesgo de separarlas del sistema original es muy
grande
Arquitectura y Diseño de Sistemas • 4 Lic. Ariel Trellini • DCIC • UNS

2
03/06/2015

Principios SOLID  Olores del Diseño

 Viscosidad
 Un software viscoso es uno en el que su diseño es difícil de preservar
 Viene en dos formas:
 Viscosidad del software. Generalmente existen varias formas de hacer un
cambio: algunas preservan el diseño y otras no. Cuando las formas que
preservan el diseño son más difíciles de usar que las que lo vulneran, entonces
la viscosidad del diseño es alta.
 Viscosidad del ambiente. Cuando el ambiente de desarrollo es lento e ineficiente

 Complejidad Innecesaria
 Ocurre cuando el diseño contiene elementos que no son actualmente
útiles.
 Ejemplo: Cuando se anticipan cambios a los requerimientos y se construyen
“facilidades” para manejar dichos cambios potenciales.
 En principio, parece algo bueno que previene pesadillas en futuros cambios.
 Muchas veces, el efecto es opuesto ya que el diseño se satura de mecanismos
que nunca se usan y que el software debe llevar a cuestas.

Arquitectura y Diseño de Sistemas • 5 Lic. Ariel Trellini • DCIC • UNS

Principios SOLID  Olores del Diseño

 Repetición Innecesaria
 Cortar y pegar puede ser útil para operaciones de edición de texto, pero
puede ser desastroso para operaciones de edición de código.
 Cuando el mismo código aparece una y otra vez, en ligeramente distintas
formas, se está necesitando una abstracción.
 Cuando hay código redundante en el sistema, los cambios en el sistema
pueden ser arduos.

 Opacidad
 Es la tendencia de un módulo a ser difícil de entender.
 El código puede ser escrito de una manera clara y expresiva, o de una
manera compleja y opaca.
 A medida que el código evoluciona en el tiempo, llega a ser más y más
opaco.

Arquitectura y Diseño de Sistemas • 6 Lic. Ariel Trellini • DCIC • UNS

3
03/06/2015

Principios SOLID

Introducción
 Los Principios SOLID surgieron a comienzos del año 2000 y su
autor-mentor es Robert C. Martin.

 El término es un acrónimo que surge de los siguientes conceptos:


S Single Responsibility Principle Un objeto debería tener una única responsabilidad.

O Open/Closed Principle Las entidades de software deberían estar abiertas


para extensión pero cerradas para modificación.
L Liskov Substitution Principle Un objeto en un programa podría ser reemplazado
con instancias de sus subtipos sin alterar la
correctitud del programa.
I Interface Segregation Muchas interfaces específicas son mejores que
Principle interfaces de propósitos generales.

D Dependency Inversion Deberíamos depender de las abstracciones y no de


Principle las concreciones.

Arquitectura y Diseño de Sistemas • 7 Lic. Ariel Trellini • DCIC • UNS

Principios SOLID  Introducción

Introducción (cont.)
 Se los considera los cinco principios básicos en el diseño y la
programación orientada a objetos.

 La intención es aplicar estos principios en conjunto para que sea


más probable obtener un software fácil de mantener y extender
en el tiempo.

Arquitectura y Diseño de Sistemas • 8 Lic. Ariel Trellini • DCIC • UNS

4
03/06/2015

Principios SOLID  Introducción

Consideraciones
 Reglas Básicas
 Los principios SOLID son guías, no son reglas inamovibles.

 Usá tu cerebro haciendo cosas que tengan sentido.

 Preguntar ¿por qué…?


 ¿Por qué hago lo que hago?
 ¿Por qué tomé tal decisión?
 Etc.

Arquitectura y Diseño de Sistemas • 9 Lic. Ariel Trellini • DCIC • UNS

Principios SOLID  Single Responsability Principle

Single Responsability Principle


Nunca debería haber más de una razón para que una clase
cambie.

 Este principio se basa en el principio de


cohesión de Tom DeMarco.
 Si una clase tiene más de una
responsabilidad, entonces las mismas
quedan acopladas.
 Los cambios en una responsabilidad
pueden afectar o inhibir la capacidad de
la clase para cumplir con el resto.
 Esta clase de acoplamiento conduce a diseños frágiles que se rompen de
maneras inesperadas cuando se producen cambios.

Arquitectura y Diseño de Sistemas • 10 Lic. Ariel Trellini • DCIC • UNS

5
03/06/2015

Principios SOLID  Single Responsability Principle

¿Qué es una responsabilidad?

En el contexto de SRP, definimos una responsabilidad a una


“razón de cambio”.

 La interfaz Modem parecería lucir perfectamente razonable.


 Existe más de una responsabilidad
 Manejo de la conexión
 Comunicación de datos

Arquitectura y Diseño de Sistemas • 11 Lic. Ariel Trellini • DCIC • UNS

Principios SOLID  Single Responsability Principle

¿Qué es una responsabilidad? (cont.)

 ¿Es necesario separarlas?


Depende de cómo evolucione la aplicación
 Por ejemplo, si la aplicación cambia de manera
que afecta la firma de las funciones de conexión,
el diseño podría oler a rígido. En este caso, sería
mejor tener dos interfaces:
 DataChannel (send, recv)
 Connection (dial, hangup)
 Por otro lado, si la aplicación no cambia de manera que cause que ambas
responsabilidades cambien en momentos diferentes, no habría necesidad de
separarlas. Es más, separarlas podría oler a complejidad innecesaria.

Una razón de cambio es una razón de cambio sólo si el cambio ocurre. No


es prudente aplicar un principio SOLID si no hay un síntoma.

Arquitectura y Diseño de Sistemas • 12 Lic. Ariel Trellini • DCIC • UNS

6
03/06/2015

Principios SOLID  Single Responsability Principle

 Ejemplo 1: Super-Rectángulo
Rectángulo
Aplicación Aplicación
Geométrica +dibujar() Gráfica
+area(): double

GUI

 La clase Rectángulo es utilizada por dos aplicaciones diferentes:


 Una se encarga de la geometría computacional.
 Usa Rectángulo para ayudarla con las matemáticas de las figuras geométricas.
 Nunca dibuja un rectángulo en la pantalla.
 La otra es gráfica en naturaleza. También dibuja el rectángulo en la pantalla.
 Este diseño viola a SRP porque Rectángulo tiene dos responsabilidades:
 i) proveer un modelo matemático para la geometría de un rectángulo
 ii) dibujar el rectángulo en una interface gráfica.
.
Arquitectura y Diseño de Sistemas • 13 Lic. Ariel Trellini • DCIC • UNS

Principios SOLID  Single Responsability Principle

 Ejemplo 1: Super-Rectángulo (cont.)


 Problemas que podrían ocurrir:
 Debemos incluir una dependencia con la GUI en la aplicación geométrica.
 Si un cambio en la aplicación gráfica causa que cambie la clase rectángulo,
podría forzarnos a re-compilar, re-testear y re-instalar la aplicación geométrica,
de manera tal de asegurar su correcto comportamiento.

 Posible solución: Separar las responsabilidades en clases distintas


Aplicación Aplicación
Geométrica Gráfica

Rectángulo Rectángulo
Geométrico GUI
+dibujar()
+area(): double +area(): double

Arquitectura y Diseño de Sistemas • 14 Lic. Ariel Trellini • DCIC • UNS

7
03/06/2015

Principios SOLID  Single Responsability Principle

 Ejemplo 2: Múltiples Funciones


 La clase Persona maneja tanto los datos
filiatorios de la persona como la
información de su saldo de cuenta
 ¿Qué sucedería si ahora se permite que
una cuenta sea compartida por más de
una persona?

 Posible Solución:
 La clase Cuenta
no tiene noción
sobre quién la
posee.
 Persona puede o bien exponer la
propiedad Cuenta, o replicar la interfaz de
Cuenta, delegando la implementación de
sus métodos
Arquitectura y Diseño de Sistemas • 15 Lic. Ariel Trellini • DCIC • UNS

Principios SOLID  Single Responsability Principle

 Ejemplo 3: Violación por Código Spaghetti


public class OrderProcessingModule {
public void Process(OrderStatusMessage orderStatusMessage) {
// Get the connection string from configuration
string connectionString = ConfigurationManager.ConnectionStrings["Main"].ConnectionString;

Order order = null;


using (SqlConnection connection = new SqlConnection(connectionString)) {
// go get some data from the database
order = fetchData(orderStatusMessage, connection);
}

// Apply the changes to the Order from the OrderStatusMessage


updateTheOrder(order);

// International orders have a unique set of business rules


if (order.IsInternational)
processInternationalOrder(order);

// We need to treat larger orders in a special manner


else if (order.LineItems.Count > 10)
processLargeDomesticOrder(order);

// Smaller domestic orders


else
processRegularDomesticOrder(order);

// Ship the order if it's ready


if (order.IsReadyToShip()) {
ShippingGateway gateway = new ShippingGateway();
// Transform the Order object into a Shipment
ShipmentMessage message = createShipmentMessageForOrder(order);
gateway.SendShipment(message);
}
Arquitectura y}Diseño de Sistemas • 16 Lic. Ariel Trellini • DCIC • UNS

8
03/06/2015

Principios SOLID  Single Responsability Principle

 Tips para no violar SRP


 Usar capas
 Permite lograr una primera separación de las responsabilidades de acuerdo al
alcance de cada capa.
 Escribir los comentarios de código para las clases antes de comenzar a
implementarlas.
/// <summary>
/// Gets, saves, and submits orders.
/// </summary>
public class OrderService
{
public Order Get(int orderId) {...}
public Order Save(Order order) {...}
public Order SubmitOrder(Order order) {...}
}

 Usar métodos pequeños


 Un método debería tener un único propósito (razón para cambiar)
 Un método debería ser fácil de leer y escribir.
 Escribir los pasos de un método usando verbos y sustantivos en los nombres de
los métodos
Arquitectura y Diseño de Sistemas • 17 Lic. Ariel Trellini • DCIC • UNS

Principios SOLID  Single Responsability Principle

 Tips para no violar SRP (cont.)


 Evitar transaction scripts generalistas (que realizan muchas operaciones
sobre, quizás, una misma entidad)
 Los transaction scripts deberían tener un verbo en el nombre de la clase.

public class GetOrderService


{
public Order Get(int orderId) { ... }
}

public class SaveOrderService


{
public Order Save(Order order) { ... }
}

public class SubmitOrderService


{
public Order SubmitOrder(Order order) { ... }
}

Arquitectura y Diseño de Sistemas • 18 Lic. Ariel Trellini • DCIC • UNS

9
03/06/2015

Principios SOLID  Single Responsability Principle

¿Por qué es importante SRP?

 Porque buscamos que sea fácil reusar código.


 Cuanto más grande es una clase, más difícil es modificarla.
 Cuánto más grande es una clase, más dura es de leer y entender.

Clases y métodos pequeños nos darán más flexibilidad, sin


tener que escribir demasiado código extra.

Arquitectura y Diseño de Sistemas • 19 Lic. Ariel Trellini • DCIC • UNS

Principios SOLID  Open Closed Principle

Open Closed Principle


Las módulo de software debiera estar abierto para extensión
pero cerrado para modificación

 Debiéramos escribir módulos que


puedan ser extendidos sin
necesidad de ser modificados.
 Los módulos que cumplen OCP
tienen dos atributos primarios:
 Están “Abiertos para Extensión”: El
comportamiento del módulo puede ser
extendido.
 Están “Cerrados para Modificación”: El
código fuente del módulo es inviolable.

 La clave es la ABSTRACCIÓN
Arquitectura y Diseño de Sistemas • 20 Lic. Ariel Trellini • DCIC • UNS

10
03/06/2015

Principios SOLID  Open Closed Principle

 Ejemplo 1: Abstracto

No soporta OCP
Soporta OCP
 Las clases Client y Server son  Se agrega una interfaz intermedia,
clases concretas. ClientInterface, entre Client y Server.
 Si por algún motivo la clase o  Si, por algún motivo, la
implementación del Server es implementación del servidor
modificada, entonces la clase cambia, el cliente probablemente no
Client también debe ser requiera cambios.
modificada.  La clase ClientInterface es cerrada
para modificación aunque si está
abierto para extensión.
Arquitectura y Diseño de Sistemas • 21 Lic. Ariel Trellini • DCIC • UNS

Principios SOLID  Open Closed Principle

 Ejemplo 2: Template Method

 El patrón Template Method (GoF design patterns)


es una alternativa clásica para lograr OCP

Arquitectura y Diseño de Sistemas • 22 Lic. Ariel Trellini • DCIC • UNS

11
03/06/2015

Principios SOLID  Open Closed Principle

 Ejemplo 3: Filtros de Consultas


public class GetUserService
{
public IList<UserSummary> FindUsers(UserSearchType type)
{
IList<User> users;
switch (type)
{
case UserSearchType.AllUsers:
// load the “users” variable here
break;
case UserSearchType.AllActiveUsers:
// load the “users” variable here
break;
case UserSearchType.ActiveUsersThatCanEditQuotes:
// load the “users” variable here
break;
}
return ConvertToUserSummaries(users);
}
}

 Si se quisiera agregar un nuevo filtro, habría que modificar el enumerado y


agregar el caso a la sentencia switch.
Arquitectura y Diseño de Sistemas • 23 Lic. Ariel Trellini • DCIC • UNS

Principios SOLID  Open Closed Principle

 Ejemplo 3: Filtros de Consultas (cont.)


public interface IUserFilter
{
IQueryable<User> FilterUsers(IQueryable<User> allUsers);
}

public class GetUserService


{
public IList<UserSummary> FindUsers(IUserFilter filter)
{
IQueryable<User> users = filter.FilterUsers(GetAllUsers());
return ConvertToUserSummaries(users);
}
}

 Permite agregar cualquier filtro sobre la búsqueda de usuarios, sin que la


consulta se entere.

Arquitectura y Diseño de Sistemas • 24 Lic. Ariel Trellini • DCIC • UNS

12
03/06/2015

Principios SOLID  Open Closed Principle

 Ejemplo 4: OCP por Composición


public class AuthenticationService
{
private ILogger logger = new TextFileLogger();

public ILogger Logger { set{ logger = value; }}

public bool Authenticate(string userName, string password)


{
logger.Debug("Authentication '{0}'", userName);
// try to authenticate the user
}
}
public interface ILogger
{
void Debug(string message, params object[] args);
// other methods omitted for brevity
}
 El servico sólo depende de una abstracción (ILogger), sin interesarle cuál es
su verdadera implementación. AuthenticationService está cerrado para
modificación, pero abierto para extensión.
Arquitectura y Diseño de Sistemas • 25 Lic. Ariel Trellini • DCIC • UNS

Principios SOLID  Open Closed Principle

 Ejemplo 5: OCP por Composición con Expresiones Lambda


var model = new AutoPersistenceModel();
model.WithConvention(convention =>
{
convention.GetTableName = type => "tbl_" + type.Name;
convention.GetPrimaryKeyName = type => type.Name + "Id";
convention.GetVersionColumnName = type => "Version";
}
);

public AutoPersistenceModel WithConvention(Action<Convention>


conventionAction)

 Ejemplo sacado del código de Fluent Nhibernate.


 Permite definir las convenciones de nombres tablas, claves primarias, etc.
 Sin cambiar el código de la clase AutoPersistenceModel , podemos cambiar
el comportamiento del auto-mapping.
 Esta modificación del comportamiento en ejecución es posible puesto que
AutoPersistenceModel depende de abstracciones (en este caso expresiones
lambda), y no sobre implementaciones específicas.
Arquitectura y Diseño de Sistemas • 26 Lic. Ariel Trellini • DCIC • UNS

13
03/06/2015

Principios SOLID  Open Closed Principle

 Ejemplo 6: OCP por Herencia


public class Line
{
public void Draw(ICanvas canvas) { /* draw a line on the canvas */ }
}

public class Painter


{
private IEnumerable<Line> lines;

public void DrawAll()


{
ICanvas canvas = GetCanvas();
foreach (var line in lines)
{
line.Draw(canvas);
}
}
}

¿Qué pasa si se quiere agregar un rectángulo?

Arquitectura y Diseño de Sistemas • 27 Lic. Ariel Trellini • DCIC • UNS

Principios SOLID  Open Closed Principle

¿Por qué es importante OCP?

 Porque la modificación de código existente y funcionando puede


introducir bugs.
 A veces, necesitamos modificar librerías de terceros o nuestras, pero
no podemos/queremos generar una nueva versión de las mismas.

Diseños extensibles son menos propensos a errores ante


cambios de requerimientos.

Arquitectura y Diseño de Sistemas • 28 Lic. Ariel Trellini • DCIC • UNS

14
03/06/2015

Principios SOLID  Liskov Substitution Principle

Liskov Substitution Principle


Subclases deberían poder ser sustituidas por sus clases base.

 Fue acuñado por Barbara Liskov y


está relacionado al concepto
“Diseño por Contratos” de Bertrand
Meyer.

 El contrato de una clase base debe


ser honrado por sus clases
derivadas.

Arquitectura y Diseño de Sistemas • 29 Lic. Ariel Trellini • DCIC • UNS

Principios SOLID  Liskov Substitution Principle

 Ejemplo 1: Ejemplo Canónico


 La herencia generalmente se interpreta como una
relación “es un”
 Un Cuadrado “es un” Rectángulo
 Sin embargo:
 Cuadrado no necesita tener un Alto y un Ancho, sólo
le alcanza con un Lado  Desperdicio de memoria.
 A un Cuadrado se le puede setear el Alto y el
Ancho  Problema de consistencia !!!!
 Solución: Sobreescribir esas propiedades en la clase
Cuadrado
public override double Alto public override double Ancho
{ {
get { return base.Alto; } get { return base.Ancho; }
set set
{ {
base.Alto = value; base.Ancho = value;
base.Ancho = value; base.Alto = value;
} }
} }

Arquitectura y Diseño de Sistemas • 30 Lic. Ariel Trellini • DCIC • UNS

15
03/06/2015

Principios SOLID  Liskov Substitution Principle

 Ejemplo 1: Ejemplo Canónico (cont.)


¿Qué problema encuentran public void Calcular(Rectangle r)
{
con la siguiente función r.Alto = 5;
cuando se le pasa una r.Ancho = 4;
Debug.Assert(r.Alto * r.Ancho == 20);
instancia de Cuadrado? }

 ¿Podría un desarrollador asumir que cuando se cambia el valor al Alto de


un rectángulo realmente no se cambia?
 Se ha violado el contrato de Rectangulo  Violación de LSP !!!!
 Cuidado: La validez de un modelo no es intrínseca
 Un modelo, visto de manera aislada, no puede ser validado significativamente.
 La validez del modelo sólo puede ser expresada en función de sus clientes.
 ¿Qué falló?
 Un cuadrado puede ser un rectángulo, pero un objeto Cuadrado NO ES un
objeto Rectángulo, ya que el comportamiento del objeto Cuadrado no es
consistente con el comportamiento del objeto Rectángulo.

Arquitectura y Diseño de Sistemas • 31 Lic. Ariel Trellini • DCIC • UNS

Principios SOLID  Liskov Substitution Principle

 Diseño por Contrato

 Definición:
 Utilizando este concepto, los métodos definirían pre-condiciones y post-
condiciones.
 Cuando se redefine un método [en una clase derivada], sólo se pueden
reemplazar su pre-condición por una más débil y su post-condición por una
más fuerte.

 Si el lenguaje no permite definir las aserciones, mínimamente debieran


quedar documentadas en el código.

Arquitectura y Diseño de Sistemas • 32 Lic. Ariel Trellini • DCIC • UNS

16
03/06/2015

Principios SOLID  Liskov Substitution Principle

 Ejemplo 2: Ejemplo más Real

 Modelo

 Cliente
void SaveAll(List<IPersistedResource> res)
{
res.ForEach(r => r.Persist());
}

List<IPersistedResource> LoadAll()
{
var all = new List<IPersistedResource>
{
new UserSettings(),
new ApplicationSettings()
};
all.ForEach(r => r.Load());
return all;
}

Arquitectura y Diseño de Sistemas • 33 Lic. Ariel Trellini • DCIC • UNS

Principios SOLID  Liskov Substitution Principle

 Ejemplo 2: Ejemplo más Real (cont.)


 ¿Qué pasa si quiero agregar la clase ReadOnlySettings al modelo?
public class ReadOnlySettings : IPersistedResource
{
public void Load()
{
// stuff...
}
public void Persist()
{
throw new NotImplementedException();
}
}

 Debemos cambiar el cliente: List<IPersistedResource> LoadAll()


{
var all = new List<IPersistedResource>
{
new UserSettings(),
new ApplicationSettings(),
new ReadOnlySettings ()
};
all.ForEach(r => r.Load());
return all;
}

Arquitectura y Diseño de Sistemas • 34 Lic. Ariel Trellini • DCIC • UNS

17
03/06/2015

Principios SOLID  Liskov Substitution Principle

 Ejemplo 2: Ejemplo más Real (cont.)

 ¿Qué sucede con el método SaveAll del cliente?


 EXPLOTA con una NotImplementedException !

 Solución:
 Manejar el caso diferente en el cliente (método SaveAll):
void SaveAll(List<IPersistedResource> res)
{
res.ForEach(r =>
{
if (r is ReadOnlySettings)
return;
r.Persist();
});
}

 No es una manera muy acertada de sobrellevar el problema.

Arquitectura y Diseño de Sistemas • 35 Lic. Ariel Trellini • DCIC • UNS

Principios SOLID  Liskov Substitution Principle

 Ejemplo 2: Ejemplo más Real (cont.)


 Mejor Solución:
 Separar la interfaz IPersistedResource en IPersistResource e ILoadResource y
asignarle a cada cliente la interfaz más apropiada.

List<ILoadResource> LoadAll() void SaveAll(List<IPersistResource> res)


{ {
var all = new List<ILoadResource> res.ForEach(r => r.Persist());
{ }
new UserSettings(),
new ApplicationSettings(),
new ReadOnlySettings ()
};
all.ForEach(r => r.Load());
return all;
}
Arquitectura y Diseño de Sistemas • 36 Lic. Ariel Trellini • DCIC • UNS

18
03/06/2015

Principios SOLID  Open Closed Principle

¿Por qué es importante LSP?

 Es una importante característica de todos los programas que cumplen


con OCP.
 Permite definir herencias consistentes.

Arquitectura y Diseño de Sistemas • 37 Lic. Ariel Trellini • DCIC • UNS

Principios SOLID  Interface Segregation Principle

Interface Segregation Principle


Varias interfaces específicas a los clientes son mejores que una
interfaz de propósito general.

 Ataca las desventajas de interfaces


gordas (no cohesivas /
contaminadas).
 La interfaz gorda es dividida en
grupos de métodos cohesivos. Cada
grupo sirve a un tipo de cliente
específico.
 Clientes no deben ser forzados a
depender de interfaces que no
usan.

Arquitectura y Diseño de Sistemas • 38 Lic. Ariel Trellini • DCIC • UNS

19
03/06/2015

Principios SOLID  Interface Segregation Principle

 Polución de las interfaces

 Escenario
 Tenemos las abstracciones Door y Timer.
 Queremos agregar una implementación
específica de TimedDoor
 ¿Cómo implemento la relación entre
TimerClient y TimedDoor?

Arquitectura y Diseño de Sistemas • 39 Lic. Ariel Trellini • DCIC • UNS

Principios SOLID  Interface Segregation Principle

 Polución de las interfaces (cont)

 Solución 1 : Herencia
 No todas las variedades de puerta requieren
timing. La abstracción Door no tiene nada que
ver con timings.
 Todas las sub-clases de Door tendrán que
implementar el método Timeout.
 La interfaz Door ha sido contaminada por la
interfaz TimerClient porque una de sus sub-
clases lo requiere.

Arquitectura y Diseño de Sistemas • 40 Lic. Ariel Trellini • DCIC • UNS

20
03/06/2015

Principios SOLID  Interface Segregation Principle

 Polución de las interfaces (cont.)


 Solución 1: Herencia (cont.)
 ¿Qué sucede si una puerta necesita
registrarse más de una vez en el timer?
 Agrego un timeoutID a cada registración para
saber de dónde se produjo un timeout.
 Implica modificar todas las implementaciones
de TimerClient… incluso aquellas puertas que
no tiene nada que ver con timings.
 Aun más, se van a ver afectados también todos
los clientes de Door !!!!

Cuando un cambio en un componente afecta a otros


componentes que no están relacionados, el costo de
repercusión de cambios se torna impredecible, y el riesgo de
romper algo se incrementa dramáticamente.

Arquitectura y Diseño de Sistemas • 41 Lic. Ariel Trellini • DCIC • UNS

Principios SOLID  Interface Segregation Principle

¿De qué manera se podría resolver el problema anterior?

 Consideraciones
 TimedDoor tiene dos interfaces utilizadas por dos tipos de cliente distintos:
Timer y los clientes de Door.
 Los clientes de TimedDoor no necesitan acceder a las dos interfaces a la vez,
sólo necesitan conocer una o la otra.
 Esto nos llevaría a separar las interfaces y que cada tipo de cliente consuma
la interfaz que necesita.

Arquitectura y Diseño de Sistemas • 42 Lic. Ariel Trellini • DCIC • UNS

21
03/06/2015

Diseño de Software  Interface Segregation Principle

 Implementaciones de ISP
 Separación a través de delegación
 Aplica el patrón Adapter de GoF
 Evita el acoplamiento de Door y Timer.
 Si ocurriera un cambio en TimerClient, ningún cliente de Door se vería afectado.
 Más aun, TimedDoor no tiene que tener la misma interfaz propuesta por
TimerClient, ya que el adapter podría transformarla.

Arquitectura y Diseño de Sistemas • 43 Lic. Ariel Trellini • DCIC • UNS

Diseño de Software  Interface Segregation Principle

 Implementaciones de ISP
 Separación a través de Herencia Múltiple
 Implementando herencia múltiple con interfaces
 Desacopla las interfaces, aunque se siguen implementando ambas en el mismo
objeto.
 Es más elegante que la anterior (y más utilizada)

Arquitectura y Diseño de Sistemas • 44 Lic. Ariel Trellini • DCIC • UNS

22
03/06/2015

Principios SOLID  Dependency Inversion Principle

Dependency Inversion Principle


Módulos de alto nivel no debieran depender de módulos de bajo
nivel. Ambos debieran depender de abstracciones.
Abstracciones no debieran depender de los detalles, sino los
detalles debieran depender de las abstracciones.
 Objetivo
 Atacar el alto acoplamiento
 Cada dependencia en el diseño debería
apuntar a una abstracción.
 Razón
 Las clases concretas tienen mayor
probabilidad de cambiar que las
abstracciones.
 Disclaimer
 Esto no debe aplicarse a rajatabla sobre todas las clases porque elevaría la
complejidad y disminuiría la legibilidad, entre otras cosas.
Arquitectura y Diseño de Sistemas • 45 Lic. Ariel Trellini • DCIC • UNS

Diseño de Software  Dependency Inversion Principle

 Ejemplo 1: Canónico
 Copiar caracteres ingresados por el
teclado en la impresora.
Copy
 Gráfico  Manera procedural
 ¿Qué pasa si se quisiera copiar los datos
a un archivo de disco?
Read Write
 Copy no es reusable en contextos que no Keyboard Printer
contemplen un teclado y una impresora
porque depende del teclado y de la
impresora.
 Podríamos agregar una sentencia “if” en
el cuerpo del ciclo
 Poco escalable. Si se agregan más
dispositivos el “if” se hace enorme.

Arquitectura y Diseño de Sistemas • 46 Lic. Ariel Trellini • DCIC • UNS

23
03/06/2015

Diseño de Software  Dependency Inversion Principle

 Ejemplo 1: Canónico (cont.)


¿De qué manera se podría resolver el
problema anterior?

 El módulo que contiene la política de


alto nivel (Copy) no dependa de los
detalles de implementación.
 Las dependencias se invierten:
 Copy depende de abstracciones.
 Los detalles dependen de las mismas
abstracciones
 Copy puede ser reusado en otros
contextos y con otras implementaciones
de Reader y Writer

Arquitectura y Diseño de Sistemas • 47 Lic. Ariel Trellini • DCIC • UNS

Diseño de Software  Dependency Inversion Principle

 Ejemplo 2: Algo más real


 El servicio de domino depende
directamente de la implementación del
repositorio.
 Es imposible testear unitariamente al
servicio de dominio sin involucrar al
repositorio
 Cualquier cambio en el repositorio puede
afectar directamente al servicio de
dominio.
 Esto viola el DIP
public class GetProductService
{
public IList<Product> GetProductById(int id)
{
var productRepository = new ProductRepository();
return productRepository.Get(id);
}
}

Arquitectura y Diseño de Sistemas • 48 Lic. Ariel Trellini • DCIC • UNS

24
03/06/2015

Diseño de Software  Dependency Inversion Principle

 Ejemplo 2: Algo más real (cont.)


 Solución: Usar interfaces para
definir abstracciones
 El servicio de dominio depende de una abstracción: la
interfaz del repositorio.
 La implemetación concreta del repositorio debe respetar
esa interfaz.
 La dependencia se le inyecta al servicio de dominio.
public class GetProductService : IGetProductService
{
private IProductRepository productRepository;
public GetProductService(
IProductRepository productRepository)
{
this.productRepository = productRepository;
}
public IList<Product> GetProductById(int id)
{
return this.productRepository.Get(id);
}
}

Arquitectura y Diseño de Sistemas • 49 Lic. Ariel Trellini • DCIC • UNS

Diseño de Software  Dependency Inversion Principle

 Ejemplo 3: Layering
 Implementación naive

 Implementación con DIP

Arquitectura y Diseño de Sistemas • 50 Lic. Ariel Trellini • DCIC • UNS

25
03/06/2015

Diseño de Software  Dependency Inversion Principle

 Ejemplo 4: Lámpara
 Problema
 Cuando se le envía el mensaje Poll al botón, determina si el usuario lo ha
presionado y enciende/apaga la lámpara.
 Implementación naive public class Button
{
private Lamp lamp;
public void Poll()
{
if (/*some condition*/)
lamp.TurnOn();
}
}

 Implementación con DIP


 Podríamos cambiar ButtonServer por
SwitchableDevice

Arquitectura y Diseño de Sistemas • 51 Lic. Ariel Trellini • DCIC • UNS

26

Anda mungkin juga menyukai