Está en la página 1de 16

Traducido del inglés al español - www.onlinedoctranslator.

com

La encapsulación no esconde información


Los principios de ocultación de información van más allá de la
facilidad de encapsulación del lenguaje Java.

Por Wm. Paul Rogers


JavaWorld| 18 DE MAYO DE 2001 1:00 AM PT

Las palabras resbalan. Como proclamó Humpty Dumpty en Lewis Carroll'sA través del espejo "Cuando uso
una palabra, significa exactamente lo que yo elijo que signifique, ni más ni menos ". Ciertamente, el uso
común de las palabrasencapsulamiento yescondite de información parece seguir esa lógica. Los autores
rara vez distinguen entre los dos y, a menudo, afirman directamente que son iguales.

¿Eso lo hace así? No para mí. Si fuera simplemente una cuestión de palabras, no escribiría una palabra
más sobre el tema. Pero hay dos conceptos distintos detrás de estos términos, conceptos engendrados
por separado y mejor entendidos por separado.

La encapsulación se refiere a la agrupación de datos con los métodos que operan sobre esos datos. A menudo, esa
definición se malinterpreta en el sentido de que los datos están ocultos de alguna manera. En Java, puede tener datos
encapsulados que no están ocultos en absoluto.

Sin embargo, ocultar datos no es todo el alcance de la ocultación de información. David Parnas introdujo por
primera vez el concepto de ocultamiento de información alrededor de 1972. Argumentó que el criterio principal
para la modularización del sistema debería referirse al ocultamiento de decisiones críticas de diseño. Hizo
hincapié en ocultar "decisiones de diseño difíciles o decisiones de diseño que probablemente cambiarán".
Ocultar información de esa manera aísla a los clientes de requerir un conocimiento profundo del diseño para
usar un módulo y de los efectos de cambiar esas decisiones.

En este artículo, exploro la distinción entre encapsulación y ocultación de información a través del
desarrollo de código de ejemplo. La discusión muestra cómo Java facilita la encapsulación e investiga las
ramificaciones negativas de la encapsulación sin ocultar datos. Los ejemplos también muestran cómo
mejorar el diseño de clases mediante el principio de ocultación de información.

Clase de posición

Con una conciencia cada vez mayor del enorme potencial de Internet inalámbrico, muchos expertos esperan que los
servicios basados en la ubicación brinden una oportunidad para la primera aplicación asesina inalámbrica. Para el
código de muestra de este artículo, elegí una clase que representa la ubicación geográfica de un punto en la superficie
de la tierra. Como entidad de dominio, la clase, denominadaPosición, representa información del sistema de posición
global (GPS). Un primer corte en la clase parece tan simple como:

Posición de clase pública


{
doble latitud pública;
doble longitud pública;
}

La clase contiene dos elementos de datos: GPSlatitud ylongitud. Actualmente,PAGS posición es


nada más que una pequeña bolsa de datos. Sin embargo,Posición es una clase, yPosiciónlos
objetos se pueden instanciar usando la clase. Para utilizar esos objetos, clasePositionUtility
contiene métodos para calcular la distancia y el rumbo, es decir, la dirección, entrePosición
objetos:

PositionUtility de clase pública


{
doble distancia estática pública (posición posición1, posición posición2)
{
// Calcula y devuelve la distancia entre las posiciones especificadas.
}
doble encabezado estático público (posición posición1, posición posición2)
{
// Calcula y devuelve el rumbo de position1 a position2.
}
}

Omito el código de implementación real para los cálculos de distancia y rumbo.

El siguiente código representa un uso típico dePosición yPositionUtility:

// Crear una posición que represente mi casa


Posición myHouse = nueva Posición ();
myHouse.latitude = 36,538611;
myHouse.longitude = -121.797500;
// Crea un puesto que represente a una cafetería local
Posición coffeeShop = nueva posición ();
coffeeShop.latitude = 36,539722;
coffeeShop.longitude = -121.907222;
// Usar un PositionUtility para calcular la distancia y el rumbo desde mi casa
// a la cafetería local.
doble distancia = PositionUtility.distance (myHouse, coffeeShop);
doble encabezado = PositionUtility.heading (myHouse, coffeeShop);
// Imprimir resultados

