Está en la página 1de 27

http://jms32.eresmas.

net/web2008/documentos/informatica/documentacion/
logica/OOP/Principios/Oop_Solid_LSP/2012_09_14_SOLID_ListkowSubstituti
onPrincipe.html
Principio de sustitucin de Liskov


Los cinco principios SOLID
Principio de Responsabilidad nica (SRP Single
Responsibility Principle)
Principio Abierto-Cerrado (OCP Open Closed Principle)
Principio de Sustitucin de Liskov (LSP - Liskov
Substitution Principle)
Principio de Segregacin de Interfaces (ISP - Interface
Segregation Principle)
Principio de Inversin de Dependencia (DIP Dependency
Inversion Principle)


SOLID LSP Liskov Substitution Principe
El Principio de Sustitucin de Liskov fue acuado por
Brbara Liskov (de ah el nombre del principio) en el ao
1987 durante una conferencia sobre Jerarqua y Abstraccin
de datos. Su principal cometido es la de asegurar en la
herencia entre clases de la Programacin Orientada a Objetos
que una clase derivada no nicamente es sino que debe
comportarse como la clase base. Su definicin es:
"Debe ser posible utilizar cualquier objeto instancia de una
subclase en lugar de cualquier objeto instancia de su
superclase sin que la semntica del programa escrito en los
trminos de la superclase se vea afectado"
Hay muchas ms definiciones de este principio
Cada clase que hereda de otra puede usarse como su
padre sin necesidad de conocer las diferencias entre
ellas
Toda subclase debe soportar ser sustituida por su clase
base lo que en trminos de contratos significa que las
precondiciones de los mtodos de la subclase no sern
ms fuertes que las de la clase base, y sus pos
condiciones no sern ms dbiles (en el lenguaje de la
calle: los mtodos derivados no deben esperar ms ni
proveer menos que los originales).
Las funciones que usan punteros o referencias a clases
base deben ser capaces de utilizar objetos de las clases
derivadas sin tener conocimiento de ello" (Martin96b),
tambin: "Las clases derivadas deben ser utilizables a
travs de la interfaz de la clase base, sin necesidad de
que el usuario conozca la diferencia
Si por cada objeto O1 del tipo S existe un objeto O2 del
tipo T tal que para todos los programas P definidos en
trminos de T y el comportamiento de P permanece
invariable cuando O1 es sustituido por O2, entonces S
es un subtipo de T.
Idea importante
En lenguajes OO como C# o VB.NET, la clave para conseguir
la abstraccin y polimorfismo de entidades es mediante
la herencia, y es precisamente en esta caracterstica en la que
se basa el Principio de Sustitucin de Liskov (Liskov
Substitution Principle, LSP). El principio indica cules son los
fundamentos bsicos de diseo que debe seguir la herencia
en un caso particular, o cul es la mejor forma de crear
jerarquas de herencia entre clases. Porque el hecho de que
una clase herede de otra, no asegura que se cumple el
principio LSP, si no se respetan tambin sus "contratos de
diseo"


Ejemplo que NO cumple con el principio
Echemos un vistazo al cdigo siguiente. Este cdigo trata de
calcular los impuestos de un vehculo en base a la matricula
(antigedad) y la cilindrada del mismo.

Listado 1

class Vehiculo
{
public string Marca { get; set; }
public string Modelo { get; set; }
public int Cilindrada { get; set; }
}

class Ciclomotor : Vehiculo
{
public string ObtenerNumLicencia()
{
// Devuelve nmero de licencia
}
}

class Coche : Vehiculo
{
public string ObtenerMatricula()
{
// Devuelve matrcula
}
}

class Impuestos
{
public void CalcularImpuesto(Vehiculo vehiculo)
{
string matricula = ((Coche)vehiculo).ObtenerMatricula();
ServicioCalculoImpuestos(matricula, vehiculo.Cilindrada);
}
}

Figura 1
Bsicamente, LSP afirma que si tenemos dos objetos de tipos
diferentes Coche y Ciclomotor que derivan de una misma
clase base Vehculo, deberamos poder reemplazar cada
uno de los tipos Coche/Ciclomotor y viceversa all dnde el
tipo base Vehculo est implementado. En el ejemplo
anterior tenemos un claro caso de violacin del LSP, ya que la
ejecucin del mtodo CalcularImpuesto generar una
excepcin de conversin de tipo si el objeto pasado por
parmetro es de tipo Ciclomotor en lugar de Coche, pese a
que ambas clases derivan de la misma clase base Vehculo.
Podramos pensar en solucionar el problema modificando el
cdigo tal y como se muestra a continuacin:

