Qué&es&un&Tipo&de&Datos& Tipos&Abstractos&de&datos& Tipos&básicos&vs&compuestos& • Básicos:& – Provistos&por&el&lenguaje& – No&sabemos&como&se&representan& – Los&accedemos&mediante&operaciones&o&usando& constantes& • Compuestos4 – Se&arman&componiendo&Xpos&básicos& • Se&puede&acceder&o&no&a&la&representación& – Se&pueden&definir&operaciones&para&manipularlos& Un&!po&define:& • Un&conjunto&de&valores& • Las&operaciones&que&puedo&realizar&sobre&estos& valores& & Ejemplos:& ? int:& &valores&=&{&…,&?2,?1,&0,&1,&2,&…},& & & &operaciones:&{+,*,?,&%,&…}& ? char:& &valores&=&{&…,&‘a’,’b’,’c’,&…},& & & & &operaciones:&{ord,&toUpperCase,…}&& ? int[]:& &valores&=&{[Int]},&& & &operaciones:{length,&[.]}&& Básicos4 int& int& 1004 float& 54 char& 2.34 ‘a’4 Compuestos4 Complejo& real:4104 Imag:454 Racional& numer:414 denom:424 Persona& nom:4Juan4 ap:4Perez4 nacido:422/4/19744 Tipos&abstractos&de&datos& • Un&Tipo4Abstracto4de4Datos4(TAD)&define&su& conjunto&de&valores&a&parXr&de&sus&operaciones&o& propiedades,&independientemente&de& implementaciones&parXculares.&& Cómo&se&uXliza&el&Xpo?&& • A&travéz&de&sus&operaciones&y&únicamente&así&& • No&se&puede&acceder&a&su&representación&interna& – Usamos&sólo&su&especificación&en&el&Tipo&Compuesto& TADs&en&Java&=&Clases&y&Objetos& “Objeto(es(una(unidad(que(encapsula(un(estado(y(4ene(un( un(determinado(comportamiento”( & Un&objeto&Xene&una&& ? Iden!dad:&para&poder&disXnguirse&de&otros&objetos& ? Comportamiento:&qué&puede&hacer.&Qué&operaciones& sabe&hacer.& ? Estado:&valores&de&sus&atributos&en&un&instante&de& Xempo&dado& • No&solo&por&convicción:&no&tenemos&otro&remedio!& El&estado&suele&estar&oculto,&solo&se&suele&interactuar&con& un&objeto&enviándole&mensajes.& TADs&en&Java&=&Clases&y&Objetos& Ejemplos& • Mecanismo&para&crear&objetos& – En&Java,&elementos&del&Xpo&que&define&la&clase& • Es&un&“molde”&que&indica&& – Qué&posibles&valores&tendra&su&estado44 – La&definición&de&los&métodos&(operaciones)& implemetará&& • Esto&determina&qué&mensajes&sabra&responder& & En&Java,&implementamos&TADs&definiendo&clases.& Estos&luego&instancian&objetos&que&solo&pueden& ser&accedidos&mediante&operaciones.& • Tipo&Complejo& – Parte&Real& – Parte&Imaginaria& • Operaciones& Tipo Complejo { observador Real(c: Complejo): Float; observador Imag(c: Complejo): Float; problema crear(r,i: Float)= res: Complejo { asegura Real(res) = r; asegura Imag(res) = I; } problema sumar(c1,c2: Comple) = res: Complejo { … } } &crear&sumar,&restar,&…&& Implementación&del&Xpo&Complejo& Nombre&del&Xpo&& class Complejo { private float real, imag; Representación&interna&(estado)& public Complejo(float r,float i) { this.real = r; this.imag = i; } public Complejo() { real = 0; imag = 0; } public void sumar(Complejo c) { this.real = this.real + c.real; this.imag = this.imag + c.imag; } public float getReal(){ return real;} public float getImag() { return imag;} } Constructor&(crea&instancias&del& Xpo&con&valores&válidos)& “this”&representa&el&objeto&que& se&está&operando.&& Cuando&se&accede&a&un&campo&o& método,&“this”&se&asume&si&no&se& indica&otra&cosa.& Operaciones&del&Xpo&(algunas& pueden&modificar&el&estado)& Encapsulamiento&y&Ocultamiento& • Acceso&a&representación&interna&en&muy&pocos& métodos& – Muy&cerca&una&de&otra&en&el&código&(misma&clase)& – Si&cambia&la&representación&interna,&solamente&hay&que& cambiar&esas&funciones& & • Si&no&pedimos&al&lenguaje&esta&protección& – Riesgo&de&usar&representación&interna&en&otros&programas& • Nosotros&mismos&u&otro&programador& – Cuando&la&cambiemos,&dejan&de&funcionar&& – Y&hay&que&modificar&uno&por&uno!!!& Funciones&proyectoras&(para& mostrar&parte&del&estado)& Instanciando&objetos& La&instrucción&new4instancia&objetos&de&una&clase,&llamando&a& un&constructor&y&devolviendo&una&referencia&al&nuevo&objeto.&& & c1& Complejo& Ejemplos:& real:404 Complejo c1 = new Complejo(); Complejo c2 = new Complejo(10,5); Imag:404 c2& Complejo& Tipo&Racional& • Queremos&un&componente&que&nos&maneje& números&racionales&de&forma&precisa& • Ejemplos&& – 1&,&1/2&,&2/3&,&23/10,&2/4& Racional& numer:414 denom:414 real:4104 Imag:454 & ?&Los&parámetros&determinan&a&que&constructor&llamar& c1.sumar(c2); ?&En&una&invocación,&el&primer&parámetro,&se&lo&llama&receptor& (va&&a&ser&el&“this”&en&el&método)& • Operaciones&deseables& – Crear& – Sumar,&Restar,&MulXplicar& – …& Racional& numer:424 denom:434 Racional& numer:414 denom:424 Representando&racionales& • Idea:&usar&&numerador&y&denominador&& class Racional • Listo?& { private private public public public public public public public int int int int numerador; denominador; getNumerador(); getDenominador(); Racional(int n, int d); bool equals(Racional r); void sumar(Racional r); void restar(Racional r); void multiplicar(Racional r); Representando&racionales& • Conviene&tener&una&&forma&uniforme&de& representar& class Racional { private int numerador; private int denominador; // invRep mcd(numerador, denominador)==1 denominador!=0; public public public public public public public } ! ! No!&Ningun&racional&es&representado&por&n/0;& Diferentes&representaciones&para&el&mismo&racional!& ! (1,2),(2,4),(3,6)…&& Representando&racionales& Invariante4 de4 Representación:& predicado& que& indica& una& propiedad&fundamental&que&debe&mantenerse&en&el&objeto& ahora&(2,4)&& y&(x,0)&&no&son&& racionales!& && int getNumerador(); int getDenominador(); Racional(int n, int d); bool equals(Racional r); void sumar(Racional r); void restar(Racional r); void multiplicar(Racional r); } ! Aun&se&“cuelan”&(1,?2),&(?1,2)& Invariantes&de&Representación&(o&Clase)& • Propiedad&fundamental&que&debe&cumplir&un& objeto&& – Para&el&que&el&objeto&esté&en&un&estado&consistente& class Racional { private int numerador; private int denominador; // invRep mcd(numerador, denominador)==1 public public public public public public public } && denominador>0; • Fuera&de&la&clase&debe&ser&siempre&verdadero&& – Pensar&en&un&Xpo&compuesto& int getNumerador(); int getDenominador(); Racional(int n, int d); bool equals(Racional r); void sumar(Racional r); void restar(Racional r); void multiplicar(Racional r); • Internamente& – Puede&ser&asumido&por&los&métodos&de&la&clase& – Debe&ser&mantenido&también&por&ellos& Invariantes&de&TC&vs.&invRep& • Un&invariante4de4un4Tipo4Compuesto4es& siempre&verdadero& Implementación&del&constructor& • Algún&problema&con&esto?& – En&los&asegura&de&los&problemas&tenemos&que&ver& si&invariante+asegura&no&es&una&contradicción& & class Racional { private int numerador; private int denominador; // invRep mcd(numerador, denominador)==1 && denominador>0; • Un&invRep&es&verdadero& public Racional(int n, int d){ – Siempre&afuera4de4la4clase4 – Dentro&de&la&clase: && numerador = n; denominador = d; } • Se&asume&verdadero&en&la&entrada&de&los&métodos& • Tenemos&que&garan!zar&que&sea&verdadero&a&la&salida& } Implementación&del&constructor& • Algún&problema&con&esto?& class Racional { private int numerador; private int denominador; // invRep mcd(numerador, denominador)==1&& denominador>0; public Racional(int n, int d){ // requiere d>0; numerador = n; denominador = d; } } Implementación&del&constructor& • Ahora&sí& class Racional { private int numerador; private int denominador; // invRep mcd(numerador, denominador)==1 && denominador>0; public Racional(int n, int d){ // requiere d!=0; int mcd = Math.gcd(n,d); numerador = Math.sign(d)*n/mcd; denominador = Math.abs(d)/mcd; } } Implementando&operaciones& Invariantes&y&abstracción& Podemos&asumir&el&invariante,&pero&debemos&restablecerlo& class Racional { private int numerador; private int denominador; // invRep mcd(numerador, denominador)==1 && denominador>0; // requiere r no es sinónimo de this public void suma(Racional r){ This.inv& numerador = numerador*r.denominador + r.numerador * denominador; This.inv& denominador = denominador*r.denominador; This.inv& This.inv?& } int mcd = Math.gcd(numerador,denominador); This.inv& } numerador = numerador/mcd; This.inv& denominador = denominador/mcd; This.inv& } } & class C { private int x,y; public dividir(){ // requiere y!=0; x = 100/y; } } r.inv& ?& Helper&methods& • Los&invariantes&deben&valer&a&la&entrada&y&salida&del&método& – A&veces&esto&es&muy&restricXvo& • Helper&methods:&& – Tienen&que&ser&privados& – No&requieren&que&valga&el&invariante& – Ni&establecer&el&invariante& class Racional { private int numerador; private int denominador; invRep mcd(numerador, denominador)== 1 && denominador>0; public void suma(Racional r){ numerador n = numerador*r.denominador + r.numerador * denominador; denominador =denominador*r.denominador; reconstuir(); } } • Pregunta:&Qué&problema&le&ven&a&este&método?& // helper: private void reconstruir() { int mcd = Math.gcd(numerador, denominador); numerador = numerador/mcd; denominador = denominador/mcd; } & ! Mejor&este!& class C { private int x,y; // invRep y!=0; public dividir(){ x = 100/y; } } Resumen& • Un&objeto&“compuesto”&necesita&estar&en&un& estado&consistente& • Esta&garanqa&puede&lograrse&mediante&el&uso& de&invariantes& • Mantener&el&invariante&requiere&un&esfuerzo& del&programador&& – Pero&facilita&implementación&de&código&que&sea& correcto&porque&se&debe&ser&consciente&de&&las& presunciones&que&se&realizan& Probrando&corrección& • Para&ver&si&una&clase&C&es&una&implementación& correcta&de&un&Tipo&compuesto&T&tenemos&que& ver&que:& 1. El&invRep&se&manXene&en&todos&los&métodos& públicos& ?&Notar&que&se&especifica&usando&los&campos4de4la4clase4 4 2. Los&métodos&&públicos&cumplen&con&su&contrato& &&&&&?&Notar&que&se&especifica&usando&los&observadores4del4 !po4 QPQ mcd(numerador, denominador)== 1 && denominador>0& // implica numerador == numerador@e1*r.denominador + r.numerador *denominador@e1 && denominador ==denominador@e1 *r.denominador int mcd = Math.gcd(numerador,denominador); // estado e4 // vale this==this@e3 && mcd==mcd(numerador@e3,denominador@e3) numerador = numerador/mcd; // estado e5 // vale numerador == numerador@e4/mcd@e4 && denominador == denominador@e4 && mcd==mcd@e4 denominador = denominador/mcd; // estado e6 // vale denominador == denominador@e5/mcd && numerador == numerador@e5 && mcd==mcd@e5 … (me&salteo&algunos&implicas) // implica numerador == numerador@e3/mcd(numerador@e3,denominador@e3) && denominador == denominador@e3/mcd(numerador@e3,denominador@e3) // implica denominador>0 (denominador@e3>0&y&mcd&es&posiXvo)& // implica mcd(numerador,denominador)==1 (ambos&fueron&divididos&por&el&mcd)& // implica invRep(this)4 } & Preservación&de&invRep& (asumimos&que&el&método&no&modifica&r,&solo&this)& public void suma(Racional r) { Recordar:&“this”&es&opcional&dentro&de&un& método&de&la&clase&si&lo&que&se&referencia& es&el&objeto&receptor&del&mensaje& //vale invRep(this):mcd(numerador, denominador)== 1 && denominador>0; //vale invRep(r):mcd(r.numerador, r.denominador)==1 && r.denominador>0; // estado e1 numerador = numerador*r.denominador + r.numerador * denominador; // estado e2 // vale numerador == numerador@e1*r.denominador + r.numerador * denominador@e1 && denominador == denominador@e1 denominador =denominador*r.denominador; // estado e3 // vale denominador ==denominador@e2 *r.denominador && numerador==numerador@e2 // implica numerador == numerador@e1*r.denominador + r.numerador*denominador@e1 && denominador ==denominador@e1 *r.denominador & Probando&corrección& Tipo Racional{ observador numerR(r: Racional): Int; observador denomR(r: Racional): Int; invariante denomR(r) > 0; invariante mcd(numerR(r), denomR(r)) == 1; } problema crearR(n, d: Int) = rac: Racional { requiere d≠0; asegura numerR(rac) * d == denomR(rac) * n; } class Racional { private int numerador, denominador; // invRep mcd(numerador, denominador)==1 && denominador>0; public Racional(int n, int d){ // requiere d>0; int mcd = Math.gcd(n,d); numerador = n/mcd; denominador = Math.abs(d)/mcd; } } ¿Cómo&probamos&que&es& correcto?& ¿Cómo&relacionamos&el&estado& de&un&objeto&de&Xpo&Racional& (numerador,&denominador)&con&los& observadores&del&Xpo& compuesto&Racional&(numerR,& denomR)?& Predicado&de&Abstracción& • abs(this:&clase,&e:&TipoCompuesto):&predicado&que& relaciona&una&instancia&concreta&de&una&clase&con&un& elemento&de&su&&Xpo&de&especificación.& class Racional { private int numerador, denominador; // invRep: mcd(numerador, denominador)==1 && denominador>0; // abs(this,rac): numerR(rac)==numerador && denomR(rac)==denominador public Racional(int n, int d){ // requiere d!=0; int mcd = Math.gcd(n,d); numerador = Math.sign(d)*n/mcd; denominador = Math.abs(d)/mcd; } } Demostrando& problema crearR(n, d: Int) = rac: Racional { requiere d≠0; asegura numerR(rac) * d == denomR(rac) * n; } class Racional { private int numerador, denominador; // invRep: mcd(numerador, denominador)==1 && denominador>0; // abs(this,rac): numerR(rac)==numerador && denomR(rac)==denominador public Racional(int n, int d){ // requiere d!=0; (notar&que&es&&d≠0 equivalente,&Xene&que&ser&mas&fuerte) int mcd = Math.gcd(n,d); numerador = Math.sign(d)*n/mcd; denominador = Math.abs(d)/mcd; … //implica numerador==sign(d)*n/mcd(n,d) && denominador==|d|/mcd(n,d) (ahora&usando&que&mcd!=0&y&&abs(this,rac)&) //implica numerR(rac)==sign(d)*n/mcd(n,d) && denomR(rac)==|d|/mcd(n,d) (divimos&numerR/denomR) //implica numerR(rac)/denomR(rac)==(sign(d)*n/mcd(n,d))*(mcd(n,d)/|d|) (simplificamos&y&usamos&que&d≠0) // implica numerR(rac) * |d| == denomR(rac) * sign(d)*n; // implica numerR(rac) * |d|/sign(d) == denomR(rac) *n; // implica numerR(rac) * d == denomR(rac) * n; Abs&vs.&InvRep& InvRep:&determina&cuándo&el&estado&de&un& objeto&es&consistente& – Es&una&propiedad&solo&de&la&representación& – Por&eso&debe&ser&mantenido& Abs:&explica&cómo&la&implementación&es&una& solución&del&Xpo&compuesto& – Conecta&representación&interna&con&observadores& – Asume&que&tanto&InvRep4como&invariante4del4TC& son&verdaderos& Ejercicio& • Especificar&el&problema&sumar&&y&demostrar&que&este&&método&es& correcto.& • Hint:&Aprovechar&parte&de&la&prueba&de&conservación&de&invRep& class Racional { private int numerador, denominador; // invRep: mcd(numerador, denominador)==1 && denominador>0; // abs(this,rac): numerR(rac)==numerador && denomR(rac)==denominador public void suma(Racional r){ numerador = numerador*r.denominador + r.numerador * denominador; denominador = denominador*r.denominador; reconstruir(); } } Representación&!=&Especificación& Ejemplo:&Lista&con&Lista&de&arrays& Estamos& obligados& a& implementar& solo4 los& problemas&definidos&en&el&Xpo&compuesto& – No&hay&necesidad&de&implementar&los& observadores!& – Los&campos&elegidos&para&implementar&no&Xenen& porque&ser&iguales&a&los&observadores& – Tampoco&hay&que&implementar&funciones&que&los& calculen& El& predicado& abs& se& ocupa& de& explicar& como& relacionar&representación&con&especificación.& 0" Cual&es&el&Abs?& 1" 2" 3" 4" 5" 6" 7" 8" Tipo Lista<T>{ observador elems(l: Lista<T>): [T]; } problema iesimo(l: Lista<T>, i: Int) = res: T { requiere |elems(l)|>0 && 0<=i<|elems(l)|; asegura res = elems(l)[i] } problema agregar(l: Lista<T>, e: T) { modifica l; asegura elems(l) = elems(l@pre) ++ [e]; } 04 14 24 34 44 54 64 74 104 114 124 134 144 count=15,&nextPos=5& class Lista<T> { private LinkedList<T[]> L; private int count, nextPos; // invRep count = 10*(|L|-1)+nextPos && todos([|b|==10| b<-L]) && 0<=nextPos<10; public Lista(){ nextPos=0; count= 0; L = new LinkedList<T>(); L.add(new T[10]); } 9" Ejercicio& 10" 11" 12" 13" 14" count=15,*nextPos=5* abs(this, l) : |elems(l)|==count && todos([elems(l)[i]==this.L[i div 10][i % 10] | i <- [0..this.count) ]) class Lista<T> { private LinkedList<T[]> L; private int count, nextPos; // invRep count = 10*(|L|-1)+nextPos && todos([|b|==10| b<-L]) && 0<=nextPos<10; public T iesimo(int i){ // requiere count>0 && 0<=i<count int pos = i div 10; T res = L.get(pos)[i % 10]; } } • Implementar&el&método&agregar& Tipo Lista<T>{ observador elems(l: Lista<T>): [T]; } problema iesimo(l: Lista<T>, i: Int) = res: T { requiere |elems(l)|>0 && 0<=i<|elems(l)|; asegura res = elems(l)[i] } problema agregar(l: Lista<T>, e: T) { modifica l; asegura elems(l) = elems(l@pre) ++ [e]; } 04 14 24 34 44 54 64 74 count==8,&nextPos==8,&|L|==1& 84 94 Ejercicio& • Implementar&el&método&agregar& Ejercicio& • Implementar&el&método&agregar& Tipo Lista<T>{ observador elems(l: Lista<T>): [T]; } problema iesimo(l: Lista<T>, i: Int) = res: T { requiere |elems(l)|>0 && 0<=i<|elems(l)|; asegura res = elems(l)[i] } problema agregar(l: Lista<T>, e: T) { modifica l; asegura elems(l) = elems(l@pre) ++ [e]; } l.agregar(8)& 04 14 24 34 Tipo Lista<T>{ observador elems(l: Lista<T>): [T]; } problema iesimo(l: Lista<T>, i: Int) = res: T { requiere |elems(l)|>0 && 0<=i<|elems(l)|; asegura res = elems(l)[i] } problema agregar(l: Lista<T>, e: T) { modifica l; asegura elems(l) = elems(l@pre) ++ [e]; } l.agregar(9)& 44 54 64 74 84 count==9,&nextPos==9,&|L|==1& Conclusiones& • TADs:&Permiten&definir&Xpos&de&acuerdo&al& comportamiendo,&ocultando&su&estructura& interna& • En&Java&los&implementamos&usando&clases.& • Conceptos&claves:& – Invariante4de4Representación:&Para&que&los& objetos&sean&consistentes& – Predicado4de4abstracción:&conectan&la& especificación&del&TAD&con&su&implementación& 04 14 24 34 44 54 64 74 84 94 count==10,&nextPos=0,&|L|==2& Especificación&de&Contratos&en&Componentes& • Un&componente&implementa&alguna&enXdad&o& elemento&importante&en&nuestra&solución& – Componente&=&conjunto&de&clases& – Clase&=&conjunto&de&métodos&que&implementan&un& TAD& • Contratos& – Por&cada&método:&&Requiere,&Asegura4 – A&nivel&clase:&invRep& – A&nivel&componente:&sistemas&de&ownerships&+& invariantes&conectados&entre&objetos& Metodologías&de&uso&de&invariantes& • Problemas&potenciales& – Exponer&la&representación& – Reentrada& • SemánXcas&de&invariantes&que&tratan&de&lidiar& con&esto& – Obligar&a&restablecer&el&invariante&en&cada&llamada& – Ownership&type&system& • Se&busca&que&el&invariante&siempre&valga&al&inicio& de&un&método& Preguntas&