System.out.println
("De mi casa en (" +
myHouse.latitude + "," + myHouse.longitude +
") a la cafetería en (" +
coffeeShop.latitude + "," + coffeeShop.longitude +
") es una distancia de" + distancia +
"en un título de" + título + "grados".
);

El código genera el resultado a continuación, que indica que la cafetería está al oeste (270,8
grados) de mi casa a una distancia de 6,09. La discusión posterior aborda la falta de unidades de
distancia.

================================================
=================
Desde mi casa en (36.538611, -121.7975) a la cafetería en
(36.539722, -121.907222) es la distancia de 6.0873776351893385 a
rumbo de 270,7547022304523 grados.

================================================
=================

Posición,PositionUtility, y su uso de código es un poco inquietante y ciertamente no muy orientado


a objetos. ¿Pero como puede ser eso? Java es un lenguaje orientado a objetos y el código usa
objetos.

Aunque el código puede usar objetos Java, lo hace de una manera que recuerda a una era pasada: funciones de utilidad
que operan en estructuras de datos. ¡Bienvenidos a 1972! Mientras el presidente Nixon se acurrucaba sobre grabaciones
secretas, los profesionales de la informática que codificaban en el lenguaje procedimental Fortran utilizaron con
entusiasmo la nueva Biblioteca Internacional de Matemáticas y Estadísticas (IMSL) precisamente de esta manera. Los
repositorios de código como IMSL estaban repletos de funciones para cálculos numéricos. Los usuarios pasaban datos a
estas funciones en largas listas de parámetros, que en ocasiones incluían no solo las estructuras de datos de entrada
sino también de salida. (IMSL ha seguido evolucionando a lo largo de los años y ahora hay una versión disponible para
los desarrolladores de Java).

En el diseño actual,Posición es una estructura de datos simple yPositionUtility es un repositorio


de funciones de biblioteca al estilo IMSL que opera en PAGS posición datos. Como el ejemplo
muestra arriba, los lenguajes modernos orientados a objetos no necesariamente excluyen el uso de
técnicas procedimentales anticuadas.

Agrupar datos y métodos

El código se puede mejorar fácilmente. Para empezar, ¿por qué colocar los datos y las funciones que
operan en esos datos en módulos separados? Las clases de Java permiten agrupar datos y métodos:

Posición de clase pública


{
doble distancia pública (posición de posición)
{
// Calcula y devuelve la distancia desde este objeto al especificado
// posición.
}
doble encabezado público (posición de posición)
{
// Calcula y devuelve el encabezado de este objeto al especificado
// posición.
}
doble latitud pública;
doble longitud pública;
}

Poner los elementos de datos de posición y el código de implementación para calcular la distancia
y el rumbo en la misma clase evita la necesidad de unaPositionUtility clase. AhoraPosición
comienza a parecerse a una verdadera clase orientada a objetos. El siguiente código utiliza esta
nueva versión que agrupa los datos y los métodos:

Posición myHouse = nueva Posición ();


myHouse.latitude = 36,538611;
myHouse.longitude = -121.797500;
Posición coffeeShop = nueva posición ();
coffeeShop.latitude = 36,539722;
coffeeShop.longitude = -121.907222;
doble distancia = myHouse.distance (coffeeShop);
doble encabezado = myHouse.heading (coffeeShop);
System.out.println
("De mi casa en (" +
myHouse.latitude + "," + myHouse.longitude +
") a la cafetería en (" +
coffeeShop.latitude + "," + coffeeShop.longitude +
") es una distancia de" + distancia +
"en un título de" + título + "grados".
);

La salida es idéntica a la anterior y, lo que es más importante, el código anterior parece más natural.
La versión anterior pasó dosPosición objetos a una función en una clase de utilidad separada para
calcular la distancia y el rumbo. En ese código, calcular el encabezado con la llamada al método
util.heading (myHouse, coffeeShop) no indicó claramente la dirección del cálculo. Un desarrollador
debe recordar que la función de utilidad calcula el rumbo desde el primer parámetro al segundo.