Listado 2

public void CalcularImpuesto(Vehiculo vehiculo)
{
string matricula = string.Empty;
if (vehiculo.GetType().Name == "Coche")
matricula = ((Coche)vehiculo).ObtenerMatricula();
else if (vehiculo.GetType().Name == "Ciclomotor")
matricula = ((Ciclomotor)vehiculo).ObtenerNumLicencia();
ServicioCalculoImpuestos(matricula, vehiculo.Cilindrada);
}
Pese a que el compilador no genere ninguna excepcin de
conversin de tipo, esta clase an viola el LSP. Esto es debido
a que estamos forzando a un objeto Vehculo pasado como
parmetro a comportarse como Ciclomotor o Coche. Adems,
esta aproximacin vulnera el Principio Open/Closed, ya que
ante cualquier nueva entidad que derive de Vehiculo
deberemos modificar el mtodo CalcularImpuesto.


Ejemplo que SI cumple el principio
Vamos a ver en este ejemplo el diseo de una calculadora
siguiendo el principio LSP:
LSP es bsicamente una extensin del principio abierto-
cerrado. Lo que nos dice es que si tenemos una clase y varias
subclases de esta al usar una referencia a la clase principal
esta debe ser capaz de aceptar cualquier objeto de sus clases
hijas.

Figura 2

Listado 2

// Tenemos una clase base
public abstract class Operacion
{
public double OperadorUno { get; set; }
public double OperadorDos { get; set; }

public abstract double Calcular();
}


// Una clase derivada de Operacion
public class OperacionSumar : Operacion
{
public override double Calcular()
{
double result = this.OperadorUno + this.OperadorDos;
return result;
}
}


//Otra clase derivada de Operacin
public class OperacionMultiplicar : Operacion
{
public override double Calcular()
{
double result = this.OperadorUno * this.OperadorDos;
return result;
}
}


public class Calculadora
{
//Aqu pasamos una referencia de la clase base
public void Calcular(Operacion op)
{
//Esa referencia la usamos aqu que es op
double result = op.Calcular();
Console.WriteLine("Resultado=" + result);
}
}


public class Main
{
public static void main(String[] args)
{
Calculadora calc = new Calculadora();

OperacionSumar suma = new OperacionSumar();
suma.OperadorUno=10;
suma.OperadorDos=4;
//El metodo calcular acepta cualquier objeto
//de una clase derivada de Operacin
calc.Calcular(suma);

OperacionMultiplicar multiplicacion = new OperacionMultiplicar();
multiplicacion.OperadorUno=6;
multiplicacion.OperadorDos=5;
//El mtodo calcular acepta cualquier objeto
//de una clase derivada de Operacin
calc.Calcular(multiplicacion);
}
}

Aqu vemos la aplicacin de este principio en el mtodo
calcular. Este mtodo usa una referencia a la clase base y
nunca se entera cual es el objeto derivado (subtipo) que se
est usando.


Un apunte sobre el diseo de objetos
basado en contratos
Existe una fuerte relacin entre el LSP y el concepto de diseo
por contrato, usando este ltimo los mtodos de una clase
declaran pre-condiciones y post-condiciones. Las primeras
deben ser verdaderas para que el mtodo se ejecute. Una vez
terminado, el mtodo garantiza que las post-condiciones sean
ciertas. Usando LSP, la pre-condicin se traduce a que
cualquier clase derivada debe aceptar cualquier cosa que la
clase base puede aceptar, mientras la post-condicin se
traduce a que las clases derivadas deben ajustarse a todas la
post-condiciones de la clase base. Es decir, sus
comportamientos y los resultados no deben infringir ninguna
de las limitaciones establecidas para la clase base. Los
usuarios de la clase base no debe confundirse ante una nueva
clase derivada.
Por tanto, se deben tener dos criterios para usar clases bases,
uno es dar a una aplicacin un comportamiento comn, y el
otro usar mtodos de ayuda para las clases derivadas. Pero
sobre todo lo primero. Por ejemplo, puedo crear un formulario
base porque quiero que todos los formularios tengan el
mismo comportamiento frente a un evento concreto, pero
tambin lo puedo hacer para tener funciones de ayuda.
Siempre es preferible el primer criterio porque el segundo
criterio lo puedo aislar luego en una clase de ayuda de simple
instancia y llevrmelo a otro proyecto.


