Sie sind auf Seite 1von 19

Cómo se pronuncia

Las afirmaciones (aquí la condición previa) son otra cosa:


formas de describir las condiciones en las que funcionarán los elementos de software y las
condiciones que alcanzarán a cambio. Al poner el condi- -
tion rrew / = Nulo en la condición previa, lo hacemos parte de la especificación de la rutina; la
última forma mostrada
(con el If) significaría que tenemos
cambió esa especificación, ampliando
para incluir el caso especial new = Void
tan aceptable como consecuencia. cualquier violación de tiempo de ejecución de una
afirmación no es un caso especial
pero siempre la manifestación de un error de software. Para ser preciso
Una violación de precondición indica
un error en el cliente (llamante). Llamador
no observó las condiciones impuestas
en llamadas correctas.
Una violación posterior a la condición es un error en
El proveedor (rutina). La rutina falló
para cumplir sus promesas.

Observaciones sobre contratos de software


En la Tabla 2, la entrada inferior derecha es
Particularmente notable. Si la condición previa no se cumple, la rutina no se cumple.
obligado a hacer cualquier cosa, como una empresa de entrega de correo dada un paquete
que hace
No cumple con la especificación. Esto significa
que el cuerpo de rutina no debe ser de
la forma mencionada anteriormente:

El uso de una construcción de este tipo anularía el propósito de tener una condición previa
(Requerir cláusula). Esta es una regla absoluta: o tienes la condición
en el Requerir, o lo tienes en un If
instrucción en el cuerpo de la rutina,
pero nunca en ambos.
Este principio es exactamente lo contrario de
la idea de programación defensiva, ya que
dirige a los programadores para evitar pruebas redundantes. Tal enfoque es posible
y fructífero porque el uso de aserciones alienta a escribir software para deletrear
las condiciones de coherencia que podrían
salir mal en tiempo de ejecución. Luego, en lugar de verificar a ciegas, como con la
programación defensiva, puede usar claramente definido
contratos que asignan la responsabilidad
para cada condición de consistencia a uno de
las fiestas. Si el contrato es preciso y
explícito, no hay necesidad de redundancia
cheques.
Cuanto más fuerte es la condición previa, el
Más pesada la carga sobre el cliente. y
Más fácil para el proveedor. El asunto
de quién debería tratar con valores anormales es esencialmente una decisión pragmática
sobre la división del trabajo: la mejor solución es la que logra la más simple
arquitectura. Si cada rutina y persona que llama
comprobado por cada posible error de llamada,
Las rutinas nunca realizarían ningún trabajo útil.
En muchos programas existentes, uno puede
Difícilmente encontrar las islas de procesamiento útil en los océanos de código de verificación
de errores.
En ausencia de afirmaciones, defensivas
La programación puede ser el único enfoque razonable. Pero con técnicas para
definiendo con precisión la responsabilidad de cada parte, según lo dispuesto por las
afirmaciones, tales
la redundancia (tan perjudicial para la consistencia y simplicidad de la estructura) es
innecesario.

¿Quién debe verificar?


El rechazo de la programación defensiva significa que el cliente y el proveedor
ambos no son responsables de una condición de consistencia. O la condición
es parte de la condición previa y debe ser
garantizado por el cliente, o no
declarado en la condición previa y debe ser
manejado por el proveedor

¿Cuál de estas dos soluciones debería