En comparación, el código anterior usa la declaraciónmyHouse.heading (cafetería) para calcular el


mismo encabezado. La semántica de la llamada indica claramente que la dirección procede de mi
casa a la cafetería. Conversión de la función de dos argumentos
encabezado (posición, posición) a una función de un argumentoposition.heading (Posición) es conocido
comocurry la función. Currying especializa efectivamente la función en su primer argumento, lo que
resulta en una semántica más clara.

Colocando los métodos que utilizanPosición datos de clase en elPosición la clase en sí hace curry las
funcionesdistancia yrumbo posible. Cambiar la estructura de llamada de las funciones en
de esta forma es una ventaja significativa sobre los lenguajes procedimentales. ClasePosición ahora representa un
tipo de datos abstracto que encapsula los datos y los algoritmos que operan sobre esos datos. Como tipo definido por
el usuario,Posición Los objetos también son ciudadanos de primera clase que disfrutan de todos los beneficios del
sistema de tipos de lenguaje Java.

La función de lenguaje que agrupa los datos con las operaciones que se realizan en esos datos es la
encapsulación. Tenga en cuenta que la encapsulación no garantiza ni la protección de datos ni el
ocultamiento de información. La encapsulación tampoco garantiza un diseño de clase cohesivo. Para lograr
esos atributos de diseño de calidad se requieren técnicas más allá de la encapsulación proporcionada por el
lenguaje. Tal como está implementado actualmente, classPosición no contiene datos y métodos superfluos o
no relacionados, peroPosición expone amboslatitud ylongitud en forma cruda. Que permite a cualquier
cliente de clasePosición para cambiar directamente cualquier elemento de datos internos sin ninguna
intervención porPosición. Claramente, la encapsulación no es suficiente.

Programación defensiva

Para investigar más a fondo las ramificaciones de exponer elementos de datos internos, suponga que
decido agregar un poco de programación defensiva aPosición restringiendo la latitud y la longitud a
rangos especificados por GPS. La latitud se encuentra en el rango [-90, 90] y la longitud en el rango
(-180, 180]. La exposición de los elementos de datoslatitud ylongitud enPosición' La implementación
actual hace que esta programación defensiva sea imposible.

Haciendo atributos de latitud y longitudprivado miembros de datos de la clasePosición y agregar métodos simples de
acceso y mutación, también llamados comúnmente captadores y definidores, proporciona un remedio simple para
exponer elementos de datos sin procesar. En el código de ejemplo a continuación, los métodos de establecimiento
filtran adecuadamente los valores internos delatitud ylongitud. En lugar de lanzar una excepción, especifico realizar
aritmética de módulo en los valores de entrada para mantener los valores internos dentro de los rangos especificados.
Por ejemplo, intentar establecer la latitud en 181.0 da como resultado una configuración interna de -179.0 paralatitud.

El siguiente código agrega métodos getter y setter para acceder a los miembros de datos
privadoslatitud ylongitud:

Posición de clase pública


{
Posición pública (doble latitud, doble longitud)
{
setLatitude (latitud);
setLongitude (longitud);
}
public void setLatitude (latitud doble)
{
// Asegúrate de -90 <= latitud <= 90 usando aritmética de módulo.
// Código no mostrado.
// Luego establece la variable de instancia.
this.latitude = latitud;
}
public void setLongitude (doble longitud)
{
// Asegúrate de -180 <longitud <= 180 usando aritmética de módulo.
// Código no mostrado.
// Luego establece la variable de instancia.
this.longitude = longitud;
}
public double getLatitude ()
{
latitud de retorno;
}
public double getLongitude ()
{
longitud de regreso;
}
doble distancia pública (posición de posición)
{
// Calcula y devuelve la distancia desde este objeto al especificado
// posición.
// Código no mostrado.
}
doble encabezado público (posición de posición)
{
// Calcula y devuelve el encabezado de este objeto al especificado
// posición.
}
doble latitud privada;
doble longitud privada;
}