Un Ejemplo que NO cumple el principio LSP
Vamos a ver el tpico ejemplo de libro de texto: el caso del
cuadrado y el rectngulo, en el que mostraremos que el que
una clase herede de otra, no asegura que se cumple el
principio LSP, si no se respetan tambin sus "contratos de
diseo"
Supongamos que tenemos una clase Rectngulo como la
siguiente:

Listado 3

public class Rectangulo
{
//-----------------------------------------------------
//La palabra clave virtual se utiliza para modificar un
//mtodo, de forma que pueda ser invalidado (sobre escrito)
//en una clase derivada.
//-----------------------------------------------------
public virtual int Ancho { get; set; }
public virtual int Alto { get; set; }
public virtual float Area()
{
return Ancho * Alto;
}

}
Luego decidimos que un Cuadrado "es-un" rectngulo y por
eso creamos la clase Cuadrado como una subclase de
Rectngulo, y para asegurarnos que la forma realmente sea
la de un cuadrado, las propiedades Alto y Ancho, las sobre
escribimos de forma que nos aseguremos de que, tal y como
corresponde a un cuadrado, el valor del ancho y del alto sean
iguales

Listado 4

public class Cuadrado : Rectangulo
{
public override int Ancho
{
get
{
return base.Ancho;
}
set
{
base.Ancho = value;
base.Alto = value;
}
}

public override int Alto
{
get
{
return base.Alto;
}
set
{
base.Ancho = value;
base.Alto = value;
}
}

public override float Area()
{
return Ancho * Alto;
}
}


Figura 3
Hasta aqu todo parece correcto, pero veamos qu pasa
cuando tenemos una tercera clase, en este caso un test
unitario, con el siguiente cdigo:

Listado 5

public class Test
{

public void TestCalculoAreaRectangulo()
{
testAreaFiguraRectangular( new Rectangulo());
}

public void TestCalculoAreaCuadrado()
{
testAreaFiguraRectangular(new Cuadrado());
}

private void testAreaFiguraRectangular(Rectangulo rect)
{
rect.Alto=10;
rect.Ancho=2;
Assert.AreEqual(20, rect.Area());
}
}
Cuando la variable rectngulo sea una instancia de la clase
Cuadrado este test va a fallar (el rea del cuadrado va a ser
2*2=4 en lugar de 2*10=20).
Esto ocurre porque se ha violado el principio LSP. Nuestro test
referencia a la clase base Rectngulo y al utilizar una
instancia de Cuadrado (clase derivada de Rectngulo) el test
comienza a fallar.
Sub-tipos vs Sub-clases y Diseo por Contrato
Una vez planteado el ejemplo viene bien mencionar un par de
conceptos que ayudan a entender un poco mas cundo se
puede estar violando LSP y cundo no.
Barbara Liskov defini como sub-tipo a la propiedad que
tiene un objeto A de poder reemplazar a otro objeto B sin que
el comportamiento de los objetos que los utilizan deba ser
modificado (A es un sub-tipo de B)
En nuestro ejemplo el Cuadrado no puede reemplazar al
Rectngulo ya que el comportamiento de nuestro test debera
ser modificado para que siga funcionando correctamente,
probablemente haciendo algo as:

Listado 6

private void testAreaFiguraRectangular(Rectangulo rect)
{
rect.Alto = 10;
rect.Ancho = 2;
if (rect is Cuadrado)
Assert.AreEqual(4, rect.Area());
else
Assert.AreEqual(20, rect.Area());
}
De aqu se deduce que el Cuadrado puede ser una sub-
clase del Rectngulo, pero no es un sub-tipo, segn la
definicin de Liskov. Por lo tanto, que una clase herede
de otra no nos asegura el principio LSP.
Otro concepto que vale la pena mencionar es el de diseo
por contrato, que se puede resumir en que los mtodos de
una clase declaran pre-condiciones y post-condiciones. Las
pre-condiciones se deben cumplir para que el mtodo se
ejecute y, cuando finalice la ejecucin del mtodo, tambin
deben de cumplirse las post-condiciones.
Para no violar LSP, cuando se redefinen los mtodos de las
cases derivadas, se deben respetar tanto las pre-condiciones
como las post-condiciones definidas en la clase base, es decir,
el comportamiento de una clase no debe romper ninguna de
las restricciones impuestas por la clase base.
En el ejemplo del rectngulo y el cuadrado, las post-
condiciones del mtodo Ancho definidas en la clase base
Rectngulo son las siguientes:
"El ancho tiene el nuevo valor asignado"
"El alto se mantiene con el mismo valor que tena antes"
La segunda post-condicin no se respeta en la redefinicin del
mtodo Ancho de la clase Cuadrado, ya que ante un cambio
en el ancho, tambin se modifica el alto en forma automtica.
Ya puestos vamos a ver una implementacin diferente de la
propiedad Ancho de la clase cuadrado que tampoco respeta
las pre-condiciones de la clase base rectngulo, sera por
ejemplo algo as:

Listado 7

public override int Ancho
{
get
{
return base.Ancho;
}
set
{
if (value == 0)
throw new Exception("El ancho debe ser distinto de 0");

base.Ancho = value;
base.Alto = value;
}
}
En este caso, estamos pidiendo que el ancho sea distinto de
cero como pre-condicin de la propiedad Ancho en la clase
Cuadrado, mientras que esa restriccin no se exige en la
clase Rectngulo.


LSP y las interfaces
Por ltimo vale la pena mencionar qu pasa con las
interfaces. Si en lugar de heredar de una clase base con
comportamiento ya definido, se implementa una interfaz,
hay pre-condiciones y post-condiciones que respetar? La
respuesta es: en la mayora de los casos si las hay, y por lo
general son implcitas y se desprenden del sentido comn.


Otro Ejemplo que NO cumple el principio LSP
Supongamos que tenemos una interfaz para nuestros objetos
DAO como la siguiente:
Nota: Un Data Access Object (DAO, Objeto de Acceso a
Datos) es un componente de software que suministra
una interfaz comn entre la aplicacin y uno o ms
dispositivos de almacenamiento de datos, tales como
una Base de datos o un archivo.

Listado 7

public interface IDao
{
void Insert(object entity);
void Update(object id, object entity);
void Delete(object id);
object[] GetAll();
object GetById(object id);
}
Luego decido usar la interface para mis Facturas

Listado 7

public class FacturaDao : IDao
{
public void Insert(object entity)
{
//se inserta una factura
}
public void Update(object id, object entity)
{
//se actualiza una factura
}
public void Delete(object id)
{
//se elimina una factura
}
public object[] GetAll()
{
//se obtienen todas las facturas
}
public object GetById(object id)
{
//se obtiene una factura por id }
}
}
En este momento y por algn motivo inexplicable necesito
crear una capa de acceso a datos que slo permita lecturas
de facturas. La implementacin de la clase debera ser como
la siguiente:

Listado 7

public class FacturaDaoReadOnly : IDao
{
public void Insert(object entity)
{
throw new DaoReadOnlyException();
}
public void Update(object id, object entity)
{
throw new DaoReadOnlyException();
}
public void Delete(object id)
{
throw new DaoReadOnlyException();
}
public object[] GetAll()
{
//se obtienen todas las facturas
}
public object GetById(object id)
{
//se obtiene una factura por id
}
}

Figura 4
Aqu el problema que se presenta es que las clases que
interacten con IDao, van a tener que tener en cuenta que en
algunos casos los mtodos Insert, Update y Delete pueden
lanzar una excepcin DaoReadOnlyException, lo cual viola el
principio de Liskov por los siguientes motivos:
No se estaran cumpliendo las post-condiciones de los
mtodos Insert, Update y Delete porque se debe
contemplar que el Dao puede lanzar una excepcin
DaoReadOnlyException, lo cual es una post-condicin
particular de una implementacin concreta del Dao, en
este caso el dao readonly de Facturas.
No se estara cumpliendo el concepto de diseo por
contrato, ya que en ninguna parte de la interfaz IDao se
sugiere que un Dao puede ser readonly.
Una posible forma de solucionar esto sera mediante la
separacin en distintas interfaces de las distintas
acciones del Dao, por ejemplo IDaoLectura,
IDaoEscritura.


Resumiendo
Podemos decir que un sistema que use este principio ser
ms fcil de mantener que otro que no lo use. Este principio
est relacionado con otros como cohesin o acoplamiento
entre clases (No hables con extraos) y con los patrones de
diseo de tipo factoras (cmo se si debo desde el cliente
usar la clase A o la B?)