ser elegido? No hay una regla absoluta;
varios estilos de rutinas de escritura son
posible, que van desde "exigente"
aquellos donde la precondición es fuerte
(poner la responsabilidad sobre los clientes) a
"Tolerantes" donde es débil (aumentando la carga de la rutina). Elegir entre ellos es hasta
cierto punto
una cuestión de preferencia personal; de nuevo,
El criterio clave es maximizar el
simplicidad general de la arquitectura.
La experiencia con Eiffel, en particular el diseño de las bibliotecas, sugiere que el uso
sistemático de un estilo exigente
Puede ser bastante exitoso. En esto
. enfoque, cada rutina se concentra en hacer un trabajo bien definido para hacerlo bien,
en lugar de intentar
manejar todos los casos imaginables. Cliente
Los programadores no esperan milagros.
Siempre y cuando las condiciones sobre el uso de
una rutina tiene sentido, y la rutina
la documentación establece estas condiciones
(el contrato) explícitamente, los programadores podrán usar la rutina
correctamente observando su parte de la
acuerdo
Una objeción a este estilo es que
parece obligar a cada cliente a hacer el
las mismas verificaciones, correspondientes a la condición previa, y por lo tanto resultan en
repeticiones innecesarias y perjudiciales. Pero esto
argumento no está justificado:
La presencia de un precondición en
una rutina r no significa necesariamente
que cada llamada debe probar p, como en
si x.p entonces
x.r
más
. . . Tratamiento especial .,.
Fin
Lo que significa la condición previa es que el
el cliente debe garantizar la propiedad p; esto es
no es lo mismo que probar para esta condición
antes de cada llamada. Si el contexto de la
la llamada implica p, entonces no hay necesidad de
Tal prueba. Un esquema típico es
x.s; x.r
donde la condición posterior de s implica p
Suponga que muchos clientes necesitarán verificar la condición previa. Entonces lo que
importa es el "Especial
Tratamiento ". Es lo mismo para todos
llamadas o específicas para cada llamada. Si es el
mismo, causando repetición indebida en varios clientes, esto es simplemente el signo de un
diseño de interfaz de clase pobre, usando un
contrato demasiado exigente para r. los
contrato debe ser renegociado y
ampliado (más tolerante) para incluir el tratamiento especial estándar
como parte de la especificación de la rutina.
l Si, sin embargo, el tratamiento especial
es diferente para varios clientes, entonces el
necesidad de que cada cliente realice su propio
La prueba individual para p es intrínseca y no es consecuencia del método de diseño.
sugerido aquí. Estas pruebas tendrían
para ser incluido de todos modos.
El último caso corresponde a la situación frecuente en la que un proveedor simplemente carece
del contexto adecuado para manejar
casos anormales Por ejemplo, es imposible para un STACK de uso general
módulo para saber qué hacer cuando se le solicita que haga estallar un elemento desde un
vacío
apilar. Solo el cliente: un módulo de
un compilador u otro sistema que usa
pilas: tiene la información necesaria.
Invariantes de clase
Las precondiciones y postcondiciones rutinarias pueden usarse en enfoques no
orientados a objetos, aunque encajan particularmente bien con las orientadas a
objetos
método. Invariantes, el siguiente uso principal
de afirmaciones, son inconcebibles fuera
del enfoque orientado a objetos.
Una clase invariante es una propiedad que
se aplica a todas las instancias de la clase, trascendiendo rutinas particulares.
Por ejemplo, el invariante de una clase que describe
los nodos de un árbol binario podrían ser del
formulario que se muestra en la Figura 4, indicando que el
padre de los hijos izquierdo y derecho de un nodo, si estos hijos existen, es
El nodo en sí. (El operador Implies
denota implicación. Las reglas de precedencia del operador Eiffel hacen que
los paréntesis sean innecesarios aquí; han sido añadidos
para mayor claridad.)
La cláusula opcional invariante de clase
aparece al final de un texto de clase:
clase BINARY-TREE [característica q
Atributo y rutina
declaraciones
invariante
. . Como se muestra arriba
TABLA de clase final
Dos propiedades
invariante:
caracterizar una clase
El invariante debe estar satisfecho después de la creación de cada instancia
del
clase (cada árbol binario en este ejemplo). Esto significa que cada creación
procedimiento de la clase debe producir un
objeto que satisface al invariante. (Una clase
puede tener uno o más procedimientos de creación, que sirven para inicializar
objetos.
El procedimiento de creación a llamar
cualquier caso dado se especifica en las instrucciones de creación).
l El invariante debe ser preservado por
cada rutina exportada de la clase (que
es decir, todas las rutinas disponibles para los clientes). Cualquier rutina de
este tipo debe garantizar
que el invariante está satisfecho a la salida si
quedó satisfecho a la entrada.
En efecto. entonces, se agrega el invariante
a la precondición y postcondición
de cada rutina exportada de la clase.
Pero lo invariante caracteriza a la clase.
como un todo en lugar de su individuo
rutinas
La figura 5 ilustra estos requisitos
al imaginar el ciclo de vida de cualquier objeto
como una secuencia de transiciones entre
Estados "observables". Estados observables,
se muestran rectángulos sombreados, son los estados
que siguen inmediatamente a la creación de objetos,
y cualquier estado alcanzado posteriormente
después de la ejecución de una rutina exportada de la clase generadora del
objeto. los
invariante es la restricción de consistencia
en estados observables. (No está necesariamente satisfecho entre estos
estados).
la creación de cada instancia de la
clase (cada árbol binario en este ejemplo). Esto significa que cada creación
procedimiento de la clase debe producir un
objeto que satisface al invariante. (Una clase
puede tener uno o más procedimientos de creación, que sirven para inicializar
objetos.
El procedimiento de creación a llamar
cualquier caso dado se especifica en las instrucciones de creación).
l El invariante debe ser preservado por
cada rutina exportada de la clase (que
es decir, todas las rutinas disponibles para los clientes). Cualquier rutina de
este tipo debe garantizar
que el invariante está satisfecho a la salida si
quedó satisfecho a la entrada.
En efecto. entonces, se agrega el invariante
a la precondición y postcondición
de cada rutina exportada de la clase.
Pero lo invariante caracteriza a la clase.
como un todo en lugar de su individuo
rutinas
La figura 5 ilustra estos requisitos
al imaginar el ciclo de vida de cualquier objeto
como una secuencia de transiciones entre
Estados "observables". Estados observables,
se muestran rectángulos sombreados, son los estados
que siguen inmediatamente a la creación de objetos,
y cualquier estado alcanzado posteriormente
después de la ejecución de una rutina exportada de la clase generadora del
objeto. los
invariante es la restricción de consistencia
en estados observables. (No está necesariamente satisfecho entre estos
estados).
Lo invariante corresponde a lo que
Sobre el lenguaje de afirmación
Este artículo incluye muchos ejemplos de afirmaciones típicas. Pero, ¿qué es
exactamente permitido en una afirmación?
Las aserciones de Eiffel son expresiones booleanas, con algunas extensiones
como la antigua notación. Desde todo el poder
de expresiones booleanas está disponible, pueden incluir
llamadas a funciones Porque todo el poder del lenguaje es
disponibles para escribir estas funciones, las condiciones que expresan pueden
ser bastante sofisticadas. Por ejemplo, el invariante de una clase ACYCLIC-
GRAPH puede contener una cláusula de
la forma
no cíclico
donde cydic es una función de valor booleano que determina
si un gráfico contiene algún ciclo utilizando el algoritmo de gráfico apropiado.
En algunos casos, es posible que desee utilizar expresiones cuantificadas de la
forma "Para todas las x de tipo T, p (x) contiene" o "Hay
existe x de tipo 7, de modo que p (x) contiene ", donde p es una determinada
propiedad booleana. Tales expresiones no están disponibles.
en eiffel Sin embargo, es posible expresar las propiedades correspondientes
utilizando la misma técnica: llamadas a
funciones que dependen de bucles para emular los cuantificadores.
Aunque se ha pensado en extender el
lenguaje para incluir un lenguaje de especificación formal completo, con cálculo
de predicados de primer orden, la necesidad de
Tal extensión no parece crucial. En Efffef, pensado como un vehículo para el
desarrollo de software industrial más bien
que solo para investigación, el uso de llamadas a funciones en afirmaciones
parece proporcionar una compensación aceptable entre
diferentes objetivos de diseño: confiabilidad, la capacidad de generar código
eficiente y la simplicidad general del lenguaje.
De hecho, el cálculo de predicados de primer orden no sería necesariamente
suficiente. Muchas propiedades prácticamente importantes,
como el requisito de que un gráfico sea no cíclico,
requieren un cálculo de orden superior.
El uso de funciones, es decir, cálculos,
No está exento de peligros. En software, una función es un caso
de una rutina: prescribe ciertas acciones. Esto hace que las funciones de
software sean imperativas, mientras que las funciones matemáticas se
consideran aplicativas. La mayor diferencia es
que las funciones del software pueden producir efectos secundarios (cambio
El estado de la computación). Introducir funciones en
aserciones permite que el zorro imperativo vuelva al aplicativo
gallinero.
En la práctica, esto significa que cualquier función utilizada en las afirmaciones
debe ser de calidad impecable, evitando cualquier
cambiar al estado actual y cualquier operación que deba
dar lugar a situaciones anormales.

put-child (nuevo: NODE)


Agregar nuevo a los hijos del nodo actual
requerimiento
nuevo I = vacío
asegurar
newgarent = Actual;
cuenta infantil = cuenta infantil anterior + 1
fue llamado "condiciones generales" en el
discusión inicial de contratos: leyes o
regulaciones que se aplican a todos los contratos de
cierta categoría a menudo a través de una cláusula
de la forma "todas las disposiciones del XX
el código se aplicará a este contrato ".
Documentando un
contrato de software

Para que la teoría del contrato funcione correctamente y conduzca a sistemas correctos,
el cliente
los programadores deben contar con un
Descripción adecuada de las propiedades de interfaz de una clase y sus rutinas: el
contratos
Aquí las afirmaciones pueden jugar un papel clave.
ya que ayudan a expresar el propósito de un
elemento de software como una rutina sin referencia a su implementación.
El breve comando del entorno Eiffel sirve para documentar una clase.
mediante la extracción de información de la interfaz. En
Este enfoque. documentación de software
no se trata como un producto para ser desarrollado y mantenido por separado de
el código real: en cambio, es el más
parte abstracta de ese código y puede ser
extraído por herramientas informáticas.
El comando corto retiene solo el
características exportadas de una clase y. por un
rutina exportada, suelta el cuerpo de rutina
y cualquier otra implementación relacionada
detalles. Sin embargo. Se mantienen las condiciones previas y posteriores. (Así es el
comentario del encabezado si está presente). Por ejemplo, Figura 6
muestra lo que produce el comando corto
para la rutina del plct. Se expresa simplemente
y concisamente el propósito de la rutina. sin referencia a un particular
implementación.
Toda la documentación sobre los detalles de
Clases de Eiffel (por ejemplo, la clase
especificaciones en el libro sobre lo básico
bibliotecas ") se produce automáticamente en
esta moda Para clases que heredan de
otros, el comando corto debe combinarse con otra herramienta.flar. que aplana la
jerarquía de clases al incluir características heredadas en el mismo nivel que
Los "inmediatos" (los declarados en
la clase misma).

Seguimiento de afirmaciones
Qué pasa si. durante la ejecución una
Qué sistema viola una de sus propias afirmaciones?
En el entorno de desarrollo. el
La respuesta depende de una opción de compilación. Para cada clase puedes elegir
desde varios niveles de control de aserciones: sin verificación de aserciones.
precondiciones solamente (el valor predeterminado). condiciones previas
y postcondiciones. todo lo anterior más
invariantes de clase. o todas las afirmaciones. (Los
diferencia entre los dos últimos sigue
de la existencia de otras afirmaciones.
como loop in \ ariants. no cubierto en
la discusión actual)
Para una clase compilada bajo el "no
opción de monitoreo de aserciones ”. afirmaciones
no tiene efecto en la ejecución del sistema. los
opciones posteriores causan evaluación de
aserciones en varias etapas: entrada de rutina para condiciones previas, salida de rutina
para
condiciones posteriores y ambos pasos para invariantes.
Bajo las opciones de monitoreo, el
efecto de una violación de afirmación es elevar
una excepción. Las posibles respuestas a
Una excepción se discute más adelante.
¿Por qué monitorear?
Como se señaló, las violaciones de afirmación no son
casos especiales (pero esperados); resultan
de bichos. La aplicación principal de la supervisión de aserción de tiempo de ejecución.
entonces, se está depurando. Activando la comprobación de afirmaciones
(en cualquiera de los niveles enumerados anteriormente)
hace posible detectar errores.
Al escribir software. desarrolladores
hacer muchas suposiciones sobre las propiedades que se mantendrán en varias etapas de
la ejecución del software, especialmente la entrada y el retorno de rutina. En los
enfoques habituales para la construcción de software. Estos supuestos siguen siendo
informales e implícitos. Aquí el mecanismo de afirmación
permite a los desarrolladores expresarlos explícitamente. El control de la afirmación,
entonces, es un
manera de llamar al fanfarrón del desarrollador comprobando qué hace el software
contra qué
su autor cree que sí. Esto produce un
enfoque productivo para la depuración, prueba. y garantía de calidad, en la cual
la búsqueda de errores no es ciega sino basada
en condiciones de consistencia proporcionadas por
los propios desarrolladores
Particularmente interesante aquí es el uso
de precorzditions en clases de biblioteca. En
Según el enfoque general para la construcción de software sugerido por el método
Eiffel, los desarrolladores crean sucesivos "grupos" de clases en un orden ascendente.
de más general (reutilizable) a más
específico (dependiente de la aplicación). Esta
es el "modelo de clúster" del software
ciclo de vida "." Decidir lanzar una biblioteca
el grupo 1 para uso general normalmente implica un grado razonable de confianza
en su calidad: la creencia de que no hay errores
permanecer en 1. Por lo tanto, puede ser innecesario
supervisar las condiciones posteriores de las rutinas
en las clases de 1. Pero las clases de un
clúster de aplicaciones que es un cliente de I
(ver Figura 7) puede ser "joven" y
contener errores: dichos errores pueden aparecer como
argumentos erróneos en llamadas a rutinas de las clases de 1. Supervisar condiciones
previas para clases de Ayudé a encontrar
ellos. esta es una de las razones del porque
la comprobación previa es el valor predeterminado
Opción de compilación.

las implementaciones correspondientes para


operaciones de búsqueda e inserción. Un descendiente de esa clase puede proporcionar
un
representación que se adapta específicamente a ciertos casos (como casi completa
árboles binarios) y redeclarar las rutinas
en consecuencia.
Tal forma de redeclaración se llama
Una redefinición. Se supone que la rutina heredada ya tenía una implementación. La
otra forma de redeclaración,
llamado efecto, se aplica a características para
que la versión heredada, conocida como
característica diferida (o abstracta), no tenía
implementación. pero solo una especificación. El efecto luego proporciona una
implementación (haciendo que la característica sea efectiva, lo contrario de diferido).
los
discusión posterior se aplica a ambos
formas de redeclaración, aunque por simplicidad se concentra en la redefinición.
La redeclaración toma todo su poder
gracias al polimorfismo y dinámica
Unión. El polimorfismo es una adaptación de tipo controlada por herencia. Más
concretamente, esto significa que si tienes b
de tipo BINARY-TREE y sb de tipo
ARBOL BINARIO ESPECIAL, este último
clase descendiente de la primera, entonces
la asignación
b: = sb
está permitido, permitiendo que b se adjunte en tiempo de ejecución a instancias de
ÁRBOL-BINARIO ESPECIAL, de un más
forma especializada que la declaración de
b especifica. Por supuesto, esto solo es posible si la relación de herencia se mantiene
entre las dos clases como se indica.
¿Qué sucede entonces para una llamada de la
formar
t.insrrt (v)
que aplica el procedimiento insert, con
argumento v, al objeto adjunto a t?
Enlace dinámico significa que tal llamada
siempre usa la versión apropiada de
el procedimiento - el original si el
El objeto al que está adjunto t es una instancia de BINARY-TREE, la versión redefinida
si es una instancia de
ÁRBOL BINARIO ESPECIAL. La política inversa, enlace estático (utilizando el
declaración de b para hacer la elección),
sería un absurdo: deliberadamente
elegir la versión incorrecta de una operación.

La combinación de herencia, redeclaración, polimorfismo y dinámica.


la unión produce gran parte del poder y la flexibilidad que resultan del uso de
enfoque orientado a objetos .: Sin embargo, estos
las técnicas también pueden plantear preocupaciones de
posible mal uso: qué es evitar un
redeclaración de producir un efecto
eso es incompatible con la semántica
de los clientes tontos de la versión original
de una manera particularmente mala, especialmente en
El contexto de la vinculación dinámica? Nada, por supuesto. Ninguna técnica de diseño
es
inmune al mal uso. Pero al menos es posible ayudar a los diseñadores serios a usar el
técnica propiamente dicha; aqui el contrato
La teoría proporciona la perspectiva adecuada.
Lo que significan la redeclaración y el enlace dinámico es la capacidad de subcontratar
un
tarea; prevenir el mal uso significa garantizar que los subcontratistas respeten el
Las promesas del contratista principal en el contrato original.
Considere la situación descrita por
Figura 8. A exporta una rutina r a su
clientela. (Por simplicidad, ignoramos cualquier
argumentos para r.) Un cliente X ejecuta un
llamada
u.r
donde se declara u de tipo A. Ahora B, a
donde el polimorfismo solo resulta
de una llamada de la forma
zsome-rutina (v)
para lo cual el argumento real v es de
tipo B. Si esta última llamada está en una clase diferente
que X, el autor de X ni siquiera
sabe que puede apegarse a un
instancia de B. De hecho, puede que ni siquiera
saber sobre la existencia de una clase B.
Pero entonces el peligro es claro. Para determinar las propiedades de la llamada u.r, el
el autor de X solo puede mirar el contrato para r en A. Sin embargo, debido a la
dinámica
vinculante, A puede subcontratar la ejecución de r a B, y es el contrato de B el que
sera aplicado.
¿Cómo evitar "engañar" X en el
¿proceso? Hay dos formas en que B podría
violar las promesas de su contratista principal:
B podría fortalecer la condición previa, aumentando el riesgo de que algunas llamadas
son correctos desde el punto de vista de x (ellos
satisfacer las obligaciones originales del cliente)
no será manejado adecuadamente.
l B podría hacer la condición posterior
más débil, devolviendo un resultado menos favorable que lo prometido a X.
Nada de esto, entonces, está permitido. Pero
los cambios inversos son, por supuesto, legítimos. Una redeclaración puede debilitar el
precondición original o puede
fortalecer la postcondición. Cambios
de cualquier tipo significa que el subcontratista hace un mejor trabajo que el contratista
original, lo que no hay razón para prohibir.
Estas reglas iluminan algunas de las
propiedades fundamentales de la herencia,
redeclaración, polimorfismo y unión dinámica. Redeclaración para todos
El poder que aporta al desarrollo de software. no es una forma de convertir una rutina en
algo completamente diferente.
la versión debe seguir siendo compatible con
La especificación original. a pesar de esto
puede mejorarlo. Las reglas señaladas expresan esto precisamente.
Estas reglas deben ser aplicadas por el
48
Figura 8. Redefinición de una rutina bajo contrato.
idioma. Eiffel usa una convención simple. En una redeclaración, no está permitido
utilizar los formularios requeridos ... y garantizar ... La ausencia de una condición
previa o
cláusula posterior a la condición significa que la versión redeclarada conserva el
original
afirmación de la versión. Ya que esto es lo más
situación frecuente, el autor de la clase es
no es obligatorio escribir nada especial en
este caso. Un autor de clase que quiere
para adaptar la afirmación utilizará o
ambas formas
requiere más
new_pre
asegurar entonces
nueva publicación
que producen lo siguiente como
condición y postcondición:
nuevo prenewgre u otra condición originalgregre
Newgost y luego
original_postcondition
donde o más y luego están los
versiones no conmutativas de "o"
y operadores "y" (evaluando sus
segundo argumento solo si es necesario). En
de esta manera. Se garantiza que la nueva condición previa será más débil o igual que la
originales, y la nueva condición posterior es
garantizado para ser más fuerte o igual
a los originales.

Invariantes y
enlace dinámico

Además de las reglas sobre condiciones previas y posteriores, otra restricción vincula
las afirmaciones con la herencia:
Las invariantes siempre se transmiten a los descendientes.
Este es un resultado directo de la opinión de que
La herencia es (entre otras cosas) clasificación. Si queremos considerar cada
instancia de una clase B como siendo también un
instancia de los antepasados de R. debemos aceptar que las restricciones de coherencia
en un
el padre A también se aplica a las instancias de B.
Por ejemplo, si el invariante para un
clase TREE, que describe nodos de árbol, incluye la cláusula
child.parent = Current
expresando que el padre de un nodo
El hijo actualmente activo es el nodo en sí,
esta cláusula se aplicará automáticamente a
instancias de una clase BINARY-TREE,
que hereda de TREE. Como resultado,
la especificación del lenguaje define "el
invariante de una clase "como la afirmación
obtenido mediante la concatenación de la afirmación en la cláusula invariante de la
clase
a los invariantes de todos los padres (obtenido
recursivamente bajo esta definición) ".
Como resultado, la invariante de una clase es
siempre más fuerte o igual que el
invariantes de cada uno de sus padres.
Estas reglas conducen a una mejor comprensión de por qué sería vinculante estático,
como se dijo anteriormente, tal desastre.
Asuma nuevamente la declaración y llame
u: A:
.
u.r
donde un descendiente B de A redefine r.
Llame a rA y r ,,, las dos implementaciones.
Entonces r, debe preservar INV ,, el invariante de A, y rg debe preservar INV ,, el
invariante de B, que es más fuerte que o
igual a INV.
No hay, por supuesto, ningún requisito.
que ra preservar INV. De hecho, clase A
puede haber sido escrito mucho antes de B,
y el autor de A no necesita
saber algo sobre eventuales descendientes de esta clase.
Si LL se une dinámicamente a
una instancia de B, el enlace dinámico requiere la ejecución de rH para esta llamada.
La unión estática provocaría ra. Ya que
esta versión de la rutina no es necesaria para preservar INV, el resultado sería
producir una situación catastrófica: un objeto
de tipo B que no satisface la restricción de consistencia -la invariante -of
su propia clase En tales casos, cualquier intento
para comprender textos de software o razonar sobre su comportamiento en tiempo de
ejecución
se vuelve inútil.
Un simple ejemplo hará que la situación sea más concreta. Asume una clase
CUENTA que describe cuentas bancarias,
con los atributos mostrados en la Figura Ya
y un procedimiento para registrar un nuevo depósito
mostrado en la Figura 9b.

Con esta versión de la clase,


obtener la cuenta actual de una cuenta
el equilibrio requiere un cálculo
expresado por una función. Figura
10 muestra cómo podría aparecer la función de equilibrio, suponiendo que
Functionum apropiado en clase
LISTA DE TRANSACCIONES.
En una clase descendiente ACCOUNT1, puede ser un mejor
compensación espacio-tiempo para almacenar el
saldo actual con cada objeto de la cuenta. Esto puede ser
logrado redefiniendo el equilibrio de la función en un atributo (un
proceso que de hecho es compatible
por el idioma). Naturalmente, esto
El atributo debe ser coherente con
los demás; esto se expresa por
la invariante de ACCOUNTI,
se muestra en la Figura 11.
Para que esto funcione, sin embargo, B
debe redefinir cualquier rutina de A
que modificaron depósitos o retiros; la versión redefinida
también debe modificar el saldo
campo del objeto en consecuencia,
para mantener lo invariante.
Este es el caso, por ejemplo,
con procedimiento de registro de depósito.
Ahora suponga que tenemos el
declaración y llamada
a: CUENTA,
Un depósito de registro (1 ~ 000 ~ 000)
Si en una ejecución determinada, a
estar conectado a un objeto de tipo ACCOUNT1 en el momento de la llamada, estático
la unión significaría aplicar la versión original, A CC0 UNT del depósito de registros,
que no actualiza el campo de saldo. El resultado sería un
objeto inconsistente ACCOUNT1 y
cierto desastre

Das könnte Ihnen auch gefallen