Usando la versión anterior dePosición requiere solo cambios menores. Como primer cambio, dado que
el código anterior especifica un constructor que toma dosdobleargumentos, el constructor
predeterminado ya no está disponible. El siguiente ejemplo usa el nuevo constructor, así como los
nuevos métodos getter. La salida sigue siendo la misma que en el primer ejemplo.

Posición myHouse = nueva posición (36.538611, -121.797500);


Posición coffeeShop = nueva posición (36.539722, -121.907222);

doble distancia = myHouse.distance (coffeeShop);


doble encabezado = myHouse.heading (coffeeShop);

System.out.println
("De mi casa en (" +
myHouse.getLatitude () + "," + myHouse.getLongitude () +
") a la cafetería en (" +
coffeeShop.getLatitude () + "," + coffeeShop.getLongitude () +
") es una distancia de" + distancia +
"en un título de" + título + "grados".
);

Elegir restringir los valores aceptables delatitud ylongitud a través de métodos de establecimiento es
estrictamente una decisión de diseño. La encapsulación no juega ningún papel. Es decir, la encapsulación,
como se manifiesta en el lenguaje Java, no garantiza la protección de los datos internos. Como
desarrollador, eres libre de exponer los aspectos internos de tu clase. No obstante, debe restringir el
acceso y la modificación de elementos de datos internos mediante el uso de métodos getter y setter.

Aislar el cambio potencial

La protección de los datos internos es solo una de las muchas preocupaciones que impulsan las decisiones de diseño además

de la encapsulación del lenguaje. El aislamiento para cambiar es otro. La modificación de la estructura interna de una clase no

debería afectar, si es posible, a las clases cliente.

Por ejemplo, ya señalé que el cálculo de la distancia en clasePosiciónno indicó unidades. Para
ser útil, la distancia reportada de 6.09 desde mi casa a la cafetería claramente necesita una
unidad de medida. Puede que sepa la dirección a tomar, pero no sé si caminar 6,09 metros,
conducir 6,09 millas o volar 6,09 mil kilómetros.

Análisis de las modificaciones necesarias para agregar unidades a clasePosición revela otro posible defecto
de diseño: no se ha especificado la geometría a utilizar. Puede calcular la distancia y viajar entre dos
puntos cualesquiera de la superficie de la Tierra de varias formas. Dependiendo de la necesidad de
precisión, las distancias dentro de los 50 kilómetros podrían usar las coordenadas cartesianas locales de la
geometría del plano. Estos cálculos simples pueden ser suficientes para ubicaciones dentro de la misma
área, pero calcular la distancia y viajar de San Francisco a París requiere cálculos geométricos más difíciles
a lo largo de un gran círculo. Y realizar estudios de terremotos midiendo la distancia y la dirección entre
ubicaciones precisas a lo largo de la falla de San Andrés en California puede requerir el uso de geometría
elíptica hiperprecisa, incluso para ubicaciones dentro de los 10 kilómetros entre sí.

En lugar de incluir directamente todas estas opciones de dominio enPosición, Agrego variables de referencia
de tipoUnidades yGeometría. Los objetos a los que hacen referencia estas variables manejan los detalles
relacionados con las unidades y la geometría. No muestro el código real para la definición de interfaces.
Unidades yGeometría ni ninguna clase concreta que implemente estas interfaces. Baste decir que hay cuatro
implementaciones para las unidades:

● Kilómetros
● Millas náuticas
● EstatuaMillas
● Radianes

y tres implementaciones para geometría:

● Geometria plana
● Geometría esférica
● Geometría Elíptica

ApropiadoPosición Los captadores y definidores permiten el cambio dinámico de las unidades o de la


geometría utilizada para los cálculos de distancia y rumbo.

Ahora, para el descubrimiento más importante y potencialmente dañino: mientras se crean las diversas
clases de geometría, se determina que para delegar el trabajo a estos objetos, la clasePosiciónno debe
mantener elementos de datoslatitud ylongitud, pero en su lugar debe representar la ubicación interna
utilizando ángulos de coordenadas esféricastheta yphi. No me desviaré del motivo. Independientemente
de por qué es necesario tal cambio, el cambio en sí mismo puede resultar doloroso o imposible siPosición
Los clientes acceden directamente a los datos internos. Antes de examinar las ramificaciones de estos
cambios, veamos la versión actualizada dePosición que presenta las nuevas variables de referencia:

Posición de clase pública


{
Posición pública (doble latitud, doble longitud)
{
setLatitude (latitud);
setLongitude (longitud);
// Por defecto a la geometría del plano y los kilómetros
geometría = new PlaneGeometry ();
unidades = nuevos Kilómetros ();
}
public void setLatitude (latitud doble)
{
setPhi (Math.toRadians (latitud));
}
public void setLongitude (doble longitud)
{
setTheta (Math.toRadians (longitud));
}
public void setPhi (doble phi)
{
// Asegúrate de -pi / 2 <= phi <= pi / 2 usando aritmética de módulo.
// Código no mostrado.
this.phi = phi;
}
público vacío setTheta (doble theta)
{
// Asegúrate de -pi <theta <= pi usando aritmética de módulo.
// Código no mostrado.
this.theta = theta;
}
// Definidores para geometría y unidades no mostradas
public double getLatitude ()
{
return (Matemáticas a grados (phi));
}
public double getLongitude ()
{
return (Math.toDegrees (theta));
}
// Getters para geometría y unidades no mostradas
doble distancia pública (posición de posición)
{
// Calcula y devuelve la distancia desde este objeto al especificado
// posición usando la geometría y las unidades actuales.
}
doble encabezado público (posición de posición)
{
// Calcula y devuelve el encabezado de este objeto al especificado
// posición usando la geometría y las unidades actuales.
}
phi doble privado;
doble theta privada;
geometría de geometría privada;
Unidades de unidades privadas;

Note que aunquePosición ya no mantiene elementos de datos internoslatitud ylongitud, los métodos
getter y setter correspondientes permanecen. Esa estabilidad aísla los objetos del cliente del cambio
interno. Al hacer que los captadores y definidores accedan a los atributos de latitud y longitud, los clientes
ni siquiera necesitan saber sobre el cambio. Las únicas modificaciones necesarias en el siguiente uso del
código del nuevoPosición la clase se refiere a la adición de las unidades y los atributos de geometría:

Posición myHouse = nueva posición (36.538611, -121.797500);


Posición coffeeShop = nueva posición (36.539722, -121.907222);
doble distancia = myHouse.distance (coffeeShop);
doble encabezado = myHouse.heading (coffeeShop);
System.out.println
("Usando" + myHouse.getGeometry () + "geometría", +
"de mi casa en (" +
myHouse.getLatitude () + "," + myHouse.getLongitude () +
") a la cafetería en (" +
coffeeShop.getLatitude () + "," + coffeeShop.getLongitude () +
") es una distancia de" + distancia + "" + myHouse.getUnits () +
"en un título de" + título + "grados".
);
myHouse.setGeometry (Geometry.SPHERICAL);
myHouse.setUnits (Unidades.STATUTE_MILES);
distancia = miCasa.distancia (cafetería);
header = myHouse.heading (cafetería);
System.out.println
("Usando" + myHouse.getGeometry () + "geometría", +
"de mi casa en (" +
myHouse.getLatitude () + "," + myHouse.getLongitude () +
") a la cafetería en (" +
coffeeShop.getLatitude () + "," + coffeeShop.getLongitude () +
") es una distancia de" + distancia + "" + myHouse.getUnits () +
"en un título de" + título + "grados".
);

El código anterior genera la siguiente salida:

================================================
=================
Usando geometría plana, desde mi casa en (36.538611, -121.7975)
a la cafetería en (36.539722, -121.907222) es una distancia de
9.79675938972254 Kilómetros con rumbo 270.58013375337254
grados.
Usando geometría esférica, desde mi casa en (36.538611, -121.7975)
a la cafetería en (36.539722, -121.907222) es una distancia de
6.0873776351893385 Millas estatutarias en un rumbo de 270.7547022304523
grados.

================================================
=================

La salida incluye tanto la geometría utilizada para los cálculos como las unidades de distancia. La distancia
relativamente pequeña produce una diferencia insignificante, aunque no imperceptible, entre el uso de
geometrías planas y esféricas. Los rumbos calculados difieren ligeramente, y la comparación de las distancias
utilizando el factor de conversión de 1,61 kilómetros por milla muestra que las distancias también varían
ligeramente.

Carros y cuervos

Ahora que sé que la cafetería está a 6,09 millas al oeste de mi casa, decido conducir hasta allí. Con la desesperada necesidad

de una dosis de café, me subo a mi coche y me preparo para dirigirme hacia el oeste. Desafortunadamente,
que me lleva directamente a través de la parte trasera de mi garaje, a través del césped trasero y en un barranco de 20
metros de profundidad. ¡Hasta aquí los servicios basados en la ubicación! Necesitaría más que café para salir de ese
lío.

Aunque me parezca genial la idea, el municipio en el que vivo aún no ha aprobado una carretera que conecte
directamente mi casa con la cafetería. Entonces, si quiero Java, tendré que modificar mi ruta planificada.
PosiciónLa información de distancia y dirección funciona bien para los cuervos, pero no tanto para los
automóviles.

Decido construir una clase que represente la ruta real de conducción entre mi casa y la cafetería. El
camino consta de una serie de segmentos que conectan puntos a lo largo del camino. Así que clase
Ruta mantiene elPosición objetos necesarios para definir una ruta. Un diseño de primer corte (y
ciertamente no completo) se ve así:

ruta de clase pública


{
ruta pública (segmentos int)
{
posiciones = nueva posición [segmentos + 1];
}
public void setPosition (int index, Position position)
{
posiciones [índice] = posición;
}
getPosition de posición pública (índice int)
{
posición de retorno [índice];
}
Posición pública [] getPositions ()
{
posiciones de retorno;
}
doble distancia pública (int segmentoNumber)
{
// Calcula la distancia del número de segmento especificado
}
público doble distancia ()
{
// Itere las posiciones y acumule las distancias entre cada una.
}
doble encabezado público (int segmentoNumber)
{
// Calcula el encabezado para el número de segmento especificado
}
Puesto privado [] puestos;
}

A continuación se describe un uso de la claseRuta:

● Crea unRuta objeto con 10 segmentos


● Crear 11Posición objetos
● Coloque cadaPosición en el lugar apropiado a lo largo de la ruta
● UtilizarRuta para calcular la distancia y la dirección del primer segmento
● UtilizarRuta para calcular la distancia total

Exponiendo la estructura interna

Abundan los problemas en el diseño simple de primera clase de la clase.Ruta. Desde una perspectiva de
encapsulación y ocultación de información, el método de accesogramoetPositions (), que devuelve el
matriz dePosición objetos usados dentro de la claseRuta, resulta potencialmente problemático. Aunque
claseRuta encapsula la matriz, no protege la decisión de diseño que resultó en el uso de una matriz. Es
decir, devolviendo la matriz interna dePosiciónobjetos deRuta ha expuesto la decisión de utilizar una
matriz. Esto es particularmente atroz cuando el diseño usa un nombre de método como
getPositionsArray (). Si en un momento posterior el diseño se cambia a unArrayList,RutaLos clientes están
expuestos al cambio. Puede crear y devolver una matriz primitiva desde elArrayList, pero el problema
persiste. La elección de la estructura de datos interna utilizada para administrar la ruta no debería afectar
a los clientes externos. Esa es una clara diferencia entre la encapsulación y el ocultamiento de
información.

Un diseño de segundo corte se ve así:

ruta de clase pública


{
ruta pública ()
{
posiciones = new ArrayList ();
}
agregar vacío público (posición de la posición)
{
posiciones.append (posición);
}
getPosition de posición pública (índice int)
{
posiciones de retorno obtener (índice);
}
doble distancia pública (int segmentoNumber)
{
// Calcula la distancia del número de segmento especificado
}
público doble distancia ()
{
// Calcula la distancia acumulada entre cada segmento.
}
doble encabezado público (int segmentoNumber)
{
// Calcula el encabezado para el número de segmento especificado
}
posiciones de la lista privada;
}

EliminandogetPositions () corrige la exposición injustificada de los detalles internos de ese método.


Cambiar la estructura de datosposiciones a unLista y usandoañadir (posición) en vez desetPosition (int,
Posición) aísla aún más la decisión de diseño con respecto a la colección interna que se está utilizando.

Exponiendo la implementación interna

Más problemas para ocultar información acechan en este diseño. El métodogramo etPosition (int) expone
los elementos de datos internos reales mantenidos porRuta. Cualquier cliente puede obtener una referencia a
cualquiera de losPosición objetos a lo largo de la ruta y librementecambiar el estado de esoPosición objeto.

Para apreciar las ramificaciones de esta exposición, considere una posible clase
Rutaimplementación del métododistancia (), que calcula la distancia acumulada en todos los segmentos
de la ruta. Suponga que la implementación itera sobre la internaPosición objetos y utiliza cada objeto
para calcular la distancia al siguientePosición. El cálculo claramente requiere el uso de un solo tipo de
unidad de distancia. Para hacer cumplir este requisito, el métodoañadir (posición) en el código siguiente
establece uniformemente las unidades de todas las entradasPosición objetos.

Y ahora para la vulnerabilidad total de lagetPosition (int) exposición al método: aunque todos losPosición
objetos agregados a la claseRuta podrían tener sus unidades correctamente configuradas cuando se
agregan a la ruta, cualquier objeto cliente podría obtener unPosiciónobjetar y configurar las unidades a lo
que elija el cliente,sin informandoRuta. El daño potencial al calcular el resultado en el métododistancia ()
resulta particularmente desconcertante. distancia () podría sin saberlo y fácilmente agregar kilómetros a
millas terrestres.

La siguiente versión de claseRuta modifica elañadir (posición) método y corrige el


getPosition (int) método devolviendo un equivalentePosición objeto en lugar de la interna
Posición objeto:

ruta de clase pública


{
ruta pública ()
{
posiciones = new ArrayList ();
}
agregar vacío público (posición de la posición)
{
Posición aPosition = nueva posición (posición);
aPosition.setUnits (myUnits);
aPosition.setGeometry (myGeometry);
posiciones.append (unaPosición);
}
getPosition de posición pública (índice int)
{
Posición posición = nueva posición (posiciones.get (índice));
posición de retorno;
}
doble distancia pública (int segmentoNumber)
{
// Calcula la distancia del número de segmento especificado
}
público doble distancia ()
{
// Calcula la distancia acumulada entre cada segmento.
}
doble encabezado público (int segmentoNumber)
{
// Calcula el encabezado para el número de segmento especificado
}
posiciones privadas de ArrayList;
Unidades privadas myUnits;
geometría privada myGeometry;
}

ClaseRuta aún no está completo, pero he reforzado el diseño ocultando adecuadamente las decisiones
de diseño tomadas hasta ahora.

Conclusión

La encapsulación es una construcción de lenguaje que facilita la agrupación de datos con los métodos que
operan en esos datos. La ocultación de información es un principio de diseño que se esfuerza por proteger a
las clases de clientes del funcionamiento interno de una clase. La encapsulación facilita, pero no garantiza, la
ocultación de información. Unir los dos en un concepto evita una comprensión clara de cualquiera de ellos.

La manifestación de encapsulación en lenguaje Java ni siquiera asegura objetos básicos orientados a objetos. El
argumento no es necesariamente que debería, solo que no es así. Los desarrolladores de Java pueden crear
alegremente bolsas de datos en una clase y colocar funciones de utilidad que operan en esos datos en una clase
separada. Entonces, como primera regla:
Regla de encapsulación 1: coloque los datos y las operaciones que se realizan en esos datos en la
misma clase

Esta práctica estándar crea clases que se adhieren a los principios de los tipos de datos abstractos.

Pero quieres más de tus objetos; desea que representen entidades cohesivas y viables. Una
segunda regla se refiere a la forma de elegir los datos y las operaciones para encapsular:

Regla de encapsulación 2: utilice un diseño basado en la responsabilidad para determinar la


agrupación de datos y operaciones en clases

Esta segunda regla de encapsulación también se refiere a la ocultación de información: no se limite a


agrupar datos y operaciones; deje que los principios de diseño le guíen. Pregúntese de qué acciones será
responsable un objeto de la clase. No permita que la clase englobe más o menos que un conjunto
completo de responsabilidades razonables.

Como ha visto en los muchos ejemplos anteriores, la función de encapsulación del lenguaje Java no es suficiente
para garantizar un diseño de clase sólido. El principio de ocultación de información estipula que protege a los
clientes de un objeto de las decisiones de diseño interno que se toman para esa clase de objetos. Entonces,
como primera regla para ocultar información:

Regla de ocultación de información 1: no exponga elementos de datos

Hacer todos los elementos de datosprivado y use getters y setters. No se engañe creyendo que no se producirá ningún
daño al acceder directamente a los elementos de datos internos de un objeto. Incluso si solo codifica contra esos
componentes internos, aún existe una vulnerabilidad futura. No puede predecir cuándo podría necesitar cambiar la
naturaleza de los datos internos, y el acoplamiento frágil con los objetos del cliente suena desconcertante cuando se
rompe.

Vaya un paso más allá cuando oculte decisiones de diseño relacionadas con datos internos. Cuando sea posible,
ni siquiera revele si un atributo está almacenado o derivado. Los objetos del cliente no necesitan conocer la
diferencia. Un atributo es una cualidad o característica inherente a una clase de objetos. Desde la perspectiva
del cliente, los atributos de los objetos corresponden a responsabilidades, no a la estructura interna de los
datos. Eso nos lleva a la siguiente regla:

Regla de ocultación de información 2: no exponga la diferencia entre los datos almacenados y los datos
derivados

Por ejemplo, un objeto cliente solo necesita saber que un objeto tiene un atributo develocidad. Utilice un
método de acceso llamadovelocidad () ogetSpeed () en lugar decalcularSpeed (). Si el valor se almacena o
se deriva es una decisión de diseño que es mejor mantener oculta.

Como corolario de las dos primeras reglas de ocultación de información, coloco todos los datos internos al final
del texto de la clase, después de los métodos que operan con los datos. Cuando examino una clase para
comprender su código, mirar primero sus datos internos me lleva a hacer las preguntas iniciales incorrectas. Me
esfuerzo por comprender las responsabilidades de una clase antes de preocuparme por
cualquier detalle de la estructura de datos. Colocar datos después de los métodos en el texto de la clase me recuerda, cada

vez que miro el código, pensar primero en el comportamiento de una clase, no en su estructura.

La siguiente regla se refiere a la elección de la estructura interna:

Regla de ocultación de información 3: no exponga la estructura interna de una clase

Los clientes deben permanecer aislados de las decisiones de diseño que impulsan la selección de la
estructura de clases interna. Por ejemplo, un cliente no debe saber si una matriz primitiva o una
ArrayList se utiliza para mantener una colección interna de objetos. La estructura interna es
particularmente evidente mediante el uso de nombres de métodos comogetDataArray () o
getTreeMap ().

La regla final se refiere a opciones de detalles de implementación:

Regla de ocultación de información 4: no exponga los detalles de implementación de una clase

No permita que los clientes conozcan o afecten de manera invisible los detalles de implementación de una clase. Por
ejemplo, un cliente no debería poder alterar el resultado de un cálculo interno cambiando el estado de los objetos
utilizados en ese cálculo supuestamente oculto.

Aunque no es una lista exhaustiva, esas reglas ayudan a separar el concepto de encapsulación proporcionado por
el lenguaje de la ocultación de información proporcionada por las decisiones de diseño. Cada regla es bastante fácil
de seguir y cada una ayudará a crear clases que no solo sean más resistentes a los cambios, sino que también sean
más fáciles de usar por los objetos del cliente.

Wm. Paul Rogers is a senior engineering manager and application architect at Lutris
Technologies, where he builds computer solutions utilizing Enhydra, the leading open source
Java/XML application server. He began using Java in the fall of 1995 in support of oceanographic
studies conducted at the Monterey Bay Aquarium Research Institute, where he led the charge in
utilizing new technologies to expand the possibilities of ocean science research. Paul has been
using object-oriented methods and technologies for more than nine years.

También podría gustarte