http://jms32.eresmas.net/web2008/documentos/informatica/documentacion/logica/OOP/Pri
ncipios/Oop_Solid_SRP/2012_09_04_SOLID_SRP_PrincipioResponsabilidadUnica.htm

Principio de responsabilidad nica
El Principio de responsabilidad nica (Single Responsability
Principle - SRP) fue acuado por Robert C. Martin en un
artculo del mismo ttulo y popularizado a travs de su
conocido libro [patrones Gof]. SRP tiene que ver con el nivel
de acoplamiento entre mdulos dentro de la ingeniera del
software.
Este principio nos viene a decir que una clase slo debera
tener una nica razn para cambiar.
"Una clase debe tener una nica razn para cambiar."
En trminos prcticos, este principio establece que:
"Una clase debe tener una y solo una nica causa
por la cual puede ser modificada."
"Cada clase debe ser responsable de realizar una
actividad del sistema
Lo que trata de decirnos este principio es que debemos huir
de aquellas clases monolticas que aglutinen varias
responsabilidades. Pero, qu es una responsabilidad? Desde
el punto de vista de SRP se podra decir que una
responsabilidad en una clase es una razn para cambiar esa
clase. Es decir, si encontramos que hay ms de una razn por
la que una clase pueda cambiar entonces es que esa clase
tiene ms de una responsabilidad.
Y no sera ms sencillo decir que una clase debera tener una
sola razn para existir en lugar de para cambiar? Cuidado,
porque esto nos podra llevar a hacer muy malos diseos de
sistemas. Llevado al pie de la letra podra encontrarme con
cientos de clases en mi sistema, cada una con una nica
funcin. Lo que hara al sistema nada fcil de mantener.
El punto clave que nos dice las razones por la que una clase
puede cambiar va a depender del contexto en el que se va a
dar uso a esa clase. Pongamos por ejemplo una clase que
represente al motor de un coche. Necesitamos conocer el
rgimen de revoluciones del motor?, el peso?, nmero de
cilindros?, presin del inyector de gasolina?, o lo que nos
interesa es simplemente poder arrancarlo y esperar que haga
andar a un coche para llevaros de un sitio a otro? La
respuesta a estas preguntas va a depender del contexto en el
cual usemos la clase motor. No va a tener las mismas
necesidades sobre esta clase un fabricante de coches que un
usuario que usa el coche para ir de un sitio a otro. El
fabricante de coches va a notar un nmero mayor de
responsabilidades en el motor que el usuario del coche. Por
tanto, para el fabricante, este principio recomendara dividir
la clase motor en otras ms pequeas que cumplan con las
especificaciones de manera individual.


Veamos un ejemplo tpico de violacin del
SRP:
Si una clase tiene dos responsabilidades, entonces asume dos
motivos por los cuales puede ser modificada. Por ejemplo,
supongamos una clase llamada Factura, la cual dentro de un
contexto determinado ofrece un mtodo para calcular el
importe total, tal y como muestra la siguiente figura.

Figura 01


Detectando responsabilidades
La piedra angular de este principio es la identificacin de la
responsabilidad real de la clase. Segn SRP, una
responsabilidad es "un motivo de cambio"; algo que en
ocasiones es difcil de ver, ya que estamos acostumbrados a
pensar un conjunto de operaciones como una sola
responsabilidad.

Listado 01

class Factura
{
public string Codigo { get; set; }
public DateTime FechaEmision { get; set; }
public decimal ImporteFactura { get; set; }
public decimal ImporteIVA { get; set; }
public decimal ImporteDeduccion { get; set; }
public decimal ImporteTotal { get; set; }
public decimal PorcentajeDeduccion { get; set; }


// Mtodo que calcula el total de la factura
public void CalcularTotal()
{
// Calculamos la deduccin
ImporteDeduccion = (ImporteFactura * PorcentajeDeduccion) / 100;
// Calculamos el IVA
ImporteIVA = ImporteFactura * 0.16m;
// Calculamos el total
ImporteTotal = (ImporteFactura - ImporteDeduccion) + ImporteIVA;
}
}
Si implementamos la clase Factura tal y como se muestra en
el listado 1, podramos decir que la responsabilidad de esta
clase es la de calcular el total de la factura y que,
efectivamente, la clase cumple con su cometido. Sin
embargo, no es cierto que la clase contenga una nica
responsabilidad. Si nos fijamos detenidamente en la
implementacin del mtodo CalcularTotal, podremos ver que,
adems de calcular el importe base de la factura, se est
aplicando sobre el importe a facturar un descuento o
deduccin y un 16% de IVA. El problema est en que si en el
futuro tuviramos que modificar la tasa de IVA, o bien
tuviramos que aplicar una deduccin en base a una tarifa
por cliente, tendramos que modificar la clase Factura por
cada una de dichas razones; por lo tanto, con el diseo actual
las responsabilidades quedan acopladas entre s, y la clase
violara el principio SRP.


Separando responsabilidades
El primer paso para solucionar este problema es separar las
responsabilidades; para separarlas, primero hay que
identificarlas. Enumeremos de nuevo los pasos que realiza el
mtodo CalcularTotal:
Aplica una deduccin. En base a la base imponible se
calcula un descuento porcentual.
Aplica la tasa de IVA del 16% en base a la base
imponible.
Calcula el total de la factura, teniendo en cuenta el
descuento y el impuesto.
En este mtodo se identifican tres responsabilidades.
Recuerde que una responsabilidad no es una accin, sino un
motivo de cambio, y por lo tanto se deberan extraer las
responsabilidades de deduccin e impuestos en dos clases
especficas para ambas operaciones; estableciendo por un
lado la clase IVA y por otro la clase Deduccion, tal y como se
presenta en el listado 2.

Listado 2

class IvaNormal
{
private const decimal PORCENTAJE_IVA_NORMAL = 0.16m;
public readonly decimal PorcentajeIvaNormal
{
get
{
return PORCENTAJE_IVA_NORMAL;
}
}

public decimal CalcularIVA(decimal importe)
{
return importe * PORCENTAJE_IVA_NORMAL;
}
}


class Deduccion
{
private decimal m_PorcentajeDeduccion;

public Deduccion(decimal porcentaje)
{
m_PorcentajeDeduccion = porcentaje;
}

public decimal CalcularDeduccion(decimal importe)
{
return (importe * m_PorcentajeDeduccion) / 100;
}
}
Ambas clases contienen datos y un mtodo y se
responsabilizan nicamente en calcular el IVA y la deduccin,
respectivamente, de un importe. Adems, con esta
separacin logramos una mayor cohesin y un menor
acoplamiento, al aumentar la granularidad de la solucin. La
correcta aplicacin del SRP simplifica el cdigo y se traduce
en facilidad de mantenimiento, mayores posibilidades de
reutilizacin de cdigo y de crear unidades de testeo
especficas orientadas a cada clase/responsabilidad. El listado
3 muestra la nueva versin de la clase Factura, que hace uso
de las dos nuevas clases IVA y Deduccin.

Listado 3

class FacturaFactorizada
{
public string Codigo { get; set; }
public DateTime FechaEmision { get; set; }
public decimal ImporteFactura { get; set; }
public decimal ImporteIVA { get; set; }
public decimal ImporteDeduccion { get; set; }
public decimal ImporteTotal { get; set; }
public decimal PorcentajeDeduccion { get; set; }

// Mtodo que calcula el total de la factura
public void CalcularTotal()
{
// Calculamos la deduccin
Deduccion deduccion = new Deduccion(PorcentajeDeduccion);
ImporteDeduccion = deduccion.CalcularDeduccion(ImporteFactura);
// Calculamos el IVA
IvaNormal iva = new IvaNormal();
ImporteIVA = iva.CalcularIVA(ImporteFactura);
// Calculamos el total
ImporteTotal = (ImporteFactura - ImporteDeduccion) + ImporteIVA;
}
}
Nota: La correcta aplicacin del SRP simplifica el cdigo y se
traduce en facilidad de mantenimiento, mayores posibilidades
de reutilizacin de cdigo y de crear unidades de testeo
especficas para cada responsabilidad


Otro Ejemplo
Veamos otro ejemplo tpico de violacin del SRP:
Supongamos que tenemos la clase Employee [Empleado] en
un sistema de gestin de una empresa cualquiera. Esta clase
nos permite realizar las tareas esperadas sobre un empleado:
Cargarlo y almacenarlo en una base de datos, generar la
nmina, informacin bsica del empleado, etc.

Figura 2
Ahora supongamos dos aplicaciones que hacen uso de la clase
Employee. Una para ser usada por el departamento de
recursos humanos [RRHH_System] para la gestin de las
nominas del personal y otra para la gestin de los proyectos
que lleva la empresa.
Podemos pensar que no se est siguiendo el SRP? Lo que
sera lo mismo, creemos que la clase Employee tiene ms de
una razn por la que pueda cambiar? A m se me ocurren
unas cuantas: Cambiar el formato de almacenamiento de
base de datos, modificar los campos que definen a un
empleado, cambiar la lgica de generacin de nminas, etc.
Es decir, esta clase tiene varias responsabilidades: Es
responsable de la persistencia de los clientes, responsable de
caracterizar a un empleado, responsable de generar las
nminas, etc.
Las consecuencias de violar el SRP en este caso son dos:
La clase Employee tiene una dependencia con la clase
AccountService para poder realizar el clculo de las
nminas. Por tanto la aplicacin de gestin de proyectos,
a la hora de hacer el despliegue de esa aplicacin,
tambien debe incluir la librera que contiene esa clase
aunque no la necesite.
Si alguna de las aplicaciones necesita implementar
nueva funcionalidad y requiere cambiar la definicin de
la clase Employee, este cambio arrastrara cambios en el
resto de aplicaciones que requeriran adaptarse a los
nuevos cambios de la clase. En caso de olvidarnos, las
consecuencias seran impredecibles.
Una mejora en el diseo sera separar las responsabilidades
en clases distintas

Figura 3
Hemos creado dos nuevas clases: una para la gestin de
nminas y otra para el almacenamiento en la base de datos.
Hay que fijarse en el detalle de que la clase Employee no
depende de las nuevas clases EmployeeAccount y de
EmployeeStorage, sino que la dependencia la tienen las
aplicaciones.


Una forma de probar este principio
Una forma para probar este principio es escribir en un papel
algo parecido a la siguiente imagen, en la primera lnea se
escribe el nombre de la clase, en las siguientes lneas
escribiremos el nombre de la clase y los mtodos de las clase,
ocuparemos tantas lneas como mtodos tengamos en la
clase, luego leeremos cada lnea y analizaremos si la frase
tiene sentido y si la clase tiene la responsabilidad del mtodo
descrito.

Figure 04
Veamos un ejemplo con la siguiente clase:

Figura 5
Veamos el resultado de aplicar el anlisis en base al
documento que se defini.

Figura 6
Aclarando que el mtodo obtenerAceite() se refiere solo a una
lectura de aceite con una posibilidad de asignar esta
responsabilidad a otra clase.


Ampliando el abanico de
"responsabilidades"
Comentbamos anteriormente que no es fcil detectar las
responsabilidades, ya que generalmente tendemos a
agruparlas. No obstante, existen escenarios o casusticas en
los que "se permite" una cierta flexibilidad. Robert C. Martin
expone un ejemplo utilizando la interfaz Modem:

Listado 4

interface Modem
{
void dial(int pNumber);
void hangup();
void send(char[] data);
char[] receive();
}
En este ejemplo se detectan dos responsabilidades,
relacionadas con la gestin de la comunicacin (dial y
hangup) y la comunicacin de datos (send y receive).
Efectivamente, cada una de las funciones puede cambiar por
diferentes motivos; sin embargo, ambas funciones se
llamarn desde distintos puntos de la aplicacin y no existe
una dependencia entre ellas, con lo que no perderamos la
cohesin del sistema.


Conclusin
Pensemos siempre en el ciclo de vida de una aplicacin, y no
nicamente en su diseo y desarrollo. Toda aplicacin sufre
modificaciones a causa de cambios en los requisitos o arreglo
de fallos existentes, y el equipo de desarrollo puede variar; si
a ello le sumamos que el cdigo es difcil de mantener, los
costes de mantenimiento se dispararn, y cualquier
modificacin se presentar como una causa potencial de
errores en entidades relacionadas dentro del sistema.
En definitiva. Este principio es uno de los ms simples de
SOLID, y sin embargo de los ms difciles de implementar
correctamente. Aplicando SRP, podemos alcanzar niveles ms
bajos de acoplamiento y una cohesin ms alta del sistema.



















http://msdn.microsoft.com/es-es/library/9kewt1b3.aspx
SEGN MICROSOFT
Parmetros
Un parmetro representa un valor que el procedimiento espera que se transfiera cuando es
llamado. La declaracin del procedimiento define sus parmetros.
Cuando se define un procedimiento Function o Sub, se especifica una lista de parmetros entre
parntesis que va inmediatamente despus del nombre de procedimiento. Para cada parmetro,
se especifica un nombre, un tipo de datos y un mecanismo para pasar argumentos (ByVal
(Visual Basic) o ByRef (Visual Basic)). Tambin puede indicar que un parmetro es opcional. Esto
significa que el cdigo de llamada no tiene que transferir un valor.
El nombre de cada parmetro acta como una variable local en el procedimiento. El nombre del
parmetro se utiliza del mismo modo que cualquier otra variable.
Argumentos
Un argumento representa el valor que se transfiere a un parmetro del procedimiento cuando se
llama al procedimiento. El cdigo de llamada proporciona los argumentos cuando llama al
procedimiento.
Cuando se llama al procedimiento Function o Sub, se incluye una lista de argumentos entre
parntesis que van inmediatamente despus del nombre del procedimiento. Cada argumento se
corresponde con el parmetro situado en la misma posicin de la lista.
A diferencia de la definicin de parmetros, los argumentos no tienen nombres. Cada
argumento es una expresin que puede contener cero o ms variables, constantes y literales. El
tipo de datos de la expresin evaluada normalmente debe coincidir con el tipo de datos
definido para el parmetro correspondiente, y en algn caso, debe poder convertirse al tipo del
parmetro.

SEGN JAVA:
El trmino parmetro, se usa a menudo para referirse a la variable en la
declaracin del mtodo,
Argumento, se refiere al valor que se envia. Para evitar confusiones, es
comn ver a un parmetro como una variable y un argumento como un
valor

RECOLECTORES DE BASURA
El Recolector de Basura de Java es un componente bsico en el
funcionamiento de la tecnologa Java, cuando un lenguaje permite el
control de memoria, el programador es quien debe indicar el
momento en que esta memoria se debe liberar.
Alguna vez habrs visto el mensaje de volcado de memoria de pila
en tu sistema operativo, pues este es el resultado de no manejar
correctamente la asignacin y liberacin de memoria, estos errores
claramente no se pueden manejar y constituyen un error que
perfectamente puede acabar con la vida de una aplicacin.
Cada vez que en java se crea un objeto, este es guardado en la pila o
en el heap(un espacio de memoria especial), y cuando ya no se
requiere que lo guarde, el colector de basura de java libera la
memoria que empleaba este objeto y la deja disponible para ser
usada nuevamente, para que esto quede claro, el recolector de
basura de Java o Garbage Collector es como un robot que se encarga
de limpiar nuestro cuarto cuando terminamos de jugar, deja todo en
su lugar, listo para que podamos volver a iniciar nuestro juego.
Por lo tanto cuando usas Java, la responsabilidad de desasignar
memoria no es del programador, aunque si se desea, existen
instrucciones para darle una ayuda al colector de basura; es decir, si
queremos limpiar podemos sugerirle que limpie aunque no se puede
asegurar que ejecute la limpieza cuando lo solicitamos, pero si lo
olvidamos, l se encarga de evitar el volcado de memoria limpiando
aquellos objetos que ya no se requieren.
Pero cuando se hace la limpieza?, bueno, lo que hace la mquina
virtual para limpiar es elegir los objetos que ocupan memoria y ya no
se necesitan, esto es porque ya salimos del cdigo que la estaba
llamando, o finalizamos el proceso y los valores de las variables ya no
son tiles.
Por ahora esto es lo bsico para que comprendas como funciona el
garbage collector, luego entraremos en ms detalle.
Comparte este post con aquellos a los que le pueda interesar.
No olvides usar los botones de las redes sociales si es que te parece
que sta informacin fue importante



OTRO:
Explique el funcionamiento del recolector de basura de JAVA
Java tiene un recolector de basura que peridicamente libera la memoria ocupada por
los objetos que no se van a necesitar ms.
El recolector de basura de Java es un barredor de marcas que escanea
dinmicamente la memoria de Java buscando objetos, marcando aquellos que han
sido referenciados. Despus de investigar todos los posibles paths de los objetos, los
que no estn marcados (esto es, no han sido referenciados) se les conoce como
basura y son eliminados.
El colector de basura funciona en un thread (hilo) de baja prioridad y funciona tanto
sncrona como asncronamente dependiendo de la situacin y del sistema en el que se
est ejecutando el entorno Java.

También podría gustarte