Está en la página 1de 241

C++

STL,
Plantillas,
Excepciones,
Roles y Objetos.

por

Ricardo Devis Botella


INFOPLUS, S.L.
devis@ieee.org
Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 2
A Consol,
Richi y Andrea.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 3
AGRADECIMIENTOS

Este libro es el resultado de casi dos años de trabajo, en los cuales he añadido,
corregido, modificado y aun eliminado capítulos enteros. En tal período han cabi-
do artículos, conferencias, mesas redondas, intercambios de correo y cientos de
horas de lectura técnica; como también muchas personas que han influido directa
o indirectamente en el trabajo que el lector tiene ahora en sus manos. Los amigos
y colegas de APTO2 (Juan Manuel Cueva, César Pérez-Chirinos, Miguel Katrib,
Luis Joyanes, Pablo Peláez y otros muchos) han contribuido con sus inestimables
ideas y su espíritu crítico, en las diversas conferencias en que hemos coincidido
(así como en larguísimas conversaciones telefónicas), a refinar algunos de los
puntos expuestos en el texto. Ha resultado valiosísima también la colaboración de
muchos anónimos lectores (ya no tan anónimos) que mandaron correos con suge-
rencias y críticas. Antonio Vázquez corrigió el texto con una celeridad y diligencia
impresionantes, y José Bernabéu buscó un hueco entre viaje y viaje para leerlo:
mi gratitud para ambos. A Daniel Alonso le debo un mucho de amabilidad y ayu-
da. A María Teresa Gómez-Mascaraque, de Editorial Paraninfo, quisiera agrade-
cerle, por fin, su paciencia con un original que ha sufrido retraso tras retraso.

Mi familia merece una mención aparte: mis hijos, Ricardo y Andrea, se han acos-
tumbrado a que los besara entre párrafo y párrafo, y para mi esposa, Consol, es
como si me hubiera trasladado durante mucho tiempo a otro huso horario. Al me-
nos me ha librado de pasear al perro, Spock, durante algunos meses.

Espero, en definitiva, que el lector disfrute tanto leyendo este libro como yo disfru-
té escribiéndolo.

Alicante, julio 1996.


Ricardo Devis Botella

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 4
ÍNDICE
i
AGRADECIMIENTOS................................................................................................................................................. 4

ÍNDICE............................................................................................................................................................................ 5

INTRODUCCIÓN.......................................................................................................................................................12
¿A QUIÉN VA DIRIGIDO ESTE LIBRO?.............................................................................................................12
SOBRE EL AUTOR.................................................................................................................................................13
UNA VISIÓN PANORÁMICA ....................................................................................................................................13
Capítulo 1 - Objetos: Hitos, Mitos y Ritos...................................................................................................13
Capítulo 2 - Contenedores en C++ .............................................................................................................14
Capítulo 3 - C++ STL: La Biblioteca Estándar de Plantillas en C++.................................................14
Capítulo 4 - Manejo de Excepciones en C++............................................................................................14
Capítulo 5 - Asignación en C++ ..................................................................................................................15
Capítulo 6 - Patrones de Diseño: La Calidad Sin Nombre .....................................................................15
Capítulo 7 - Roles y Objetos en C++...........................................................................................................15
Capítulo 8 - Bases de Datos Orientadas-a-Objetos...................................................................................16
Capítulo 9 - Consideraciones Prácticas.....................................................................................................16
Capítulo 10 - Gestión de Proyectos Orientados-a-Objetos.....................................................................16
Capítulo 11 - Modelos de Roles: El Método Ooram.................................................................................16
Capítulo 12 - Bibliografía Comentada .......................................................................................................17
GARANTÍA .............................................................................................................................................................17
OBJETOS: HITOS, MITOS Y RITOS ...................................................................................................................18
MITO 1: UN LENGUAJE ORIENTADO-A-OBJETOS “PURO” ES MEJOR QUE OTRO “HÍBRIDO”......................18
MITO 2: PARA APRENDER A USAR ADECUADAMENTE C++ HAY QUE EMPEZAR POR SMALLTALK. ......19
MITO 3: C++ ES MÁS FÁCIL DE ASIMILAR PARA LOS PROGRAMADORES DE C. ............................................19
MITO 4: LAS HERRAMIENTAS OOCASE INTEGRADAS GENERAN CÓDIGO ORIENTADO-A-OBJETOS
FIABLE. .....................................................................................................................................................................20
MITO 5: LA HERENCIA ES UNA CUALIDAD ESENCIAL DE LOS SISTEMAS ORIENTADOS-A-OBJETOS........20
MITO 6: LA HERENCIA ES UN MECANISMO INDISPENSABLE EN LOS LENGUAJES DE PROGRAMACIÓN
ORIENT ADOS-A-OBJETOS.......................................................................................................................................21
MITO 7: LA HERENCIA ÚNICAMENTE SE PUEDE APLICAR CUANDO SE DA UNA RELACIÓN “ES-UN”....21
MITO 8: EXISTEN DEMASIADAS METODOLOGÍAS DE ANÁLISIS Y DISEÑO ORIENTADAS-A-OBJETOS.....22
MITO 9: CADA EMPRESA DEBE ADAPTAR A SU MANERA LOS MÉTODOS EXISTENTES PARA
CONSTRUIRSE UNO “A MEDIDA”..........................................................................................................................22
MITO 10: LO PRIMERO QUE HAY QUE HACER ES CREAR UNA BIBLIOTECA DE CLASES REUTILIZABLES.23
CONTENEDORES Y PLANTILLAS.......................................................................................................................24
DISEÑO DE CONTENEDORES.............................................................................................................................25

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 5
LA OPCIÓN INGENUA: CUESTIÓN DE ÁMBITOS.........................................................................................26
LA TRAMPA DEL PREPROCESADOR..............................................................................................................27
CUANDO LA HERENCIA ES DISPUTADA ......................................................................................................30
HERENCIA MÚLTIPLE: ¿MÁS DE LO MISMO?..............................................................................................41
ITERADORES..........................................................................................................................................................42
LAS PLANTILLAS: CON HERENCIA, SIN ELLA O A SU PESAR................................................................43
C++ STL.......................................................................................................................................................................46
ESTRUCTURA DE LA BIBLIOTECA..................................................................................................................47
CONTENEDORES ...................................................................................................................................................48
ITERADORES..........................................................................................................................................................50
ALGORITMOS........................................................................................................................................................56
¿FUNCIONES-OBJETO U OBJETOS-FUNCIONES?.........................................................................................57
DE LOS PELIGROS DE LA EXTENSIBILIDAD.................................................................................................59
LEVES CRÍTICAS...................................................................................................................................................62
APRENDIZAJE Y DOCUMENTACIÓN..............................................................................................................63
REFERENCIAS DIRECTAS...................................................................................................................................64
MANEJO DE EXCEPCIONES EN C++...................................................................................................................65
LOS CÓDIGOS DE ERROR....................................................................................................................................65
CONCEPTOS BÁSICOS.........................................................................................................................................68
MANOS A LA OBRA ............................................................................................................................................70
CLASES DE EXCEPCIONES .................................................................................................................................73
JERARQUÍAS DE CLASES DE EXCEPCIONES ................................................................................................73
EL DESBOBINADO DE LA PILA.........................................................................................................................74
CONSISTENCIA DE ESTADOS...........................................................................................................................75
ADQUISICIÓN DE RECURSOS VÍA INICIALIZACIÓN..................................................................................76
ESPECIFICACIÓN DE INTERFACES ..................................................................................................................78
EL FINAL DE LA CUERDA ..................................................................................................................................79
VULNERACIÓN DE LA ESPECIFICACIÓN DE EXCEPCIONES ....................................................................80
CONCLUSIONES ....................................................................................................................................................81
REFERENCIAS DIRECTAS...................................................................................................................................81
ASIGNACIÓN EN C++..............................................................................................................................................83
CONCEPTOS BÁSICOS.........................................................................................................................................83
EL OPERADOR DE ASIGNACIÓN POR DEFECTO..........................................................................................84
LOS LÍMITES EXPLÍCITOS DE LA ASIGNACIÓN IMPLÍCITA....................................................................85
LA COPIA DE UN PUNTERO GENERA ... ¡OTRO PUNTERO! ......................................................................87
ASIGNACIÓN NO ES INICIALIZACIÓN...........................................................................................................87
EL OPERADOR POR DEFECTO RESULTA DEFECTUOSO............................................................................88
EL OPERADOR DE ASIGNACIÓN EXPLÍCITO................................................................................................89
DESIGNACIÓN NO ES ASIGNACIÓN................................................................................................................90
LO QUE LA ASIGNACIÓN PRODUCE...............................................................................................................91
CONVERSIÓN, CONSTRUCCIÓN Y ASIGNACIÓN.........................................................................................92
LA ASIGNACIÓN EN JERARQUÍAS DE HERENCIA......................................................................................93
ASIGNACIÓN NO ES TRANSMUTACIÓN.......................................................................................................93
RECURSIVIDAD EXPLÍCITA: CÉSAR O NADA..............................................................................................94
CUIDADO CON LA AUTO-ASIGNACIÓN .......................................................................................................95
IDENTIDAD, IGUALDAD Y ... ¿FRATERNIDAD? ..........................................................................................97
SOBRE LA ASIGNACIÓN BIDIRECCIONAL....................................................................................................98
POLIMORFISMO EN ASIGNACIÓN ................................................................................................................100
CUANDO LA ASIGNACIÓN ES INDESEABLE..............................................................................................101
LA ASIGNACIÓN CON CLASES BASE VIRTUALES ...................................................................................102

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 6
ALGUNOS CONFUSOS CONSEJOS..................................................................................................................103
REFERENCIAS DIRECTAS.................................................................................................................................103
PATRONES DE DISEÑO........................................................................................................................................105
LA CALIDAD SIN NOMBRE .............................................................................................................................105
PATRONES DE DISEÑO SOFTWARE .............................................................................................................106
CATÁLOGOS DE PATRONES ...........................................................................................................................108
¿PATRONES ORIENTADOS-A-OBJETOS?.....................................................................................................109
TELEPREDICADORES DE SOFTWARE ..........................................................................................................109
PATRONES ELECTRÓNICOS ............................................................................................................................110
ROLES Y OBJETOS EN C++ ................................................................................................................................113
CLASES DE ROLES..............................................................................................................................................114
UNA BUENA HERENCIA ¿LO ARREGLA TODO? ........................................................................................115
DINÁMICA DE CLASES.....................................................................................................................................116
LA HERENCIA INNECESARIA .........................................................................................................................117
DIVIDE Y HEREDARÁS......................................................................................................................................120
EL PATRON DE ROLES INTERCAMBIABLES...............................................................................................123
EL PATRON DE ESTADOS ................................................................................................................................124
EL PATRÓN PROXY DE SUBROGACIÓN.......................................................................................................125
CONVERSIONES E INDIRECCIÓN....................................................................................................................126
CONCURRENCIA Y SECUENCIACIÓN DE ROLES .......................................................................................129
EN LA ASOCIACIÓN ESTÁ LA FUERZA.......................................................................................................130
LA CITA DEL DESASOSIEGO ...........................................................................................................................135
REFERENCIAS DIRECTAS.................................................................................................................................135
BASES DE DATOS ORIENTADAS-A-OBJETOS ............................................................................................137
DEGRADADO DE BASES DE DATOS.............................................................................................................137
LOS DESEOS DEL PROGRAMADOR...............................................................................................................139
PERSISTENCIA, TRANSITORIEDAD Y MATRICES ....................................................................................140
ORTOGONALIDAD DE PERSISTENCIA Y TIPO...........................................................................................140
CRÍTICA DE LA RAZÓN PRÁCTICA ..............................................................................................................142
INTERNET: ¿CÓMO NO? ....................................................................................................................................142
CONSIDERACIONES PRÁCTICAS ....................................................................................................................144
PANORAMA PARA MATAR...........................................................................................................................145
REFLEXIONES REBAJADAS.............................................................................................................................146
Distribución Jerárquica Cósmica de Clases............................................................................................146
El Paradigma M2VC.....................................................................................................................................147
Arquitectura Gráfica MultiModal..............................................................................................................148
Gestión de Concurrencias............................................................................................................................151
Gestión de Objetos a través de Vistas........................................................................................................155
Incidencias de Usuarios...............................................................................................................................160
Actualización de Vistas ................................................................................................................................162
Seguridad del Sistema ..................................................................................................................................166
Gestión de Objetos mediante Colecciones................................................................................................171
Esquema de Consultas..................................................................................................................................172
Gestión de Dependencias entre Vistas.......................................................................................................178
GESTIÓN DE PROYECTOS ..................................................................................................................................181
EL SÍNDROME DE ESTOCOLMO.....................................................................................................................181
ANÁLISIS Y DISEÑO ORIENTADOS-A-OBJETOS.......................................................................................182
MÉTODOS COMERCIALES DE OOA/OOD....................................................................................................182

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 7
SOBRE LISTAS Y COMPARACIONES ............................................................................................................184
CÉSAR O NADA ..................................................................................................................................................185
LA SOLUCIÓN ECLÉCTICA...............................................................................................................................186
HERRAMIENTAS OOCASE...............................................................................................................................187
GESTIÓN DE PROYECTOS ORIENTADOS-A-OBJETOS..............................................................................187
SÍSIFO REDIVIVO.................................................................................................................................................188
MODELOS DE ROLES ...........................................................................................................................................189
ESCENARIOS Y APREHENSIONES ..................................................................................................................190
TRAS DIVIDIR HAY QUE MULTIPLICAR......................................................................................................192
EL LIBRO................................................................................................................................................................194
IDEAS PRINCIPALES ..........................................................................................................................................194
MODELADO DE ROLES .....................................................................................................................................195
SÍNTESIS DE MODELOS DE ROLES ................................................................................................................195
PUENTE A LA IMPLEMENTACIÓN................................................................................................................196
CREACIÓN DE COMPONENTES REUTILIZABLES ......................................................................................196
LEA, LECTOR: LEA..............................................................................................................................................197
REFERENCIAS DIRECTAS.................................................................................................................................197
BIBLIOGRAFÍA COMENTADA ...........................................................................................................................201
LIBROS BÁSICOS SOBRE C++..........................................................................................................................201
The C++ Workbook, por Richard S. Wiener & Lewis J. Pinson, 1990, Addison-Wesley, 0-201-
50930-X, 349 pág. .........................................................................................................................................201
A C++ Toolkit, por Jonathan S. Shapiro, 1990, Prentice Hall............................................................202
On to C++, por Patrick Henry Winston, 1994, Addison-Wesley, 0-201-58043-8.............................203
C++ Primer, 2 nd Edition, por Stanley B. Lippman, 1991, Addison-Wesley, 0-201-54848-8, 614 pág.
...........................................................................................................................................................................203
Mastering Object-Oriented Design in C++, por Cay S. Horstmann, 1995, John Wiley & Sons, 0-
471-59484-9. ..................................................................................................................................................204
Object-Oriented Design for C++, por Tsvi Bar-David, 1993, Prentice-Hall, 0-13-630260-2. .......204
The C++ Programming Language, 2 nd Edition, por Bjarne Stroustrup, 1991, Addison-Wesley, 0-
201-53992-6, 669 pág. .................................................................................................................................205
The Annotated C++ Reference Manual, por Margaret A. Ellis & Bjarne Stroustrup, 1990,
Addison-Wesley, 0-201-51459-1, 447 pág................................................................................................206
The Design and Evolution of C++, por Bjarne Stroustrup, 1994, Addison-Wesley, 0-201-54330-3.206
Algorithms in C++, por Robert Sedgewick, 1992, Addison-Wesley, 0-201-51059-6.......................207
LIBROS DE ESTILO EN C++...............................................................................................................................207
Effective C++: 50 Specific Ways to Improve Your Programs and Designs, por Scott Meyers, 1992,
Addison-Wesley, 0-201-56364-9.................................................................................................................207
C++ Programming Style, por Tom Cargill, 1992, Prentice Hall, 0-201-56365-7............................208
C++ Programming Guidelines, por Thomas Plum & Dan Saks, 1991, Plum Hall, 0-911537-10-4,
274 pág............................................................................................................................................................208
Taligent’s Guide To Designing Programs: Well-Mannered Object-Oriented Design in C++, por
Taligent Inc., 1994, Addison-Wesley, 0-201-40888-0.............................................................................209
LIBROS DE PROGRAMACIÓN MEDIA Y AVANZADA EN C++...............................................................210
C++ IOStreams Handbook, por Steve Teale, 1993, Addison-Wesley, 0-201-59641-5.....................210
Advanced C++ Programming Styles and Idioms, por James O. Coplien, 1992, Addison-Wesley, 0-
201-54855-0. ..................................................................................................................................................210
The Power of Frameworks for Windows and OS/2 Developers, por Taligent Inc., 1995, Addison-
Wesley, 0-201-48348-3. ................................................................................................................................211
Data Abstraction and Object-Oriented Programming in C++, por Keith E. Gorlen, Sanford M.
Orlow & Perry S. Plexico, 1990, John Wiley & Sons, 0-471-92346-X, 403 pág...............................212

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 8
C++ Strategies and Tactics, por Robert B. Murray, 1993, Addison-Wesley, 0-201-56382-7, 273
pág....................................................................................................................................................................213
Designing and Coding Reusable C++, por Martin D. Carroll y Margaret A. Ellis, 1995, Addison-
Wesley, 0-201-51284-X.................................................................................................................................213
Taming C++: Pattern Classes and Persistence for Large Projects, por Jiri Soukup, 1994, Addison-
Wesley, 0-201-52826-6. ................................................................................................................................214
LIBROS SOBRE SMALLTALK.................................................................................................................................214
Programación Orientada a Objetos: Aplicaciones con Smalltalk, Angel Morales & Francisco J.
Segovia, 1993, Paraninfo, 84-283-2019-5. ..............................................................................................215
Smalltalk Programming for Windows, Dan Shafer con Scott Herndon y Laurence Rozier, 1993,
Prisma Publishing, 1-55959-237-5............................................................................................................215
Object Oriented Programming, Peter Coad & Jill Nicola, 1993, Prentice-Hall, 0-13-032616-X,
con disquete del código C++ y Smalltalk................................................................................................215
Discovering Smalltalk, Wilf LaLonde, 1994, Benjamin Cummings, 0-8053-2720-7.........................216
Smalltalk -80: The Language, Adele Goldberg & David Robson, 1989, Addison-Wesley, 0-201-
13688-0............................................................................................................................................................216
Smalltalk -80: The Interactive Programming Environment, Adele Goldberg, 1984, Addison-Wesley,
0-201-11372-4................................................................................................................................................217
Inside Smalltalk I, Wilf LaLonde & John Pugh, 1990, Prentice-Hall, 0-13-468414-1. ...................217
Inside Smalltalk II, Wilf LaLonde & John Pugh, 1990, Prentice-Hall, 0-13-465964-3...................217
Smalltalk/V: Practice and Experience, Wilf LaLonde & John Pugh, 1994, Prentice-Hall, 0-13-
814039-1, con disquete. ...............................................................................................................................218
IBM Smalltalk: The Language, por David N. Smith, 1994, Addison-Wesley, 0-8053-0908-X........218
Smalltalk Best Practice Patterns - Volume 1: Coding, por Kent Beck................................................218
LIBROS SOBRE JAVA .........................................................................................................................................219
Hooked on Java: Creating Hot Web Sites with Java Applets, por Arthur van Hoff, Sami Shaio y
Orca Starbuck, 1995, Addison-Wesley, 0-201-48837-X, con CD-ROM. .............................................219
Mecklermedia’s Official Internet World 60 Minute Guide to Java, por Ed Tittel y Mark Gaither,
1995, IDG Books, 1-56884-711-4...............................................................................................................220
Java!, por Tim Ritchey, 1995, New Riders Publishing, 1-556205-533-X. ..........................................220
The Java Handbook: The Authoritative Guide to the Java Revolution, por Patrick Naughton,
1996, Osborne McGraw Hill, 0-07-882199-1. .........................................................................................220
The Java Primer Plus: Supercharging Web Applications with the Java Programming Language,
por Paul M. Týma, Gabriel Torok y Troy Downing, 1996, Wayte Group Press, 1-57169-062-X. ..221
LIBROS SOBRE SOFTWARE ORIENTADO-A-OBJETOS............................................................................221
Object-Oriented Software Construction, por Bertrand Meyer, 1988, Prentice Hall, 0-13-629031-
0,534 pág.........................................................................................................................................................221
A Book of Object-Oriented Knowledge, por Brian Henderson-Sellers, 1991, Prentice Hall, 0-13-
059445-8, 297 pág. .......................................................................................................................................222
Object-Oriented Methods, 2 nd Edition, por Ian M. Graham, 1994, Addison-Wesley, 0-201-59371-8,
473 pág............................................................................................................................................................223
Object-Oriented Programming, por Peter Coad y Jill Nicola, 1993, Prentice Hall-Yourdon Press,
0-13-032616-X, 582 págs. y disquete incluido........................................................................................223
LIBROS DE ANÁLISIS Y DISEÑO ORIENTADO-A-OBJETOS (OOA & OOD) ........................................224
Designing Object-Oriented Software, por Rebecca Wirfs-Brock, Brian Wilkerson & Lauren Wiener,
1990, Addison-Wesley, 0-13-629825-7, 368 pág.....................................................................................224
Using CRC Cards: An Informal Approach to Object-Oriented Development, por Nancy M.
Wilkinson, 1995, SIGS Books, 1-884842-07-0 (Prentice-Hall 0-13-374679-8), 226 págs.............225
Object-Oriented Modeling and Design, por James Rumbaugh, Michael Blaha, William Premerlani,
Frederick Eddy & William Lorensen, 1991, Prentice Hall, 0-13-629841-9, 528 pág. ....................226
Object-Oriented Analysis and Design with Applications, 2 nd Edition, por Grady Booch, 1994,
Benjamin/Cummings, 0-8053-5340-2, 589 pág. ......................................................................................227

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 9
Migrating to Object Technology, por Ian M. Graham, 1994, Addison-Wesley, 0-201-59389-0, 552
pág....................................................................................................................................................................228
Object-Oriented Analysis, 2nd Edition, por Peter Coad & Edward Yourdon, 1991, Yourdon
Press/Prentice Hall, 233 pág.......................................................................................................................228
Object-Oriented Design, por Peter Coad & Edward Yourdon, 1991, Prentice Hall, 0-13-630070-7.229
Object-Oriented Analysis: Modeling the World in Data, por Sally Shlaer & Stephen J. Mellor,
1988, Yourdon Press/Prentice Hall, 144 pág...........................................................................................229
Object Lifecycles: Modeling the World in States, por Sally Shlaer & Stephen J. Mellor, 1991,
Prentice Hall, 0-13-629940-7. ....................................................................................................................229
Object-Oriented Systems Analysis: A Model-Driven Approach, por David W. Embley, Barry D.
Kurtz & Scott N. Woodfield, 1992, Prentice Hall, 0-13-629973-3, 302 pág......................................230
Object-Oriented Software Engineering: A Use Case Driven Approach, por Ivar Jacobson, Magnus
Christerson, Patrik Jonsson y Gunnar Övergaard, 1992, Addison-Wesley, 0-201-54435-0, 524
págs. .................................................................................................................................................................230
Object Oriented Program Design with Examples in C++, por Mark Mullin, 1990, Addison-Wesley,
0-201-51722-1, 303 pág...............................................................................................................................231
A Complete Object-Oriented Design Example, por Joseph E. Richardson, Ronald C. Schultz &
Edward V. Berard, 1992, Berard Software Engineering Inc, 1-881974-01-4, 350 pág...................231
Working With Objects: The OOram Software Engineering Method, por Trygve Reenskaug con Per
Wold y Odd Arild Lehne, 1996, Manning Publications, 1 -884777-10-4 (Prentice Hall: 0-13-
452930-8)........................................................................................................................................................232
LIBROS SOBRE PATRONES DE DISEÑO .............................................................................................................232
Notes on the Synthesis of Design, de Christopher Alexander, 1964, Harvard University Press. ...232
A Pattern Language: Towns/Building/Construction, de Christopher Alexander, Sara Ishikawa,
Murray Silverstein, Max Jacobson, Ingrid Fiksdahl-King y Shlomo Angel, 1977, Oxford University
Press, 0-19-501919-9....................................................................................................................................232
The Oregon Experiment, de Christopher Alexander, 1978, Oxford University Press.......................233
The Timeless Way of Building, de Christopher Alexander, 1979, Oxford University Press.............233
The Production of Houses, de Christopher Alexander, 1985, Oxford University Press. ..................233
Advanced C++ Programming Styles and Idioms, por James O. Coplien, 1992, Addison-Wesley, 0-
201-54855-0. ..................................................................................................................................................233
Design Patterns: Elements of Reusable Object-Oriented Software, de Erich Gamma, Richard Helm,
Ralph Johnson y John Vlissides, 1995, Addison-Wesley, 0-201-63361-2. .........................................234
Design Patterns for Object-Oriented Software Development, de Wolfgang Pree, 1995, Addison
Wesley...............................................................................................................................................................234
Pattern Languages of Program Design, editado por James O. Coplien y Douglas C. Schmidt, 1995,
Addison-Wesley, 0-201-60734-4.................................................................................................................235
LIBROS SOBRE BASES DE OBJETOS......................................................................................................................235
Object-Oriented Concepts, Databases, and Applications, editado por Won Kim & Frederick H.
Lochovsky, 1989, Addison-Wesley, 0-201-14410-7. ...............................................................................235
Object-Orientation: Concepts, Languages, Databases, User Interfaces, Setrag Khoshafian &
Razmik Abnous, 1990, John Wiley & Sons, 0-471-51801-8..................................................................235
Object Data Management: Object-Oriented and Extended Relational Database systems, R.G.G.
Catell, 1991, Addison-Wesley, 0-201-53092-9. .......................................................................................236
Object-Oriented Databases, Setrag Khoshafian, 1993, John Wiley & Sons, 0-471-57056-7.........236
Sistemas de Bases de Datos Orientadas a Objetos: Conceptos y Arquitecturas, Elisa Bertino &
Lorenzo Martino, 1993 (traducción 1995), Addison-Wesley Iberoamericana, 0-201-65356-7. ...236
The Object Database Standard: ODMG-93, editado por R.G.G. Catell, 1994, Morgan Kaufmann
Publishers, 1-55860-302-6. .........................................................................................................................237
Object-Oriented Databases: Technology, Appllications and Products, Bindu R. Rao, 1994,
McGraw-Hill, 0-07-051279-5......................................................................................................................237
Object Databases: The Essentials, Mary E. S. Loomis, 1995, Addison-Wesley, 0-201-56341-X.....237

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 10
Modern Database Systems: The Object Model, Interoperability, and Beyond, editado por Won Kim,
1995, Addison-Wesley, 0-201-59098-0......................................................................................................237
LIBROS SOBRE INGENIERÍA DE PROCESOS.................................................................................................238
Reengineering the Corporation: A Manifesto for Business Revolution, por Michael Hammer &
James Champy, New York: HarperCollins Publishers, 1993, 0-88730-640-3...................................238
The Object Advantage: Business Process Reengineering with Object Technology, por Ivar
Jacobson, Maria Ericsson y Agneta Jacobson, ACM Press, 1994.......................................................238
Business Engineering with Object Oriented Technology, por David A. Taylor, John Wiley & Sons,
1995..................................................................................................................................................................238
LIBROS SOBRE GESTIÓN DE PROYECTOS...........................................................................................................239
Succeeding with Objects: Decision Frameworks for Project Management, por Adele Goldberg &
Kenneth S. Rubin, 1995, Addison-Wesley, 0-201-62878-3, 542 págs. ................................................239
Pitfalls of Object-Oriented Development, por Bruce F. Webster, 1995, M&T -Books, 1-55851-397-
3.........................................................................................................................................................................239
Object Lessons: Lessons Learned in Object-Oriented Development Projects, por Tom Love, 1993,
SIGS Books, 0-9627477-3-4 (Prentice-Hall 0-13-472432-1)...............................................................240
LIBROS CONTRA LA TONTERÍA ....................................................................................................................240
201 Principles of Software Development, por Alan M. Davis, 1995, McGraw-Hill, 0-07-015840-1.241
Software Requirements & Specifications: A Lexicon of Practice, Principles and Prejudices, por
Michael Jackson, 1995, Addison-Wesley, 0-201-87712-0.....................................................................241

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 11
INTRODUCCIÓN
ii
¿O tro libro sobre C++? Bueno, al menos no otro libro del tipo “Aprenda
C++ por ósmosis” o “¡Los secretos de C++ por fin revelados!”. La pre-
tensión de este texto es mucho más modesta: se trata de comunicar al
lector que también en C++ existen ciertas normas de estilo, y que tales normas se
desprenden tanto de la sintaxis del lenguaje como de prudentes decisiones de
diseño íntimamente relacionadas con la Tecnología de Objetos que anida tras el
lenguaje. La intención es, pues, colocar al lector en una posición más elevada a la
de la mera mecánica del lenguaje, donde el CO2 de la tontería quede bien abajo.

¿A QUIÉN VA DIRIGIDO ESTE LIBRO?

Podría decirse que al programador de C++, pero lo cierto es que la mayor parte
del material que en el mismo aparece ha sido impartida con éxito a postulantes a
la Tecnología de Objetos con muy distinto bagaje: desde programadores de RPG
hasta expertos en C++, pasando por analistas y diseñadores de todo tipo y con-
dición. Y es que un buen programador de C++ sobre todo lee y decide, lee y
estructura, lee y modifica, lee y vuelve a leer para, al final, codificar. En esa tónica,
el presente libro muestra arquitecturas, métodos, formas-de-hacer y técnicas de
codificación en C++ que el lector difícilmente encontrará reunidas en un solo libro.
Se explicitan, a la vez, errores frecuentes de uso del lenguaje y otros más sutiles
relacionados con decisiones de diseño que sobrepasan el ámbito de la
especificación sintáctica. Pero, con todo, no se trata de procurar recetas ni
brebajes milagrosos, sino más bien de argumentar, en continua evolución, las
posibles estrategias respecto de distintas características del lenguaje, así como
sus defectos, ventajas y carencias, para conducir de esta manera al lector hacia
soluciones razonadas de probada eficacia. Espero, pues, que el libro resulte tan
útil a los programadores de C++ como a los que quieren introducirse en el
proceloso mundo de la codificación en C++.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 12
SOBRE EL AUTOR

Ricardo Devis es, además de miembro del Comité ANSI X3J16 C++ para la
estandarización del lenguaje de programación C++, presidente nacional de
APTO2, la Asociación Española para la Promoción de las Tecnologías
Orientadas-a-Objetos; miembro con voto de ACM y ACM SigPlan; fellow de la
Institution of Analysts and Programmers (IAP); miembro con voto de IEEE e IEEE
Computer Society; miembro de The British Computer Society; miembro del Grupo
de Planificación de Métodos Orientados-a-Objetos de IEEE (Object-Oriented
Methods Planning Group), Objetos de Negocio, para la normalización y
estandarización de los métodos orientados-a-objetos; coordinador de INFOOP, la
conferencia española de OOP y C++; y miembro del IEEE CS&E Critics Circle.
Es editor del texto “INFOOP ‘93: I Congreso Español de Programación
Orientada-a-Objetos y C++” (Alicante, 1993, 84-604-6533-0), autor del libro
“Programación Orientada-a-Objetos en C++” (Editorial Paraninfo, Madrid, 1993,
320 páginas, 84-283-2056-X), y editor de “The Object Oriented Page (La Página
Orientada-a-Objetos)” en el World Wide Web para Tradewave’s Galaxy. Ricardo
Devis es el presidente y fundador de INFOPLUS, S.L., consultoría de Tecnología
de Objetos, C++ y Smalltalk, y ha estado trabajando en el área informática desde
1.982, y fuertemente focalizado en Tecnología de Objetos desde 1.987. Como
reconocida autoridad en Orientación-a-Objetos, ha formado a muchísimos
ingenieros de software en C++, Smalltalk y métodos de OORA/OOA/OOD, a la
vez que (co)dirigido multitud de proyectos orientados-a-objetos, principalmente
para grandes corporaciones y compañías institucionales.

UNA VISIÓN PANORÁMICA

Una cierta visión de conjunto suele beneficiar la comprensión de estructuras com-


puestas por piezas aparentemente inconexas. Así, en lo que sigue, se detallan la
esencia de los capítulos del libro y las relaciones que observan entre ellos, de
forma que el lector puede compartir y evaluar las pretensiones del autor, para bien
o para mal.

Capítulo 1 - Objetos: Hitos, Mitos y Ritos


En torno a ciertos popularmente entendidos hitos se han conjugado una larga se-
rie de inexactitudes conformando lo que, con la adición de fantasía tecnológica,
se han convertido rápidamente en mitos, alrededor de los cuales ha surgido un
cúmulo de ritos, a cual más atroz, ridículo o profano. Hablo, naturalmente, de la
Tecnología de Objetos y la parafernalia que la rodea. En el presente capítulo me
ocupo de algunos de ellos, a los que trato de imponer la luz de la razón o, cuando
menos, del pragmatismo sentido, pretendiendo insuflar al lector cierto ánimo críti-
co respecto de la ingente (des)información generada por la explosión comercial
de la “Programación Orientada-a-Objetos”. La idea subyacente es similar a la que

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 13
anima las oberturas de las obras musicales, cuyo antiguo objetivo era “tranquilizar
al ruidoso público y prepararlo para el comienzo de la representación”. Aparece
claro, con todo, que ésta no es la obertura de Tannhäuser, pero espero que surta
un parecido poderoso efecto.

Capítulo 2 - Contenedores en C++


La parametrización de tipos respecto de estructuras agregativas es el caballo de
batalla (el tamaño es lo de menos) de los modernos lenguajes de programación:
colas, listas, conjuntos, etc., son algunos de los contenedores de uso frecuente en
cualquier códificación no trivial. En este capítulo se muestran las ventajas y ma-
yormente inconvenientes del uso de contenedores en C++ mediante su codifica-
ción expresa (a mano). Así, a pesar de mostrar distintas técnicas, más o menos
sofisticadas, para el manejo de relaciones parametrizables continente-contenido,
la conclusión final es que las plantillas son la mejor solución genérica.

Capítulo 3 - C++ STL: La Biblioteca Estándar de Plantillas en C++


¿Quién no ha usado en C++ macros genéricas, o aun cierto tipo de derivación o
las relativamente nuevas plantillas, para reutilizar código ligado a ciertos tipos de
datos, determinados iteradores o útiles algoritmos? ¿Debemos recurrir a biblio-
tecas comerciales para encontrar clases contenedoras básicas (vector, bolsa,
etc.) o, peor aún, codificar a-mano las estructuras de datos necesitadas, cuando
el lenguaje debiera, en un alarde de prudencia, procurarlas? La STL (Biblioteca
Estándar de Plantillas) es una adición al estándar del lenguaje C++, ya consolida-
da, que soluciona todas estas cuitas, además de mostrar un muy elegante, eficaz
y extensible entorno-marco de algoritmos reutilizables. Este capítulo es lógica
continuación del anterior y expone la solución de C++ al problema de la parame-
trización de contenedores.

Capítulo 4 - Manejo de Excepciones en C++


C++ proporciona soporte explícito para el tratamiento de las excepciones que
puedan surgir en el flujo normal de computación de una aplicación dada, pero la
sintaxis, comportamiento y, sobre todo, implementación de las soluciones provis-
tas son relativamente nuevos. C++ es, por otro lado, un lenguaje excepcionalmen-
te prolijo en sutilezas, así que parece conveniente una revisión del fondo y forma
de las excepciones, que en esta ocasión se convierten en regla. No se dan en
este capítulo, con todo, instrucciones paso-a-paso para la inserción de las excep-
ciones de C++ en aplicaciones complejas (pues esto más bien corresponde a un
manual de arquitectura de aplicaciones), sino más bien se argumentan y explici-
tan ciertos mecanismos que inducen determinadas decisiones de diseño cuyo
conocimiento resulta imprescindible para cualquier programador de C++.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 14
Capítulo 5 - Asignación en C++
¿Qué se esconde tras una aparentemente simple asignación en C++? En esen-
cia la voluntad de copiar un objeto en otro. Pero en C++ la complejidad se escon-
de en casi todos los recovecos del lenguaje, de forma que una simple expresión
de asignación puede generar gravísimos errores muy difíciles de depurar. Natu-
ralmente el remedio es el conocimiento exacto no sólo de la sintaxis del lenguaje,
sino también de la precisa actuación semántica que se agazapa tras aquélla.
Cuando yo planteo esto ante algún auditorio, suele preguntárseme: ¿no se puede
obviar toda esta complejidad? Mi respuesta suele ser: “Para permitirse ser es-
céptico hay que haber creído mucho antes”. Esto es, sólo simplifica quien com-
prende.

Capítulo 6 - Patrones de Diseño: La Calidad Sin Nombre


El viejo sueño de la comunidad software ha sido emular a la arquitectura tradicio-
nal utilizando similares esquemas, procesos, herramientas, elementos y compo-
nentes en la construcción de sistemas software. A lo largo del tiempo el desarrollo
de tales sistemas ha ido ajustándose de forma metódica a este propósito: las
bibliotecas reutilizables, los módulos interconectables, la formalización de fases,
etc. muestran distintos aspectos de este acercamiento. Los patrones, al fin,
ejemplifican el último estadio de esta relación: bloques que, organizados en catá-
logos, encapsulan la experiencia de excelentes diseños software. Pero el lector
puede objetar: “¿Qué tienen que ver los patrones con la programación C++?” ¡Oh,
cielos! ¡Mucho! Resulta, por un lado, que el grueso de los patrones está ejemplifi-
cado en C++, mientras que por otro lado cabe recordarle al olvidadizo lector que
en C++ se lee mucho y se codifica poco, y los patrones son una excelente lectura
si no se quiere reinventar la rueda constantemente. Además los patrones obser-
van una relación directa con los importantes “modelos de roles” e, incidental pero
expresamente, su conocimiento será necesario para abordar el siguiente capítulo.

Capítulo 7 - Roles y Objetos en C++


Los sistemas software generalmente procuran simular sistemas reales en los que
distintas entidades interactúan usualmente de forma no trivial. No resulta raro,
pues, que las conceptualizaciones más frecuentes en el mundo real hayan inten-
tado ser trasladadas al dominio software, con mayores o menores fortuna, fideli-
dad o eficacia, generando, en cualquier caso, un acervo de soluciones que pue-
den ser reutilizadas por analistas, diseñadores y programadores. En el presente
capítulo abordamos (como corsarios, naturalmente), aun de forma elemental, uno
de los problemas de modelado software más comunes: el de los roles, esto es, el
de los papeles que determinadas entidades físicas o conceptuales asumen res-
pecto de otras. La exposición se enfoca a nivel de lenguaje y, por tanto, se obvian
bastantes de los aspectos metodológicos propios de las áreas de análisis y di-
seño.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 15
Capítulo 8 - Bases de Datos Orientadas-a-Objetos

¿Qué es un objeto? Dices mientras clavas en mi pupila tu pupila azul ¡Qué es


un objeto! ¿Y tú me lo preguntas? Un objeto ... ¡eres tú! Y es que en Tecnología
de Objetos (como en otros muchos ámbitos de la vida y la política) cualquier enti-
dad con límites conceptuales bien definidos es un objeto. Y como todo son obje-
tos surge, de forma natural, la idea de repositorios que mantengan estas entida-
des respetando sus identidades: las Bases de Objetos. En el presente capítulo
examino, desde la óptica pragmática del programador (u OODA, como veremos),
algunos aspectos de esta tecnología, fundamentalmente basada en los productos
comerciales que la sustentan. Pero, atención: no se pretende enseñar a codificar
en C++ los accesos a bases de objetos, si no más bien explicitar ciertos concep-
tos básicos que, además, servirán perfectamente al lector para seguir con exacti-
tud el siguiente capítulo.

Capítulo 9 - Consideraciones Prácticas


¿Cómo se enfrenta el programador de C++ a la conjunción de entornos gráficos
con bases de objetos y bibliotecas multiplataforma? Pues usualmente con miedo,
resignación o imprudencia temeraria. Para cubrir este cierto vacío, en el presente
capítulo se exponen varias técnicas de acercamiento a la codificación de aplica-
ciones basadas en entornos gráficos. El enfoque es decididamente elemental y
se basa en resaltar ciertas ideas y formas de abordar problemas suficientemente
genéricos en la codificación diaria de aplicaciones: el paradigma MVC, la gestión
de vistas, la gestión de concurrencias, etc.

Capítulo 10 - Gestión de Proyectos Orientados-a-Objetos


En este capítulo se pretende mostrar, aun muy sucintamente, el estado actual de
la Tecnología de Objetos respecto de su aplicación en proyectos reales de desa-
rollo software. Es imposible, naturalmente, resumir y condensar en tan pocas pá-
ginas tamaña nebulosa tecnológica, pero al menos se expondrán algunas consi-
deraciones prácticas extraídas de la aplicación por el autor de lenguajes, técnicas
y métodos orientados-a-objetos en proyectos reales. El lector avisado ya supon-
drá que el capítulo no va a ser, precisamente, un rosario de elogios y recetas, sino
más bien lo contrario: un toque de atención referente a las dificultades de elección
de métodos, herramientas y, al fin, de arquitecturas sobre las que basar los sis-
temas software.

Capítulo 11 - Modelos de Roles: El Método Ooram


La arena del análisis y diseño orientados-a-objetos está llena de luchadores re-
pletos de trucos y estrategias comerciales, pero que sorprendentemente usan de
armas muy parecidas, de forma que el lastimero enfoque final parece venir de la
mano de alianzas, mixturas y refritos comerciales de distintos métodos existentes.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 16
Ante tamaña impostura cabe señalar que realmente existen metodologías serias
y, de entre ellas, destaca por su originalidad, solidez y probada aplicación en la
construcción de grandes sistemas la representada por OOram y que se basa en
modelos de roles, la unidad natural de composición de sistemas software. En es-
te capítulo se dan algunas pistas y se exponen ciertas ideas que pretenden redi-
mir al lector de tanta comercialidad metódica.

Capítulo 12 - Bibliografía Comentada

“Si yo tuviera que vender mi gato (al menos a un informático) no diría que es
amable y autosuficiente y que se alimenta de ratones: más bien argüiría que
está orientado-a-objetos” (Roger King). ¡Oh, oh, hay que protegerse de la tonte-
ría! Lo que aparece en este capítulo es una cuidada y actualizada selección bi-
bliográfica (casi completamente en inglés) que abarca el grueso de las áreas de
la Tecnología de Objetos. He preferido comentar preferentemente los textos de
indiscutible calidad, pero también he incluido alguno sectorialmente interesante o
históricamente importante. Definitivamente se trata de ayudar al lector a evitar
tanto perder el tiempo como ser víctima de imposturas cometidas por autores
infames o simplemente inicuos.

GARANTÍA

Todos los errores que el lector encuentre en el presente texto son únicamente mí-
os, como lo son las carencias, deslices e inexactitudes que en el mismo se pue-
dan dar. Los comentarios, elogios, sugerencias y críticas son, cómo no, bienveni-
dos: el lector puede localizarme bien en INFOPLUS S.L., voz/fax (96) 5930017,
bien mediante correo electrónico en Internet (devis@ibm.net) o Compuser-
ve(100140.1435), mientras que la URL de mis páginas en el WWW es
“http://www.well.com/user/ritchie”.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 17
OBJETOS: HITOS, MITOS Y RITOS
1
N
o hay que subestimar la fuerza de la repetición y la estulticia de los medios
de comunicación: si una barbaridad se repite con insistencia en los luga-
res adecuados, en breve pasará a ser considerada como una realidad
incontestable. Les pondré un ejemplo: cuando se habla de objetos mucha gente
perpetra con demasiada facilidad vocablos como “paradigma”, “entorno/marco”
(por framework), etc. Pero, ¿cuál es su significado preciso? Humm, mejor no pre-
guntar. Pregunten sin miedo, empero, sobre la orientación a objetos y obtendrán:
tal, tal ... y “polimorfismo”. ¡Demonios! La traducción literal del griego, “muchas-
formas”, no genera excesivas luces, la verdad, así que la mayoría de los encues-
tados tiende a suplir sus carencias formales con su autoexaltado sentido común,
de forma que los resultados vienen a ser como la traducción al castellano de las
películas extranjeras: un dislate sin sentido. ¿Es polimórfico Felipe González?
¿Es polimórfica la plastilina? En fin, como el lector puede apreciar el panorama
es agotadoramente desolador. En lo que sigue intentaré reseñar algunas de las
supersticiones más extendidas, pero el tema en absoluto queda completamente
cubierto, así que a lo largo del libro irán apareciendo ideas parecidas que intenta-
ré sean oportunamente desenmascaradas.

MITO 1: UN LENGUAJE ORIENTADO-A-OBJETOS “PURO” ES MEJOR


QUE OTRO “HÍBRIDO”.

Esto es lo mismo que decir que un veneno puro es más beneficioso para el cuer-
po que otro convenientemente rebajado. Cualquier mente no enferma entiende
que la idoneidad de un lenguaje, en cualquier ámbito, se basa en las facilidades
que provee para acometer las tareas a que se destina. Parece, además, pruden-
te suponer que, como bien afirma Stroustrup, “un lenguaje de propósito general ha
de soportar más de un estilo (paradigma) de programación”. Oh, naturalmente
esto no es sólo una defensa de C++ frente a Smalltalk (que lo es, por cierto), sino
una llamada al sentido común: si preguntamos a diez dentistas cuáles son los pi-
lares de la orientación-a-objetos, al menos nueve de ellos responderán “encapsu-
lación, herencia y polimorfismo”, para después añadir “y no provoca caries”. Ah,
pero esto no es cierto. La verdad es que todavía no hay consenso respecto de las

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 18
características esenciales de la orientación-a-objetos. Y no digamos de otras ca-
racterísticas: En los lenguajes orientados-a-objetos ¿todo son objetos? ¿Las cla-
ses han de ser por fuerza objetos? ¿Son los sistemas orientados-a-objetos en
realidad sistemas de objetos, para los objetos y con los objetos? ¿La herencia
múltiple es una bendición o más bien un artefacto del infierno que requiere vender
primero el alma para ser manejado con acierto? ¿El chequeo estático de tipos es
una herejía? Vaya, demasiadas incertidumbres. Así que si Smalltalk o Eiffel son
químicamente puros, C++ podría ser físicamente híbrido. Bah, ¡tonterías!

MITO 2: PARA APRENDER A USAR ADECUADAMENTE C++ HAY QUE


EMPEZAR POR SMALLTALK.

En más de una ocasión Stroustrup oportunamente ha señalado: “C++ no es un


buen Smalltalk; nunca se pretendió que lo fuera. De igual manera Smalltalk no
es un buen C++, pues evidentemente nunca se pretendió tal”. El mensaje es
claro: Smalltalk conduce a una particular forma de diseñar y codificar, basada
esencialmente en la estructuración mediante herencia de clases en jerarquías
cósmicas (derivadas de Objeto), que trasladada a C++ usualmente genera un
estilo particularmente ineficiente e inseguro. ¿Quién en su sano juicio usaría en
C++ de forma masiva, a no ser por imperativos físicos, las colecciones de Small-
talk pudiendo hacer uso de la Biblioteca Estándar de Plantillas de C++ (que obvia
completamente la herencia, dicho sea de paso)? Mi experiencia en dirección de
proyectos y formación me señala, en contrapartida, que la realidad es exactamen-
te la inversa: un buen programador de C++ se convierte con mucha facilidad, y
normalmente a su pesar, en un buen programador de Smalltalk. Pero, claro, ésta
es una broma práctica.

MITO 3: C++ ES MÁS FÁCIL DE ASIMILAR PARA LOS PROGRAMADORES


DE C.

Este lugar común casa perfectamente con aquel otro que afirma “Para estudiar
C++ hay que empezar estudiando C”. En general habría que decir, con Stroustrup,
que “si vas a usar C++, estudia C++”. Desmond D’Souza acertadamente expone
que el hecho que la enseñanza de C++ no sea trivial en absoluto implica que C++
sea difícil de aprender (o al menos un cierto subconjunto de uso del lenguaje): otra
cuestión es que lo parezca, y esto se debe más a lo que se adivina no saber que
a lo que en realidad se ha aprendido (C++ es un lenguaje ciertamente prolijo). En
general el segmento de C++ que intersecta con C no posee el mismo comporta-
miento en ambos lenguajes: esto es, C enseña a codificar estructuras de código
preparadas para utilizar los recursos del lenguaje, bien distintos a los de C++; y a
la inversa. Así una buena parte de las técnicas que se enseñan en excelentes tex-
tos de C resultan inservibles o inadecuadas en C, mientras que el uso de ciertas
características del subconjunto C de C++ no es en absoluto trasladable al puro C.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 19
Resulta, pues, que en la práctica los programadores que arrostran un cierto estilo
C tienden a observar un lapso de tiempo más prolongado para el aprendizaje de
C++ que los que, sin bagaje previo de C, empiezan por C++.

MITO 4: LAS HERRAMIENTAS OOCASE INTEGRADAS GENERAN CÓDIGO


ORIENTADO-A-OBJETOS FIABLE.

Bueno, hay que distinguir dos vertientes: la generación de código de implementa-


ción y la generación de cabeceras, frecuentemente llamadas esqueletos de la
aplicación y que usualmente adquieren en la práctica su sentido más mórbido. En
cuanto a las cabeceras, refiriéndose al código de descripción de clases, bueno,
como la mayoría de las herramientas OOCASE requieren una completa informa-
ción de nombres y tipos de métodos y atributos, la generación de tales protoco-
los, unidos a lo sumo por una relación de herencia, resulta aparentemente trivial;
en lo referido al código de los métodos, bueno, esta es otra historia: cualquier
programador de C++ sabe que tras una simple línea de código pueden escon-
derse cientos de líneas en absoluto triviales o evidentes. Y es que ¿cómo asume
una herramienta CASE idiomas y estilos estrictos de codificación para cada len-
guaje particular? Pues como murio mi abuela: con mucho dolor y dificultad. Si el
detalle no excesivamente prolijo del uso del operador de asignación en C++ pue-
de llevar más de 20 páginas, ¿podría permitirse una herramienta CASE su parti-
cularización hasta tal extremo, teniendo en cuenta, además, que muchas decisio-
nes de implementación dependen de la arquitectura y patrones del diseño a codi-
ficar? Naturalmente no estoy hablando de generación de código para la construc-
ción de interfaces gráficos, pues existen entornos/marcos y bibliotecas de clases
perfectamente estables y fiables sobre los que construir con facilidad y seguridad
clases concretas de uso.

MITO 5: LA HERENCIA ES UNA CUALIDAD ESENCIAL DE LOS SISTEMAS


ORIENTADOS-A-OBJETOS.

¿Cuáles son los pilares de la Orientación-a-Objetos? Henderson-Sellers los esta-


bleció en un triángulo: abstracción, encapsulación y polimorfismo. ¿Y la herencia?
Bueno, parece que hay poco patrimonio que transmitir. Y es que, pese a lo que se
pueda pensar, la herencia es básicamente un mecanismo que utilizan los lengua-
jes de programación orientados-a-objetos para implementar el polimorfismo, y
como tal mecanismo puede ser perfectamente sustituido por otro que procure la
misma suerte de funcionalidad, siempre a nivel de lenguaje. Oh, ya veo temblar al
lector, que atónito observa como desdigo y mortifico a multitud de gurus (un bar-
barismo, aunque no lo crea) y expertos que basan la construcción de sistemas
software fundamentalmente en la herencia. Bien, Alan Davis afirma que hay que
seguir a los lemmings con cuidado: la herencia, en tales sistemas, pertrecha cau-
sa común con los tipos abstractos de datos, jerarquías de subtipado y con algu-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 20
nos aspectos de simplificación del diseño, para terminar con cuestiones de reuti-
lización: todo un completo panorama que parece trocar a la herencia en piedra
angular. Nada más lejos de lo deseable, empero: ¿por qué distinguir la herencia,
como relación entre clases, de las relaciones de agregación, composición o uso
que también se dan en tales sistemas? ¿Por qué la herencia suele aplicarse a
clases y no a objetos/instancias? Pues porque es una componenda esencialmen-
te estática que procura una estructura lógica recorrible sobre la que basar bien la
un esquema de reutilización bien la resolución de operadores (por métodos) en
tiempo de ejecución. Pero componenda no es panacea, así que nos quedaremos
con que la herencia es una cualidad accesoria de los sistemas orientados-a-
objetos.

MITO 6: LA HERENCIA ES UN MECANISMO INDISPENSABLE EN LOS


LENGUAJES DE PROGRAMACIÓN ORIENTADOS-A-OBJETOS

Oh, este es un mito especialmente peligroso. Ciertamente la aplicación de la he-


rencia en los lenguajes de programación constituyó, en su día, un importante hito
en el dominio de la modularidad software. La herencia, o derivación, ha incurrido,
empero, en generar una larga serie de despropósitos que la acompañan donde-
quiera que va. Resultaría, de acuerdo con lo expuesto, que un lenguaje orientado-
a-objetos prototípico y basado en delegación, como es Self, no estaría realmente
orientado-a-objetos. Se oye frecuentemente, también, que una buena codificación
ha de hacer uso intensivo de la herencia, y se obvian otras características como
contenedores parametrizables, algoritmos reutilizables, etc. Con todo cuando en
alguna mesa redonda yo afirmo que en algunos proyectos prohibo la herencia (y
por prohibir indico que cualquier relación de herencia necesitará la aprobación de
un arquitecto), mis colegas me miran con sospecha. Y es que realmente la heren-
cia es un mecanismo poderoso, pero también peligroso y difícil de controlar en
grandes equipos humanos.

MITO 7: LA HERENCIA ÚNICAMENTE SE PUEDE APLICAR CUANDO SE


DA UNA RELACIÓN “ES-UN”.

La dictadura de las jerarquías de subtipación se ha convertido, en muy breve


tiempo, en una absurda tecnocracia. En el mundo Smalltalk (universo, le llaman
sus postulantes) es frecuente, por ejemplo, usar la derivación de clases como mé-
todo puro de reutilización, haciendo caso omiso de consideraciones estrictas de
subtipado. Y es que si se piensa que los lenguajes son herramientas imperfectas
de modelado en el dominio software de problemas reales, ¿cómo no se ha de ser
esencialmente pragmático tanto en el diseño como en la codificación? Los
“mixins”, por ejemplo, son clases resultantes de la aplicación de la herencia múlti-
ple que se usan como componentes de implementación de otras clases, bien
mediante herencia bien mediante composición (layering). Tiende a olvidarse con

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 21
facilidad que la finalidad de las actividades de desarrollo es la construcción de
sistemas software, y no la aplicación de métodos o técnicas en sí.

MITO 8: EXISTEN DEMASIADAS METODOLOGÍAS DE ANÁLISIS Y DISEÑO


ORIENTADAS-A-OBJETOS.

Bien al contrario, prácticamente no existen en la actualidad metodologías orienta-


das-a-objetos. Observe el lector que metodología es “ciencia de los métodos”, y
con lo que mayormente contamos es con métodos: OMT, Coad/Yourdon, Wirfs-
Brock, Jacobson, Booch, etc. son, pura y simplemente, métodos en el mejor de
los casos, aderezados con algunos heurísticos y bastantes trucos, varios ejem-
plos y un demasiado de filosofía genérica. Claro que en informática se soluciona
todo con rapidez, de forma que se ha redefinido “metodología” para ajustar el vo-
cablo a las carencias del área: así que en informática “metodología” significa “mé-
todo” (y si el lector se sorprende, ríase de la locución “ingeniería de software”).
Con todo, no existen tantos métodos orientados-a-objetos: casi todos ellos se
basan en alguna extensión del modelo entidad-relación de Chen, sobre la base
de clases/objetos, a la que se adicionan notaciones sospechosamente parecidas
entre sí y algunos componentes de comportamiento sustanciados en el modelado
de escenarios arbitrarios y en el control y gestión de estados de las entida-
des/clases/objetos. Realmente con los métodos OO se sustancia la vox populi
sobre los políticos: conocido uno, conocidos todos. Bueno, casi todos: sí existe
alguna metodología orientada-a-objetos (y seguro que no es la que el lector pue-
da pensar), pero eso es materia de un próximo libro.

MITO 9: CADA EMPRESA DEBE ADAPTAR A SU MANERA LOS MÉTODOS


EXISTENTES PARA CONSTRUIRSE UNO “A MEDIDA”.

Piense el lector, en primer lugar, si no es ridículo pensar que un método dado


pueda manejar cualquier problema de la realidad, sobre todo teniendo en cuenta
que la mayoría de los métodos observa una estructuración secuencial a la vez
breve y establecida en fases inamovibles. Oh, esto no es sino mirar la realidad,
como hizo Freud respecto del sexo, por un tubito tan pequeño como el método
usado. Examine el lector seguidamente la desafortunadamente extendida suposi-
ción que una conjunción particular de varios métodos, encajados en razón del dis-
cutible criterio de una empresa, pudiera modelar todos los problemas. Es como
si, una vez desechado el esperanto, la solución viniera de la mano de un refrito de
lenguas: I t’aime mucho! Y lo curioso es que este enfoque se ha convertido en una
de esas verdades difusas que casi nadie cuestiona: ahora cojo OMT de Rum-
baugh, le añado un tanto de casos de uso (use cases) de Jacobson, lo aderezo
con algo de fichas CRC y Diseño Orientado-a-Responsabilidades de Wirfs-
Brock, lo agito en la coctelera, le doy un nombre nuevo, fabrico una herramienta
pseudo-CASE que lo soporta y ... voilà el método universal. Es como si se anun-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 22
ciara: sobre gustos no hay nada escrito. ¡Mentira! Hay muchísimo escrito, pero
seguramente usted no lo ha leído, cabría contestar. La solución, a mi entender, no
pasa por el refrito de métodos, ni tampoco por la solución que proponen los auto-
denominados métodos de segunda generación, abanderados por Fusion, de Co-
leman y otros, y que se basa en la utilización de un método cualquiera (OMT, Ja-
cobson, Wirfs-Brock, etc.) como información de entrada para un marco formal que
regule y distribuya adecuadamente los resultados. El modelado genérico de sis-
temas de información habría más bien de pasar, según mi opinión, por lo que se
conocen como “marcos referenciales metodológicos”: esto es, un adecuado con-
junto de herramientas metodológicas y de métodos que conforman un arsenal de
vistas aplicables de forma segmentada y no necesariamente completa sobre el
dominio de los problemas a modelar. ¿Existen tales marcos? Por supuesto, pero
esto es otra historia.

MITO 10: LO PRIMERO QUE HAY QUE HACER ES CREAR UNA BIBLIOTE-
CA DE CLASES REUTILIZABLES.

Esta frase se oye mucho en cursillos y se lee por doquier en las revistas del ramo.
La cuestión es: ¿cómo puede crearse de la nada una clase reutilizable? Esto es,
¿puede reutilizarse una clase que todavía no ha sido utilizada? ¡Naturalmente que
no! Primero hay que generar a la vez política y ambiente de reutilización en la em-
presa, de forma que cuando se creen clases con un diseño prudente tales clases
sean utilizadas por el equipo humano y la información que se desprenda de tal
uso pueda revertir en la mejora de tales clases, que volverán a ser utilizadas de
nuevo en un ciclo involutivo. Se entiende por reutilización no el uso (una clase
“Ventana” se usará por varias aplicaciones, pero tal no es reuso), sino los usos
distintos en distintos contextos. Por supuesto que el (re)uso se da tanto a nivel de
aplicativos como de las propias bibliotecas de clases, pero aquí, como ya se ex-
presó anteriormente, la herencia juega un papel usualmente desdichado: si nos
dedicamos a insertar las clases a reutilizar en jerarquías no-triviales de herencia,
cuando queremos hacer uso de una determinada clase arrastraremos con ellas
buena parte del arbol de derivación, con lo que el nivel de aprovechamiento, si
posible, dejará mucho que desear.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 23
CONTENEDORES Y PLANTILLAS
2
D
esafortunadamente el término "contenedor" no existe -si no es un armazón
metálico- como sustantivo en castellano, pero para eso está la informática:
para alterar los nervios de los puristas y modificar, de facto, para bien o
mejor para mal, el lenguaje. Así diremos, parafraseando a la Real Academia, que
un objeto “contenedor” (o de tipo contenedor) es “el que lleva o encierra objetos
dentro de sí”. De la misma manera afirmaremos que una clase “contenedora” es
una clase cuyas instancias pueden contener otras instancias. Obviaremos, pues,
expresamente, el exacto vocablo “continente”, cuya acepción como “cosa que
contiene en sí a otra” es en realidad lo que necesitamos, pues aquí la ortodoxia
del lenguaje deberá ceder frente a la costumbre establecida: la práctica totalidad
de los asistentes a cursos prefiere el neologismo, pues “continente” ofrece unas
connotaciones de “uso restrictivo de la continencia” no deseadas. Pero, ¿qué in-
terés tienen estas baratijas pseudo-lingüísticas? Pues resulta que, como el pers-
picaz lector ya habrá adivinado, continuamente estamos usando clases y objetos
contenedores; de hecho existen muchos libros que, de forma mayormente lasti-
mosa, dedican casi todo su espacio a la ejemplificación de los mismos: pilas,
bolsas, conjuntos, vectores, matrices, listas, listas enlazadas, colas, etc. Su utili-
zación es tan frecuente que la circunstancia de que, hasta hace poco, C++ no in-
cluyera clases contenedoras como parte del lenguaje indudablemente ha perjudi-
cado la extensión de su adopción. Piénsese, por ejemplo, que el esperanto es un
lenguaje bienintencionado (y recordemos aquí a Valle-Inclán, buen ilustrador de la
materia con que está empedrado el infierno) dotado de una sintaxis y pronuncia-
ción racionales que en teoría facilitarían su urgente adopción universal. Sin em-
bargo algo falla, pues es evidente que estas líneas no están en esperanto 1. Pa-
semos a otro escenario: si yo me siento bien con RPG, me he acostumbrado a él,
le he cogido cariño con el tiempo y no creo en la muerte asistida, ¿por qué demo-
nios he de cambiar a un lenguaje pretendidamente mejor, como anuncian pueda
ser C++? Y lo mismo puedo pensar si estoy cuidando de Cobol, Visual Basic, C,
Pascal, etc.: ¿No es demasiado presumida la asunción de que si no cambio es
debido a un cúmulo de incapacidades? Bueno, un lenguaje se usa en la práctica,

1
En Español (castellano <sic>) en el original.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 24
independientemente de los bienintencionados motivos que lo amparen, en tanto
que proporciona facilidades para el tipo de programación para el que se preten-
de utilizar como herramienta. En este sentido cabe destacar que C++ es -en la
actualidad- un lenguaje de extensa sintaxis y gran prolijidad semántica (sólo hay
que contar que el borrador del estándar del lenguaje consta de aproximadamente
700 páginas en formato A-4, y en él "sólo" se describe el lenguaje C++), y su con-
cepción y desarrollo se han ligado, sobre casi todo lo demás, a su practicidad
como herramienta de programación, de tal forma que si realmente se necesitaba
una facilidad ésta se añadía al acervo del lenguaje. Pero, ¿proporciona C++, por
ejemplo, las facilidades sobre contenedores que necesitamos? Pues bien, sí. Re-
sulta que ANSI X3J16 ha incorporado recientemente como parte del lenguaje una
biblioteca de plantillas de contenedores2 (STL, The Standard Template Library:
La Biblioteca Estándar de Plantillas), que en el capítulo siguiente diseccio-
naremos pedagógicamente. Pero yendo más allá descubriríamos, en palabras de
Stroustrup, que la génesis de las plantillas se basa "en el deseo de expresar la
parametrización de clases contenedoras"3, situando de esta manera en un lugar
privilegiado, por su importancia, al diseño y uso de tales contenedores. En reali-
dad C++ se convierte, así, en uno de los lenguajes mejor preparados para la ges-
tión de contenedores. Y aunque esto pueda tildarse de opinión subjetiva, a lo que
sigue me remito, pues la intención primera de este capítulo es, además de pon-
derar la importancia de los contenedores, llegar a explicitar los mecanismos, sin-
taxis, sutilezas y carencias que animan el soporte conceptual en que se apoyan
las plantillas (templates) en C++. Pero el tema es largo, muy largo, así que ¡al gra-
no!.

DISEÑO DE CONTENEDORES

Aparte de lo anteriormente expresado, los contenedores son vitales para los pro-
gramadores por una razón adicional: virtualmente todas las actuales bases de
objetos (OODBSs: Object-Oriented Database Systems) gestionan buena parte de
los accesos a objetos mediante estas estructuras. Pero a esto atenderemos más
adelante. Por ahora simplemente consideraremos las distintas estrategias que,
de forma inexorable, nos conducirán al empleo de plantillas en C++. En principio
para el diseño de contenedores en C++ podemos tener en cuenta, básicamente,
cuatro enfoques distintos: lo obvio, el preprocesado, la herencia y las plantillas.
Pero vayamos paso a paso.

2
En verdad la adición de características en C++ es tan importante, en comparación con lo mos-
trado por la mayoría de compiladores comercializados en la actualidad, que realmente sorprende.
Algún colega ha llegado a decir que C++ ha dejado de ser un lenguaje para transformarse en un
entorno. Bueno, es una idea.
3
Bjarne Stroustrup, The Design and Evolution of C++, 1994, Addison-Wesley, Página 337.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 25
LA OPCIÓN INGENUA: CUESTIÓN DE ÁMBITOS

Una primera solución elemental consiste en usar un pequeño truco para sustituir
en cada ocasión únicamente el tipo de objetos a usar por el contenedor, sin tener
que modificar la definición de éste. De esta manera, al definir la siguiente clase
en un fichero denominado "coleccio.h":

class Coleccion {
public:
bool inserta( TIPO );
Coleccion& operator|=( TIPO );
// etc., etc.
};

si queremos una colección de enteros deberíamos codificar algo de la siguiente


guisa:

#define TIPO int


#include "coleccio.h"
Coleccion coleccionDeEnteros;
coleccionDeEnteros.inserta( 17 );

Si por otra parte queremos -puro capricho- una colección de decimales, podría-
mos codificar, en un ámbito diferente, así:

typedef double TIPO;


#include "coleccio.h"
Coleccion coleccionDeDecimales;
coleccionDeDecimales.inserta( 1.1235813 );

Lo que en ambos casos hemos hecho es, en primer lugar, asignar una sustitución
del identificador TIPO con sentido para el lenguaje, para después, usando del
preprocesador, incluir el código de definición de la clase colección (mediante la
directiva "#include"). Seguidamente se define una variable (objeto) del tipo Co-
leccion (que en realidad, en ese momento, no es una clase colección genérica,
sino que es ya, de hecho, una colección de enteros en el primer caso y otra de
doubles en el segundo). Por último se llama a una función miembro de la clase
con el argumento adecuado en cada caso. ¡Ea, parece que esto funciona!, podría
aquí exclamar el incauto lector. Pero no, desafortunadamente ¡esto no funciona!
En primer lugar la clase Coleccion depende, para su compilación, de una defini-
ción exterior a la misma, de forma que se genera una necesidad de secuenciali-
dad bien difícil de documentar. Por otro lado resulta que si compilamos las líneas:

#define TIPO double


#include "coleccio.h"

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 26
Coleccion unaColeccionDeDecimales;
double unDecimal = 1.23456
unaColeccionDeDecimales.inserta( unDecimal );

#define TIPO Cliente


#include "coleccio.h"
#include “cliente.h” /* nuestra clase “Cliente” */
Coleccion unaColeccionDeClientes;
Cliente unCliente( “Fulano y Cia” );
unaColeccionDeClientes.inserta( unCliente );

aparecerá en primer lugar un aviso indicándonos que la redefinición de TIPO co-


mo Cliente no es idéntica a su anterior definición como double (¡naturalmente!).
Pero, atención, se trata de un aviso, no de un error, y esto quiere decir que el
compilador simplemente ignorará esta nueva redefinición, de forma que al llegar
a la línea en que se incluye de nuevo el fichero “coleccio.h”, con la ingenua inten-
ción de reprocesar el identificador TIPO, podrán pasar dos cosas: si el fichero de
cabecera incluye la típica construcción

#ifndef coleccio_h
#define coleccio_h

// definición de clase

#endif /* coleccio_h */

entonces, dado que se ha definido “coleccio_h” en la pasada anterior del prepro-


cesador, se ignorará en la práctica la nueva lectura del fichero; si, por el contrario,
no se incluyesen estas instrucciones (con la vaga y fatal idea de forzar el nuevo
preprocesamiento de la clase) entonces aparecería un error por “múltiple declara-
ción de la clase Coleccion”. En cualquier caso la última línea originará un error por
“desajuste de tipo en el parámetro”: se espera un double, y aquí se le pasa un
Cliente (recordemos que el compilador ha ignorado nuestros infantiles deseos).
De aquí se colige que, con este esquema, no podríamos usar dos colecciones
que manejen distintos tipos en el mismo ámbito. Y esto es inaceptable. Debemos
encontrar una forma de compatibilizar tales dos distintas colecciones. ¿Y qué tal
-preguntaría aquí el lector inquieto- si definimos la primera clase con el identifica-
dor "doubleColeccion" y la segunda con el de "ClienteColeccion"? Bueno, queri-
do lector, esto puede funcionar. Veámoslo.

LA TRAMPA DEL PREPROCESADOR

Bueno, necesitamos un fichero "ppcolecc.h" en el que escribiremos lo siguiente,


usando de la sintaxis del preprocesador:

// la siguiente línea crea un nuevo identificador

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 27
// como yuxtaposición de dos identificadores dados.
4
#define concatenar(a,b) a##b
// seguidamente se codifica la macroinstrucción que
// generará un nombre específico para cada colección.
#define Coleccion(tipo) concatenar(tipo,Coleccion)
// y aquí viene la definición "genérica" de nuestra
// clase contenedora.
#define declara(Coleccion,tipo) \
class Coleccion(tipo) { \
public: \
Coleccion(tipo)(); \
Coleccion(tipo)(const Coleccion(tipo)&); \
bool inserta(tipo); \
// etc., etc. \
};

De esta forma si codificamos

#include <ppcollec.h>
declara(Coleccion,int)
Coleccion(int) miColeccionDeEnteros;

el preprocesador realizará el siguiente trabajo por nosotros:

class intColeccion {
public:
intColeccion();
intColeccion(const intColeccion&);
bool inserta(int);
// etc., etc.
5
};
intColeccion miColeccionDeEnteros;

y, de la misma manera, si codificamos

declara(Coleccion,double)

4
Este código para concatenar identificadores no es, como resulta fácil suponer, portable entre
sistemas, de tal manera que también se usan lindezas como las siguientes:

#define concatenar(a,b) a/**/b


#define concatenar(a,b) a\
b
5
Atención: la macroexpansión de la definición de la clase se producirá en una sola línea (noten los
caracteres "\" al final de cada línea de la definición de la macro del preprocesador). Aquí se ha
mejorado la presentación debido a razones tipográficas, pero hay que tener en cuenta que al-
gunos editores -afortunadamente muy pocos hoy en día- pueden truncar líneas demasiado largas,
ocasionando errores de compilación difíciles de detectar y, por ende, corregir.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 28
Coleccion(double) miColeccionDeDoubles;
miColeccionDeDoubles.inserta( 1.1135917 );

obtendremos la definición de una clase con identificador "doubleColeccion", así


que ahora ya podemos usar en el mismo ámbito dos colecciones que, con el mis-
mo funcionamiento interno, contienen objetos de tipos distintos: ints y doubles,
pues sus identificadores respectivos son también distintos. ¡Vaya, -exclamará
aquí el lector de antes- mi idea ha funcionado perfectamente! Pues no, reinciden-
te lector. Ha fallado de nuevo, y esto se está convirtiendo en un hábito. Resulta
que, al fin, esta idea puede devenir mala malísima de la muerte. Echémosle, si no,
un vistazo a la siguiente galería de horrores:

declara(Coleccion,flota) /* aquí empiezan los problemas */


Coleccion(fluat) miRaraColeccion; // ¡error!
Declara(Coleccion,*int) /* aquí la tipografía intimida */
Coleccion(Coleccion,int*) misInts; // ¡nuevo error!
declara(Coleccion,const Fecha) /* ¡horror! */
Coleccion(const Fecha) misFechas; // ¡la hecatombe!
declara(Coleccion,char*) /* ?! */
Coleccion(char*) misCaracteres; // ¿qué pasará aquí?

Se deja como ejercicio al lector la comprobación individual de los errores en cada


línea. En definitiva, se puede fácilmente apreciar que las macrosustituciones del
preprocesador ("macros", si usamos un flagrante neologismo) no entienden de
tipos: su "inteligencia" está limitada a la mera sustitución léxica. Así, por ejemplo,
la línea

declara(Coleccion,char*)

intentaría generar el código de una clase con un identificador tal que así:

char*Coleccion

lo que, naturalmente, generaría un error en compilación sobre una línea que en


realidad no está visible en el código fuente. Claro que esto podría arreglarse de la
siguiente forma:

typedef char* punteroAChar;


declara(Coleccion,punteroAChar)

pero lo que aquí estamos haciendo es ... suplir el trabajo que usualmente el len-
guaje hace por nosotros, reconociendo a la vez la incapacidad del preprocesador
respecto de los tipos y su alejamiento del lenguaje. Una de las mejores caracterís-
ticas de C++ es su fuerte chequeo de tipos que, en el caso de las macrosustitu-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 29
ciones, se pierde como se perdió Cuba: de forma irremediable. Húyase, pues,
del preprocesador siempre que sea posible ("Me gustaría ver abolido el Cpp"6,
Stroustrup dixit). Pero, entonces, ¿qué hacer respecto de los contenedores gené-
ricos? Bueno, la mayoría de bibliotecas de C++ se basan en un uso más o menos
intensivo de la herencia mediante la derivación de clases. ¿Qué tal si aplicamos
la derivación y los mecanismos de conversión, implícitos o no, de C++ para pro-
curar la genericidad deseada? Al grano.

CUANDO LA HERENCIA ES DISPUTADA

Si deseamos un contenedor genérico (esto es, que admita objetos de cualesquie-


ra de los tipos que usamos, o de un subconjunto de estos), necesitamos que to-
dos los objetos que hayan de formar el "contenido" deriven de una misma clase
base, usualmente llamada "Objeto". Volvamos a nuestra clase:

class Coleccion {
public:
bool inserta( Objeto* );
// etc., etc.
};

Si deseamos una colección en la que insertar libros, lo único que necesitamos es


hacer derivar la clase "Libro" de "Objeto":

class Libro : public Objeto {


public:
Libro( char* titulo = 0 );
// sigue resto descripción clase
};

de forma que ahora podríamos codificar lo siguiente:

Coleccion miBiblioteca;
Libro* miPreferido = new Libro( "The Devil's Dictionary" );
miBiblioteca.inserta( miPreferido );

y esto funciona perfectamente, pues aunque en la última línea se ha usado como


argumento un puntero a un objeto Libro donde se esperaba un puntero a un objeto
de clase Objeto, dado que aquél deriva públicamente de éste, se produce -como
el lector ya sabe- una conversión implícita de tipos, y el puntero a Libro se trans-

6
Cpp es el acrónimo de "C Preprocessor" -o sea, "Preprocesador de C"-, lo que indica suficiente-
mente su carácter básico de herencia de C adquirida por C++. Como en las malas herencia de la
vida jurídica real, no hay más remedio que resignarse: se trata de una deuda.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 30
forma en un puntero a Objeto, y todo funciona como en los cuentos de Perrault.
Bien, sigamos. Pero antes una consideración adicional: ¿y si deseáramos man-
tener una colección de bibliotecas -o sea, una colección de colecciones? Pues no
tendríamos más que volver a aplicar el esquema derivativo. Esto es, necesitaría-
mos que la misma clase Coleccion derivara también, directa o indirectamente, de
la clase Objeto:

class Coleccion : public Objeto {


// sigue descripción de la clase
};

para así poder codificar lo siguiente:

Coleccion misBibliotecas;
Coleccion* miBibliotecaErotica = new Coleccion;
misBibliotecas.inserta( miBibliotecaErotica );

Por otro lado ahora podemos mantener perfectamente en el mismo ámbito varias
colecciones conteniendo tipos distintos, pues en realidad se tratará de objetos
del mismo tipo: una colección de enteros es exactamente del mismo tipo que otra
de caracteres (ambas son instanciaciones de la clase Coleccion). Resulta así que
todas nuestras clases derivarán de Objeto: o sea todas las instancias que mane-
jemos serán Objetos (no olvidemos que la derivación pública en C++ representa
relaciones "ES-UN"). Y aquí nos enfrentamos con el primer problema. ¿Qué pasa
si codificamos lo siguiente?

Coleccion misEnteros;
int* pEntero = 7;
misEnteros.inserta( pEntero );

¡pues que obtendremos un bonito error! Pasamos como argumento un puntero a


un int, pero "int" no es un tipo derivado de "Objeto", y por lo tanto el compilador se
queja. ¿Qué hacer, entonces? Pues tenemos dos soluciones: bien codificamos
una serie de funciones miembros sobrecargadas que puedan manejar los tipos
predefinidos:

class Coleccion : public Objeto {


public:
bool inserta( int );
bool inserta( float );
// ... resto tipos predefinidos
bool inserta( Objeto* );
// sigue resto descripción clase
};

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 31
bien encapsulamos tales tipos en sendas clases derivadas, éstas sí, de la clase
Objeto, de la forma:

class Entero : public Objeto {


friend Entero operator+( const Entero&, const Entero& );
private:
int valor;
public:
Entero( int entero = 0 ) : valor( entero ) {}
// etc., etc.,
};

de tal manera que ahora sí se podrá escribir

Entero* pEntero = new Entero( 4019 );


Coleccion* misEnteros = new Coleccion();
misEnteros->inserta( pEntero );

¿Cuál es la mejor opción? Bueno, piénsese que codificar una clase simuladora
de un tipo predefinido no es un ejercicio trivial, y habría que hacerlo para cada uno
de los tipos definidos en el sistema: float, long, char, etc. En contrapartida, si nos
hacemos con el conjunto de tales clases en el futuro podremos utilizarlos sin codi-
ficación adicional en cualquiera de los contenedores que manejen Objetos, pero a
costa de obligar al programador a no usar los tipos predefinidos, de tal manera
que se influye en el posible código ya existente cuando se intentan usar estos
contenedores, so pena de usar una mixtura de operadores de conversión y de
sobrecarga, algo que casi nunca funciona totalmente de la forma deseada. La
opción de la sobrecarga de las funciones miembros es, por otro lado, menos in-
trusiva, pero obliga a la codificación repetitiva de las funciones en cada nueva
clase contenedora, de forma que cada nueva función miembro que se adicione y
contenga punteros a Objeto en su signatura deberá ser replicada para cada uno
de los tipos predefinidos. Si suponemos una jerarquía estable de contenedores
esta sería la opción menos desventajosa: de hecho es la elegida por la mayoría
de bibliotecas comerciales.

Ya hemos visto que el enfoque derivativo en contenedores presenta problemas


respecto de los tipos predefinidos. Ahora veremos que tales problemas persisten
en los tipos definidos-por-el-usuario (las clases): nuestro enfoque se basa en que
los objetos de una determinada clase podrán ser insertados en nuestros contene-
dores si derivan de la clase Objeto, pero ¿qué ocurre si necesitamos utilizar obje-
tos de clases pertenecientes a una biblioteca comercial aparte? Imaginemos, por
ejemplo, que una jerarquía de clases comercial nos proporciona una clase "Politi-
co" que deseamos usar en nuestra aplicación: ¿qué sabe tal jerarquía de nuestra
clase Objeto? Probablemente nada, así que añadir a tal clase una nueva clase
base (Objeto) va a resultar, si no imposible, sí ciertamente costoso, porque, en

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 32
cualquier caso, la biblioteca comercial no está preparada para conjuntarse con la
nuestra. Y además habría que modificar el código de la biblioteca comercial: algo
calificado como anatema, pues habría que repetir tales modificaciones (y volver a
comprobar su repercusión) en cada versión de la misma. Y, con todo, suponemos
que disponemos del código fuente de la biblioteca para poder modificar las cla-
ses, algo que no siempre ocurre.

De lo anterior se infiere la necesidad -al menos hasta que examinemos esta


cuestión bajo la luz de la herencia múltiple- que todos los contenidos hayan de ser
"Objetos", pero la misma naturaleza polimórfica del contenedor fuerza el siguiente
corolario: "cualquier Objeto (instancia de una clase derivada de Objeto) puede
insertarse en nuestro contenedor". Esto significa que no podemos garantizar la
homogeneidad de los tipos que maneje un contenedor dado. Examinemos el si-
guiente código:

class ColeccionOrdenada : public Coleccion {


public:
Objeto* primero();
Objeto* ultimo();
// etc., etc.
};

class Persona : public Objeto {


public:
Persona( char* = 0 );
int edad();
// etc., etc.
};

class ElementoQuimico : public Objeto {


public:
ElementoQuimico();
ElementoQuimico( char*, char* );
char* nomenclatura();
// etc., etc.
};

ColeccionOrdenada miBolsa;
Persona analista( "Ricardo Lara" );
ElementoQuimico analito( "Sodio", "Na" );
miBolsa.inserta( analito );
miBolsa.inserta( analista );

Estamos introduciendo indiscriminadamente en la misma colección tanto objetos


de tipo Persona como objetos de tipo ElementoQuimico. ¿Qué ocurrirá si escri-
bimos lo siguiente?

cout << miBolsa.ultimo()->edad();

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 33
El lector pensará que si el último elemento es una Persona la línea funcionará per-
fectamente. Pues no. Resulta que la función miembro "ultimo()" devuelve un punte-
ro a Objeto (en realidad un puntero a la porción de Objeto contenida en el objeto
accedido), y sobre este puntero se aplica la función "edad()", pero la clase Objeto
no tiene definida tal función, por lo que el compilador arrojará un lógico error. Ne-
cesitamos, pues, un cast que troque el puntero a Objeto en un puntero a nuestra
clase deseada (esta es, Persona), y explicitaré la sintaxis con profusión de parén-
tesis:

cout << ( (Persona*)(miBolsa.ultimo()) )->edad();

Esto es lo que se denomina un "downcast" (o sea, un cast de una clase base a


una clase derivada de ésta). Así, claro, la línea funciona si el último elemento es
una Persona. Pero ¿qué ocurre si tal elemento es un ElementoQuimico? Pues
que se estará aplicando un cast sin sentido a tal objeto, obteniendo resultados
impredecibles. Y encima este error no lo puede detectar el compilador (es un típ i-
co error de ejecución). Pero la cosa se puede complicar aún más: imaginemos la
siguiente jerarquía:

class Ente : virtual public Objeto { /* ... */ };


class Elemento : virtual public Objeto { /* ... */ };
class Persona : public Ente, public Elemento { /* ... */ };

En este caso Persona derivaría "virtualmente" de Objeto, y entonces ... ¡la línea
con el cast originaría un error en compilación!, pues resulta que el lenguaje no
permite un "downcasting" de una clase base virtual a una clase derivada de ésta
(y el lector que dude de esta afirmación debería urgentemente consultar el ARM).
La biblioteca NIH (cuya referencia encontrará más adelante el lector) dispone, sin
embargo, de una triquiñuela para salvar esta imposibilidad, facilitando un conjunto
de funciones estáticas con identificador "castdown(...)" que, mediante macrosubs-
tituciones del preprocesador, se generan automáticamente para cada clase del
sistema. Pero, como ya sabe el lector, las soluciones del cpp no nos gustan, así
que ... ¡al saco!

Naturalmente una posible solución es evitar la aplicación de moldeos (casts) en


contenedores de este tipo (y no es una idea descabellada intentar evitarlos en
general), para lo cual deberíamos dotar a la clase base de funciones virtuales
(usualmente puras) que deberán ser redefinidas en cada clase derivada, con in-
dependencia de que tales sean efectivamente usadas o no por la clase derivada.
De todas formas, a pesar que Persona y ElementoQuimico comparten algún que
otro elemento común, la edad no puede aplicarse a un ElementoQuimico, por lo

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 34
que el cast aparece inevitable 7. Para poder aplicar una cierta validación de tipos
necesitaríamos un contenedor como el siguiente:

class ColeccionDePersonas : public Objeto {


public:
bool inserta( Persona* );
// etc., etc.
};

Lo que ocurre es que de esta manera ... ¡perdemos por completo la genericidad
objeto de las presentes disquisiciones! Podríamos, por otra parte, intentar la si-
guiente desgraciada triquiñuela, usando del mecanismo de identificación de tipos
en tiempo de ejecución (el esquema se ha simplificado de forma grosera para
evitar perdernos en profundidades que ahora mismo resultan un poco lejanas,
sobre todo teniendo en cuenta que la clase "typeid" está pendiente todavía de
definición en el estándar del lenguaje 8):

#include <typeinfo.h>

class Coleccion : public Objeto {


public:
Coleccion( Objeto* = 0 );
type_info& tipo() {
return typeid( tipoDeColeccion );
}
bool inserta( Objeto* );
private:
// el siguiente objeto se usará para determinar
// el tipo de la colección, en razón del tipo
// del argumento usado en su creación.
Objeto* tipoDeColeccion;
// etc., etc.
};

Coleccion::Coleccion( Objeto* tipoDeObjeto ) :


tipoDeColeccion( 0 )
{
if ( tipoDeObjeto )
tipoDeColeccion = tipoDeObjeto->clone();
// sigue resto del constructor

7
A no ser que creemos lo que se denomina una "Flat Base Class", o sea, una clase base dotada
de un abrumador número de funciones virtuales puras, comprehensivas de toda la posible fun-
cionalidad de sus clases derivadas, presentes y futuras. Esto suele dar lugar, en jerarquías poco
meditadas, a verdaderos pastiches formales.
8
El ejemplo siguiente se basa en lo que se denomina RTTI (Run-Time Type Identification:
Identificación de Tipos en Tiempo de Ejecución), y a pesar de la actual indefinición del estándar,
ya se pueden encontrar implementaciones concretas en compiladores, como por ejemplo en
Borland C++ 4.XX.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 35
}

bool Coleccion::inserta( Objeto* candidato )


{
if ( tipoDeColeccion &&
!( tipo().before( typeid( candidato ) ) )
return FALSE;
// aquí se efectúa la operación de inserción
return TRUE;
}

Con esta esquema se daría la posibilidad de crear "Colecciones con Tipo" (o


"Colecciones Tipificadas") pasando un argumento en el constructor que, copiado
a un dato miembro de la clase, mantenga la información del tipo de objeto que se
desea manejar. En cada llamada a la función de inserción se compararía el tipo
del objeto candidato a insertar con el tipo del objeto con el que se creó, en su ca-
so, la colección, de manera que si el objeto candidato pertenece a la misma clase
que tal objeto, o a una clase derivada de ésta (de ahí el “before(...)”), entonces se
procederá a la inserción; en otro caso no se efectuará la inserción y la función
devolverá un valor de FALSE. Bien, parece que esto puede funcionar, pero ... ¡no
convence del todo!, pues recuerda los tan denostados campos selectores de tipo
(con mejoras, naturalmente). Resulta, así, que la rapidez y efectividad de nuestro
código quedan efectivamente mermadas, el tamaño de la colección aumenta al
añadir un nuevo dato miembro, y se nos fuerza a codificar en cada clase derivada
de Objeto una función virtual de clonación que devuelva un puntero a un nuevo ob-
jeto copia de ése al que se aplica. Aparte de que, al producirse la comparación
en tiempo de ejecución, se pierde el chequeo estático de tipos, tan propio de
C++.

Además, a los inconvenientes expresados se añaden los debidos a la derivación


forzosa desde la clase Objeto. Tenemos, en general, que en el tipo de contenedo-
res que ahora nos ocupa "todo son Objetos". Esto significa que, al forzar un grado
mínimo de derivación en cada clase y en virtud de los mecanismos de derivación
propios de C++, cada vez que se llame a un constructor de un objeto "contenido"
se llamará también, de forma dirigida por la lista de inicialización o no, a cada
uno de los constructores de las clases base de esa a la que el objeto pertenece.
En todo caso siempre se llamará al constructor de la clase Objeto, y si éste no es
trivial entonces una línea como

Persona multitud[ 100000 ];

originará cien mil llamadas al constructor por defecto de Objeto, y, francamente, el


asunto puede resultar muy costoso. Además cada porción de la clase base Obje-
to contenida en objetos de clases derivadas de ésta incluirá un puntero a la tabla
de funciones virtuales de Objeto, utilizando así una memoria en ocasiones
valiosísima.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 36
Bueno, lo estamos pintando muy negro, pero la verdad es que los problemas del
enfoque derivativo no acaban aquí: pasemos a un nuevo capítulo de molestias.

Dado que la conversión implícita de tipos exige el paso de argumentos mediante


punteros y las referencias son desechadas, las funciones miembros de nuestros
contenedores que manejen elementos no pueden trabajar con objetos. Al trabajar
con punteros aparecen, pues, una serie de errores, relacionados con lo que se
denomina CRUD 9, demasiado comunes para considerarlos meras curiosidades,
casi todos ellos basados en los desajustes de gestión de memoria entre conte-
nedores y contenidos. Así, por ejemplo, un esquema muy extendido es el siguien-
te:

void afiliate()
{
Coleccion partidoPolitico;
partidoPolitico.inserta(new Persona("Juan Nadie"));
// aquí, por terminar el ámbito de la función
// se destruye el objeto partidoPolitico
}

Vemos que se destruye el contenedor, pero ¿qué pasa con el objeto Persona que
hemos insertado? Pues que, al serle asignada memoria del almacenamiento li-
bre, queda sin destruir ocupando memoria útil del sistema, y además sin posibili-
dad evidente de ser accedido. Estamos suponiendo, naturalmente, que el conte-
nedor no suprime al destruirse los objetos que contiene, porque si así fuera nos
veríamos abocados a errores aún más peligrosos:

void afiliateYMuere()
{
Persona* incauto = new Persona( "Pepe Pérez" );
Coleccion* partidoMinimalista = new Coleccion;
partidoMinimalista->inserta( incauto );
Coleccion* partidoNeologista = new Coleccion;
partidoNeologista->inserta( incauto );
// se ejecuta aquí algún proceso irregular
// y se disuelve el partido Neologista
// y con éste se "disuelven" también todos
// sus afiliados, incluido nuestro "incauto"
delete partidoNeologista;
// seguidamente la caida del partido Neologista
// arrastra a la disolución al partido Minimalista
// (temas de empatía política, ya saben)
delete partidoMinimalista; // ¡el desastre!
}

9
CRUD es el acrónimo de "Create, Read, Update & Delete" (Crear, Leer, Actualizar y Borrar) y se
refiere a las operaciones más usuales que pueden aplicarse a objetos.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 37
¿Cuál es el problema aquí? Pues que al destruirse el partido Minimalista intenta
destruir a todos sus miembros, pero resulta que el objeto al que apunta el identifi-
cador "incauto" ya ha sido destruido anteriormente, por lo que el resultado de
aplicar el operador "delete" a tal puntero es impredecible. Naturalmente obten-
dríamos el mismo desastroso resultado si destruyéramos "a mano" los objetos
contenidos. Esta es, de hecho, la poderosa razón por la que los contenedores de
la mayoría de bibliotecas comerciales no destruyen a sus objetos contenidos
cuando se destruyen ellos mismos. Otra solución muy socorrida es proveer a la
clase Objeto de las siguientes funciones:

class Objeto {
public:
// copia superficial o referencial de objetos
virtual Objeto* shallowCopy();
// copia profunda o estructural de objetos
Objeto* deepCopy();
// convierte una deepCopy en una shallowCopy
virtual void deepenShallowCopy();
// etc., etc., como siempre
};

de forma que shallowCopy() copia "bit a bit" un objeto (usando normalmente el


constructor de copia por defecto), mientras que deepCopy() replica completa-
mente el objeto obteniendo uno nuevo sin referencias del original. Se deja, así, a
criterio del usuario de las clases el tipo de copia deseado, de manera que, por
ejemplo, puede manejarse un contenedor que destruya su contenido usando de la
función deepCopy() y duplicando, por tanto, los objetos antes de insertarlos.

Pero los peligros de los punteros no acaban aquí. Consideren, si no, el siguiente
código:

Coleccion partidoContrapopular;
void afiliateYVeras()
{
Persona personaDePaso( "Eutimio" );
// atención: seguidamente se inserta en la colección
// un puntero a un objeto de ámbito local.
partidoContrapopular.inserta( &personaDePaso );
// aquí se acaba el ámbito de la función y se
// destruye el objeto automático "personaDePaso"
}

Nos encontramos con una colección que contiene, como en el caso anterior, un
puntero a un objeto que ya no existe. Cualquier operación sobre la colección que
afecte a tal puntero originará ... cualquier cosa, usualmente muy desagradable.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 38
Vemos, pues, que se dan muchos problemas pero, demonios, no todo es tan ma-
lo en el esquema de diseño de contenedores por derivación. Después de todo es
un enfoque que funciona en Smalltalk <sic> y en las jerarquías de clases C++
consideradas como un "idioma" o "patrón" (pattern) Smalltalk, casi todas ellas
descendientes de la biblioteca NIH de Keith Gorlen et al.10, aunque en ésta el me-
canismo de identificación de tipos recae, de una forma muy propia de Smalltalk,
en una clase denominada Class. Además estas jerarquías cósmicas (pues así se
denominan las jerarquías de clases que tienen una única clase base) son muy
prácticas cuando se trata de obtener una adecuada granulación de objetos en
base a sus relaciones de herencia. Vamos a intentar explicarlo con un ejemplo:
supongamos la siguiente jerarquía (que respeta escrupulosamente la directriz ES-
UN, aunque de estas cuitas ya hablaremos más adelante):

Objeto
Bien
BienMueble
Prenda
Pulsera

Supongamos que montamos un esquema de codificación que, para cada clase,


inserte cada nuevo objeto en una colección representativa del tipo (lo que se de-
nomina -en inglés- el "extent"), y lo extraiga en el momento de su destrucción.
Veámoslo:

class Objeto {
public:
Objeto() { extent.inserta( this ); }
~Objeto() { extent.extrae( this ); }
private:
static Coleccion extent;
// etc., etc.
};

class Bien : public Objeto {


public:
Bien() { extent.inserta( this ); }
~Bien() { extent.extrae( this ); }
private:
static Coleccion extent;
// etc., etc.
};

10
La biblioteca NIH es, para gozo del lector, de dominio público y puede encontrarse en la may-
oría de servicios on-line: Internet, BIX, Compuserve, etc. Aunque yo, personalmente, no prescin-
diría del libro en que se explicitan las decisiones de diseño de tal biblioteca, un clásico ya de la
programación en C++: Data Abstraction and Object-Oriented Programming in C++, Gorlen, Orlow
& Plexico, 1990, John Wiley & Sons, 0-471-92346-X.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 39
// aquí se repite el mismo esquema para todas las
// clases de la jerarquía, hasta llegar a la terminal

class Pulsera : public Prenda {


public:
Pulsera( String* unaMarca = 0 ) {
// la clase String tiene definido el
// constructor de copia, naturalmente
marca = new String( *unaMarca );
extent.inserta( this );
}
~Pulsera() {
delete marca;
extent.extrae( this );
}
static Coleccion& getColeccion() {
return extent;
}
private:
static Coleccion extent;
String* marca;
// etc., etc.
};

De esta manera cada vez que creemos un nuevo objeto de alguna de estas cla-
ses automáticamente se insertará en la colección estática (única para todos los
objetos de la clase) correspondiente a su tipo (con el nombre "CLASE::extent" y
accesible a través de las correspondientes funciones miembros públicas estáti-
cas, que aquí se han obviado). Así, por ejemplo, la línea

Pulsera* miPulseraDeCompromiso = new Pulsera( "Cartier" );

insertará mi pulsera de marca en el extent de pulseras. Pero, atención, como sa-


bemos que el constructor de Pulsera automáticamente llamará al constructor de
su clase base -Prenda-, y éste a su vez al de su respectiva clase base, hasta lle-
gar a la clase Objeto, resultará que el mismo objeto Pulsera se insertará en cada
una de las colecciones estáticas de las clases bases de Pulsera. ¡Vaya, pero es-
to puede resultar muy costoso! Y, ¿para qué sirve? Muy fácil: imaginemos que
deseamos relacionar, para su posterior impresión o visionado, todos los objetos
existentes del tipo (de la clase) BienMueble. Dado que las pulseras son bienes
muebles, el comportamiento lógico a esperar es que mi pulsera de compromiso
aparezca en tal relación. Y así es en este caso. Se trata, en definitiva, del polimor-
fismo aplicado a las jerarquías. Cuando se destruya el objeto pulsera:

delete miPulseraDeCompromiso;

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 40
al entrar en acción los destructores de las clases bases (en orden inverso al de
construcción, como el voraz lector ya sabe), se extraerá tal objeto de cada una de
las colecciones. Un corolario lógico es que la colección estática de la clase Obje-
to contendrá en cada momento todas las instancias de clases disponibles en
nuestra aplicación. De cualquier forma, y como ya se ha dicho, este es un esque-
ma particularmente poco eficiente, así que sólo se usará cuando el polimorfismo
que proporciona sea real y positivamente necesario (lástima que no existan toda-
vía mecanismos ni métricas orientadas-a-objetos maduras que permitan calibrar
tal necesidad).

Tenemos, pues, que el esquema de derivación simple no encaja demasiado bien


con lo que queremos. Pero, puesto que estamos en C++ y no en Smalltalk, ¿no
nos podría ofrecer alguna solución la herencia múltiple? Bueno, es justo que le
echemos un vistazo.

HERENCIA MÚLTIPLE: ¿MÁS DE LO MISMO?

Como no queremos, y normalmente no podemos conseguir, que todas nuestras


clases deriven de Objeto, vamos a intentar un enfoque matizadamente distinto. Se
trata de, para no modificar nuestra clase, crear una nueva clase que derive a la
vez de ésta y de Objeto. Esto es, si tenemos:

class Cliente {
// descripción de clase
};

crearíamos una nueva clase:

class ObjCliente : public Objeto, private Cliente {


// descripción de clase
};

de forma que así se facilitaría la parametrización de contenedores respecto de


clases de las que no podemos obtener el código fuente, o que no pudieran ser
modificadas. De cualquier forma el esquema no cambia mucho y, sin embargo,
nos procura complicaciones adicionales, pues deberemos dotar a nuestra nueva
clase de un interfaz mínimo: constructores, sobrecargas del operador de asigna-
ción, del operador de comparación, etc. Pero lo peor es, sin duda, que tendría-
mos que usar objetos de una clase distinta a la prevista: un ObjCliente NO-ES, en
sentido estricto, un Cliente, y en realidad la derivación pública de Cliente tampoco
nos solucionaría este problema de fondo. Imaginemos un sistema persistente
donde a cada objeto se le otorga un identificador único (cual es el caso de la ma-
yoría de las bases de objetos, donde a cada objeto se le asigna un OID: object
identifier): el hecho de instanciar un objeto de una nueva clase supone la asigna-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 41
ción de un nuevo identificador, de forma que no se mantiene la integridad referen-
cial, que debería aquí ser expresamente codificada (o sea: ¡problemas!).

Claro que algo se puede arreglar -nunca se arregla todo, como el desencantado
lector ya puede intuir- a costa de complicar un tanto -o mejor un mucho- el esque-
ma de derivación y de relaciones entre clases. Se trata, en esencia, de aplicar a
nuestros contenedores un patrón conocido como de clases "Sobre/Carta" monta-
do sobre un mecanismo virtual que permita la inserción y extracción de objetos en
contenedores sin tener que realizar "casts" expresos. Este patrón está suficien-
temente explicitado en el libro de Coplien11, y junto con el patrón o idioma de
"Ejemplares" proporciona soporte para una parametrización en tiempo de ejecu-
ción de nuestros contenedores. Pero quizás este modelado sobrepase el tono
resueltamente elemental del presente capítulo. Además, ¿qué demonios son "pa-
trones"? En pocas palabras: se trata de construcciones idiomáticas con persona-
lidad y estructuración propias, que conllevan particulares formas de codificar y
modelar los problemas12. Pero esto, aparte de resultar muy extenso, en realidad
es relativamente nuevo (de hecho existe en Internet un grupo de discusión de
patrones via correo electrónico al que se puede apuntar cualquier interesado), así
que volvamos a nuestras cuitas anteriores: ¿puede solucionar la conjunción de
herencia múltiple y el uso de patrones nuestros problemas de parametrización de
contenedores? Sí en buena medida, pero básicamente a costa de la eficiencia y
del chequeo estático de tipos, aparte de que se genera la necesidad de mantener
un esquema derivativo no trivial en el que hay que insertar cada nueva clase (esto
es, se hace necesario un "administrador" de la parametrización). Pero la cosa no
acaba aquí.

ITERADORES

Los contenedores agrupan objetos que, tras ser debidamente insertados,


necesitarán ser accedidos, y este trabajo lo facilitan los iteradores. Un iterador es,
en esencia, un objeto que, de forma secuencial, accede a los objetos insertos en
un contenedor, y su implementación se basa, por lo general, en un dato miembro
que apunta al contenedor sobre el que opera y en datos miembros que apuntan a
los objetos en cada momento accedidos. Algo así como:

class Iterador {
public:
Iterador( Coleccion* coleccion ) :

11
Advanced C++ Programming Styles and Idioms, James O. Coplien, 1992, Addison-Wesley, 0-
201-54855-0.
12
El lector inquieto puede leer el excelente texto “Design Patterns: Elements of Reusable Object-
Oriented Software”, de Gamma, Helm, Johnson & Vlissides, 1994, Addison-Wesley, 0-201-63361-
2.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 42
contenedor( coleccion );
void rebobina();
Objeto* operator();
Objeto* operator++();
private:
Coleccion* contenedor;
Objeto* objetoSeleccionado;
// etc., etc.
};

Se trata de un iterador “genérico” que, al operar sobre Objetos puede manejar


cualquier tipo derivado. El operador () devuelve un puntero al objeto actualmente
apuntado, mientras que el operador ++ llama a la típica función next() del conte-
nedor para devolver un puntero al próximo objeto en la secuencia. ¿Perfecto?
Bueno, como el lector ya habrá adivinado, aquí se nos plantea el mismo problema
de “downcasting” anterior. Examinemos una porción de código:

Coleccion* piara = new Coleccion;


// aquí se insertarían los distintos objetos
// de la clase Cerdo, derivada de Objeto.
piara->inserta( porky );
piara->inserta( echanove );
// etc., etc.,
// seguidamente se define un iterador
// afecto a nuestra colección
Iterador iterador( piara );
// se rebobina al principio de la colección
iterador.rebobina(); // como first()
// y ahora se accede al primer objeto
((Cerdo*)iterador())->groink();

Por supuesto que si suprimiéramos el cast a Cerdo* la última línea no funcionaría,


y pensar que la función groink() debe ser virtual en Objeto no casa con la mínima
prudencia y el decoro que se le suponen a un programador. Entonces, ¿cómo
solucionar este problema y los anteriormente expuestos? ¡Con las plantillas, natu-
ralmente! Pasemos a ello.

LAS PLANTILLAS: CON HERENCIA, SIN ELLA O A SU PESAR

¿Qué es una plantilla? En primer lugar la traducción, desafortunada o no, del tér-
mino "template", actualmente parte del lenguaje C++. Otros autores prefieren "ge-
nérico", pero personalmente pienso que esto constituye un sobreabuso del voca-
blo (adjetivo, al fin y al cabo). Se trata, en definitiva, de un concepto asociado al
de "parametrización de tipos", de manera que se usarán "plantillas" para imple-
mentar en C++ estructuras de datos (objetos contenedores, de forma más explíci-
ta) y algoritmos independientes en buena medida (aunque no absolutamente) de
los tipos de objetos sobre los que operan o se aplican.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 43
En resumen, y dado que en este capítulo no examinaremos la sintaxis de las plan-
tillas, ahora podríamos codificar una colección genérica de la siguiente forma:

template <class T> class Coleccion {


public:
bool inserta( T* );
T* extrae( T* );
// etc., etc.
};

donde "T" ha de ser sustituido por el tipo de objeto (clase o predefinido) que de-
seamos manejar en nuestra colección (y obviaremos momentáneamente el espi-
noso asunto de los punteros y los objetos en plantillas). Pero esta sustitución se
produce mediante la "instanciación" de la plantilla en una línea como la siguiente:

Coleccion< Politico > parlamento;

de tal forma que las siguientes líneas:

Perro miPerro = new Perro( "Buck" );


parlamento.inserta( miPerro );

originarán un error en tiempo de compilación (algo que no habíamos podido con-


seguir con el esquema derivativo), pues se está pasando como argumento un
puntero a un Perro, mientras que lo que se espera es un puntero a Politico. A la
vez tenemos que la siguiente línea

cout << (parlamento.extrae( pPolitico ))->partido();

funciona sin cast alguno, pues la función miembro devuelve directamente un pun-
tero a Politico (en este caso), y no un puntero a una clase base (cual era el caso
con Objeto en el enfoque anterior) que necesitaría de un "downcast" para poder
acceder a una función miembro de la clase adecuada.

Bien: hemos visto, aun muy brevemente, que el enfoque de plantillas soluciona los
problemas planteados. ¿Cuál es el siguiente paso? Parece que habría que usar
la sintaxis de plantillas para codificar las clases contenedoras que necesitemos.
Pero, ¿es lógico que cada programador haya de enfrentarse individualmente al
no-trivial problema de codificar los mismos contenedores? O sea, ¿hay que in-
ventar la rueda cada vez? ¡Naturalmente que no! El lector puede estar seguro que
existen multitud de bibliotecas comerciales con particulares implementaciones de
contenedores mediante plantillas (Rogue Wave, Borland, etc.). Pero, ¿cuál elegir?
¡Ninguna de ellas! Resulta que, como ya se anunció al principio de este capítulo,
C++ ya posee su propia biblioteca estándar de plantillas (STL), desarrollada por

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 44
Alex Stepanov en los laboratorios HP y puesta por Hewlett Packard en el dominio
público, liberando todas las licencias a ella afectas. Cualquier interesado puede
acceder al código fuente completo de la STL via ftp anónimo o vía e-mail, aunque
el fichero también puede encontrarse en distintos servicios electrónicos, como
por ejemplo en el forum de Microsoft España en Compuserve. La STL supone un
trabajo de desarrollo de unos diez años, y el código C++ es de una calidad que
impresiona, con dos particularidades notables: en absoluto se usa de la herencia
en esta biblioteca y el tratamiento algorítmico recuerda las mejores bibliotecas
fortran. Pero el uso de la STL y su integración en los esquemas de programación
en C++ es asunto prolijo que necesita de un capítulo exclusivo: el siguiente.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 45
C++ STL
3
L
a STL, acrónimo de Standard Template Library (Biblioteca Estándar de
Plantillas), es, según Stroustrup, “un entorno-marco, formado por contene-
dores, iteradores y algoritmos, que resulta grande, sistemático, limpio,
formalmente completo, comprehensible, elegante y eficiente”. Y además codifi-
cado en C++. Tantos elogios los recibe una biblioteca-marco de clases derivada
de los trabajos de Dave Musser y Alex Stepanov en Hewlett Packard, cuyo propó-
sito primero fue la producción de una estructura de código que permitiera la crea-
ción, en su seno, de algoritmos reutilizables. Tras un proceso iterativo de acopla-
miento a Scheme, Ada y finalmente C++, en Noviembre de 1.993 el propio Ste-
panov junto con Meng Lee presentaron al Comité de Estandarización ANSI/ISO
X3J16 la biblioteca, que fue aceptada como parte de la Biblioteca Estándar de
C++ en julio de 1.994 e incorporada al borrador del estándar del lenguaje.

Bien, bien: parece que la STL es fabulosa, pero, tras esta exposición histórica,
¿para qué demonios sirve? Durante mucho tiempo se ha achacado a C++ la ca-
rencia de estructuras de datos con funcionalidad suficiente para procurar las ba-
ses de una deseada genericidad en la codificación. Y la STL proporciona justa-
mente eso: un conjunto de estructuras de datos y de algoritmos que operan sobre
aquéllas. Pero sobre todo procura, según Koenig, “un entorno conceptual que fa-
cilita a los usuarios la adición de sus propios algoritmos y estructuras de datos”
de una forma ciertamente genérica: los nuevos algoritmos funcionarán con todas
las estructuras de datos (contenedores) existentes, a la vez que sobre las nuevas
estructuras de datos se podrán aplicar todos los algoritmos existentes. La STL
viene, pues, a cubrir un hueco mal tapado hasta ahora por macros y clases no-
estandarizadas, propias o de bibliotecas comerciales, usualmente poco efecti-
vas, con un uso inadecuado de la herencia, con escaso o nulo chequeo de tipos,
tan sustancial a C++.

En los páragrafos siguientes se expondrá, trufado con varios ejemplos (validados


con VisualAge C++ para OS/2 y STL<ToolKit>), un acercamiento a la STL que no
se pretende exhaustivo (toda vez que la sola descripción completa de las clases y
algoritmos involucrados supera con mucho el ámbito de un capítulo estándar,
ademas de existir un excelente manual de dominio público en que tales se deta-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 46
llan), sino más bien comprehensivo e iniciático. No se detallará, tampoco, la sin-
taxis y uso de las plantillas en C++. En definitiva mi intención es transmitir al lector
el “espíritu” de la STL y la animosidad suficiente para que pueda usarla con efec-
tividad, esperando, entre otras cosas, no vuelva a usar otra clase “vector” que la
provista por esta biblioteca13. A ello.

ESTRUCTURA DE LA BIBLIOTECA

La STL está construida sobre cinco componentes:

algoritmo define un procedimiento computacional


contenedor gestiona un conjunto de localizaciones en memoria
iterador provee un medio para que un algoritmo recorra un contenedor
objeto-función encapsula una función en un objeto reutilizable por otros componentes
adaptador adapta un componente para procurarle un interfaz distinto

aunque la relación fundamental entre componentes podría establecerse así:

algoritmos -------------------------- iteradores --------------------------- contenedores

Esto es, el núcleo de la STL son los algoritmos, que utilizan iteradores para acce-
der a contenedores. Como bien explican Stepanov y Lee, si quisiéramos codificar
‘c’ contenedores para ‘t’ tipos de datos accesibles mediante ‘a’ algoritmos, debe-
ríamos contemplar ‘c*t*a’ combinaciones. Debido a la parametrización de tipos
que procuran las plantillas, la STL sólo necesitaría ‘c*a’ combinaciones, que fi-
nalmente se reducen a ‘a+c’ por la reutilización de los algoritmos en diferentes
contenedores.

Adicionalmente los componentes de la STL son al menos tan eficientes como los
codificados a-mano (debido al uso del “inlining” y el prácticamente nulo uso de la
derivación); proveen un fuerte chequeo de tipos (mediante el uso de plantillas);
sus iteradores son generalizaciones de los punteros (pudiendo usarse donde ta-
les aparecen, singularmente en los arrays de C); y permiten el uso, sobre todo en

13
Mucha gente piensa, como Prémontal, que las bibliotecas son como las farmacias: muchos
venenos y pocos remedios. Así que prefieren las soluciones caseras a-mano. El mercado mismo
evidencia su error.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 47
algoritmos, de funciones encapsuladas en objetos (también como el uso de las
típicas funciones C/C++).

CONTENEDORES

En la STL se dan las siguientes categorías de contenedores, que en definitiva son


“objetos que contienen otros objetos”:

Secuencial el almacenamiento de ítems es lineal, con inserciones rápidas al final (vector,


...)
Asociativo estructuras adecuadas para las búsquedas asociativas rápidas (conjuntos, ...)
Adaptador interfaces a otros tipos de colecciones (colas, pilas, ...)

que a su vez encierran las siguientes colecciones concretas, todas ellas codifica-
das en C++ mediante plantillas y con las subdisiviones que se indican:

COLECCIONES DE PRIMERA CLASE


Colecciones Secuenciales
vector almacenamiento contiguo y linear de elementos, con inserciones rápi-
das al final.
bit_vector vector de bits.
deque doble cola, como un vector, con posibilidad de inserciones rápidas de
elementos en sus dos extremos.
list lista doblemente enlazada, en la que se pueden insertar elementos en
cualquier lugar.
Colecciones Asociativas
multiset conjunto no ordenado en que se permiten duplicados.
set conjunto, como el multiset, pero en el que no se permiten duplicados.
multimap colección de correspondencias de 1-a-muchos.
map colección de correspondencias de 1-a-1.

ADAPTADORES
stack pila, en la que el primer elemento en entrar es el último en salir.
queue cola, en la que el primer elemento en entrar es el primero en salir.
priority_queue cola que mantiene sus elementos en orden.

Todas las colecciones de la STL tienen definido un interfaz mínimo, compuesto


por las siguientes funciones públicas (se usará el contenedor “list” para los proto-
tipos):

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 48
Descripción Prototipo de las funciones miembros T
Constructor list() D
Constructor Copia list( const list<T>& lista ) C
Destructor ~list() D
Tamaño máximo size_type max_size() const T
Tamaño size_type size() const T
Intercambio void swap( list<T>& lista ) A
Asignación list<T>& operator=( const list<T>& lista ) A
Igualdad bool operator==( const list<T>& lista ) const C
Comparación orden bool operator<( const list<T>& lista ) const C

STL provee otras funciones de comparación (C) que se derivan (en sentido figu-
rado) de los operadores == y <, a saber: <=, >=, > y !=. En realidad STL provee
tales operadores adicionales para cualquier objeto, supuesta la existencia de los
dos primeros.

Pero veamos un ejemplo de uso de los contenedores STL:

class Politico{ /* protocolo */ };


Politico felipe, joseMaria, julio; // material de relleno
// definimos una lista doblemente-enlazada de Politicos
list<Politico> camarilla;
camarilla.push_back( felipe ); // inserta al final
camarilla.push_front( joseMaria ); // inserta al principio
// crea un iterador (que se verá más adelante) del tipo adecuado
// y lo inicializa apuntando a la primera posición de la lista
list<Politico>::iterator iterador = camarilla.begin();
// inserta un nuevo politico en la segunda posición de la lista
camarilla.insert( ++iterador, julio );
// le da la vuelta a la lista (‘julio’ sigue en medio)
camarilla.reverse();
camarilla.size(); // el tamaño actual de la lista: ‘3’

Los adaptadores son, en esencia, plantillas que procuran un interfaz especial pa-
ra los otros tipos de contenedores. Así cualquier contenedor que soporte las ope-
raciones de push_back y pop_back (como vector, list y queue) puede ser usado para
instanciar “stack”. Si se añade la operación front , entonces se podrá instanciar una
“priority_queue”. Por fin “queue” podrá instanciarse con contenedores que soporten
front , back, push_back y pop_front . Como el lector fácilmente habrá adivinado, los
adaptadores “adaptan” o “adecúan” un determinado contenedor para que adopte
un nuevo comportamiento, restringiendo ciertas características y cambiando
otras.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 49
“Bien, bien -exclamará el impaciente lector- Todo esto está muy bien, pero yo ya
uso los contenedores de la biblioteca de clases X (o de una propia) que me van
estupendamente”. ¡Estupendo! Pero el énfasis de la STL no está en los contene-
dores, sino en los algoritmos (que veremos poco después). De cualquier manera
la genericidad que otorgan las plantillas es difícilmente superable por una biblio-
teca comercial, y la efectividad de los contenedores de la STL es, por otro lado,
virtualmente inmejorable. Pero de poco sirve una mera estructura sin las
herramientas que permitan accederla, revisar y modificar tanto su propia
configuración como los ítems que contenga. Y aquí aparecen los iteradores.

ITERADORES

Según Andrew Koenig, “un iterador es algo que permite un conjunto de operacio-
nes en una estructura de datos sin decir nada sobre la naturaleza de tal estructu-
ra”. Esta definición resulta especialmente relevante dentro del entorno que pro-
porciona la STL pues ésta, centrada en los algoritmos, se encuentra con la dificul-
tad de aplicar éstos directamente en los contenedores o estructuras genéricas de
datos: algunos algoritmos funcionan con ciertas estructuras; otros no. Con la in-
troducción de los iteradores se genera un nuevo nivel de indirección que permite,
en su calidad de mediador, el perfecto entendimiento entre contenedores y algo-
ritmos. Los iteradores, así, saben poco de algoritmos o de contenedores, de for-
ma que son grandemente independientes de ambos. De esta manera los algorit-
mos se expresarán en términos de iteradores, de manera que las estructuras de
datos accedidas, en otro nivel, por los iteradores, serán independientes de los
primeros. De hecho esto es precisamente lo que diferencia a la STL de otras bi-
bliotecas de clases.

Pero, ¿son en realidad los iteradores totalmente independientes de las coleccio-


nes que acceden? O sea, ¿cualquier iterador funciona en cualquier contenedor?
Humm, parece que no, pues si, en el sentido práctico que nos ocupa, un iterador
es un objeto que permite recorrer los elementos de una colección, recorriéndola
(o “atravesándola”) con distintas estrategias, para cada estrategia quizás haya
que procurar un iterador distinto. Ciertamente los iteradores de la STL funcionan,
en general, tanto en los contenedores STL, como en los arrays de C o en los
“streams” de C++, pero esto no significa que un iterador (un tipo de iterador) con-
creto funcione a la vez en todos. Nos hacen falta, pues, varios tipos de iteradores.
Veámoslos:

• InputIterator (iterador de entrada/lectura): lee un solo ítem cada vez, en una úni-
ca dirección, y su desreferenciación (*iterator) sólo podrá ser usada a la dere-
cha de una asignación: esto es, la desreferenciación devuelve un elemento
constante. Un puntero de C operando sobre un array predefinido es un iterador
de lectura (veremos que también es de escritura, pero las dos condiciones in-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 50
cluyen la establecida). Así, por ejemplo, si consideramos el algoritmo
“max_element ”, con signatura:

template<class InputIterator>
InputIterator max_element(
// iterador en el primer ítem del rango elegido
InputIterator primero_,
// iterador tras el último ítem del rango elegido
InputIterator ultimo_ )

y cuya funcionalidad es devolver un iterador apuntando al mayor ítem dentro del


rango [primero, ultimo), para lo que usa el operador <, tenemos que, dado que
usa iteradores de lectura, podremos usarlo con arrays y punteros:

int cabala[] = { 666, 17, 7 };


cout << *max_element( cabala + 1, cabala + 2 ) // ‘17’
// la desreferenciación aplicada al InputIterator que
// devuelve el algoritmo procura el ítem deseado (sólo lectura)

pero también con contenedores STL:

vector<int> monroe; // vector cinematográfico


// inserta elementos (int) al final del vector
monroe.push_back( 90 );
monroe.push_back( 60 );
monroe.push_back( 90 );
cout << *max_element( ++monroe.begin(), monroe.end() ); //
‘90’

Repare el lector que la aplicación de las funciones miembros “begin” y “end” al


vector devuelve sendos iteradores, que pueden ser directamente usados en el
algoritmo. La aplicación, por otro lado, del operador de pre-incremento origina
que el iterador avance una posición desde el principio, posicionándose en el
ítem con valor ‘60’, de manera que el algoritmo devuelve un iterador apuntando
al tercer elemento con valor ‘90’ del vector, convenientemente desreferenciado.
Dejamos al lector suponer cuál sería el resultado si se hubiera tomado como
rango el vector entero.

• OutputIterator (Iterador de salida/escritura): Un iterador de escritura es como


un iterador de lectura, con la particularidad que su desreferenciación (*iterator)
sólo podrá ser usada a la izquierda de una asignación. O sea el iterador de
lectura sólo lee ítems, y el de escritura sólo los escribe. Veamos un ejemplo de
uso con el algoritmo “fill_n”, con signatura:

template<class OutputIterator, class Size, class T>


void fill_n (

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 51
OutputIterator primero_, // apunta al primer ítem del
rango
Size n_, // limita el rango: primero_ + n_
const T& valor_ ) // valor de relleno

y cuya funcionalidad es rellenar un contenedor asignando a cada ítem, desde el


ítem apuntado por el iterador ‘primero’ hasta el lugar ‘ n’, el valor “valor_”:

vector<int> quiniela( 15 );
fill_n( quiniela.begin(), quiniela.size(), 1 );
// el iterador de escritura que devuelve la función “begin()”
// se utiliza para escribir el valor ‘1’ en el ítem apuntado,
// y quiniela queda como { 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 }

• ForwardIterator (Iterador de lectura-escritura): se denomina así al iterador que a


la vez opera como de lectura y escritura, como por ejemplo en el algoritmo
“iter_swap”, con signatura:

template<class ForwardIterator1, class ForwardIterator2>


void iter_swap(
ForwardIterator1 a_,
ForwardIterator2 b_, )

y cuyo sentido es intercambiar los elementos apuntados por los iteradores (ca-
da iterador lee el ítem al que apunta, pero también lo escribe):

int vector<Politico> listaElectoral;


// se rellena la lista
// y se procede a aplicar el principio
// de intercambiabilidad de los politicos
iter_swap( listaElectoral.begin() + 1,
listaElectoral.begin() + 4 );
// se ha cambiado el segundo por el quinto

• BidirectionalIterator (Iterador bidireccional): se trata de un iterador de lectura-


escritura al que se puede aplicar el operador ‘--’; esto es, que puede iterar en
dos sentidos. Como ejemplo podemos considerar el algoritmo “copy_backward”,
con signatura:

template< class BidirectionalIterator1,


class BidirectionalIterator2>
BidirectionalIterator2 copy_backward (
BidirectionalIterator1 primero_, // inicio rango origen
BidirectionalIterator1 ultimo_, // ++final rango origen
BidirectionalIterator2 resultado_ ) // ++final rango desti-
no

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 52
y cuyo propósito es la copia de los elementos comprendidos en el rango [prime-
ro_, ultimo) en otro rango cuyo final está indicado por el iterador ‘resultado_’ (que
apunta a la posición tras el último elemento del rango). O sea, para copiar se
da la posición final del destino (como si se copiara “hacia atrás”), copiando
mediante el ‘operator= ’. Lo que se devuelve es otro iterador del mismo tipo que
‘resultado_’ pero apuntando al primer elemento del nuevo rango:

vector<Politico> listaPartidoD;
vector<Politico> listaPartidoI;
// se rellenan ambas listas
copy_backward( ++listaPartidoD.begin(),
--listaPartidoD.end(),
--listaPartidoI.end() );
// ahora ambas listas son iguales menos el
// primer y último elemento: la sin par realidad.

• RandomAccessIterator (Iterador de acceso aleatorio): se trata de un iterador


bidireccional que admite la adición y substracción de enteros, la computación
de la distancia entre dos iteradores y la indexación. Si usamos el algoritmo
“partial_sort”, con signatura:

template<class RandomAccessIterator>
void partial_sort (
RandomAccessIterator primero_,
RandomAccessIterator medio_,
RandomAccessIterator final_ )

y cuya funcionalidad es la de ordenar, usando operator<, los primeros n ele-


mentos en el rango [primero_, final), donde n = medio_ - primero_ (operación permiti-
da sólo en los iteradores de acceso aleatorio), dejando el resto del rango en
orden indefinido:

int array[ 7 ] = { 6, 4, 2, 1, 5, 3, 7 };
partial_sort( array, array + 4, array + 7 );
// resultado: { 1, 2, 3, 4, 7, 5, 6 }

Como vemos, los punteros de C son, en realidad, iteradores de acceso aleato-


rio, como el lector ya había adivinado. De hecho los iteradores son generaliza-
ciones de punteros, y su semántica es una generalización de la de aquéllos.
Así Musser expresamente nota que “Cualquier tipo de puntero C++, T*, obe-
dece todas las leyes de la categoría de iteradores de acceso aleatorio”.

En resumen, si ‘i’ y ‘j’ son iteradores y ‘n’ un entero cualquiera, se cumple:

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 53
Iteradores Todos Input Output Bidirectional Random Access
Operaciones ++i *i (const) *i --i i += n
permitidas i++ i == j i=j i-- i -= n
i != j i+n
i-n
i[ n ]

dándose la siguiente relación entre iteradores:

input
forward bidirectional random access
output

de forma que donde se espera un cierto iterador, puede aplicarse cualquier itera-
dor a su derecha. Y, claro, aquí el lector podría exclamar: “¡Una jerarquía de
herencia de iteradores!”. Pues no, irredento lector: nada de jerarquías: aunque
parezca lo contrario, la clase del iterador bidirectional no “deriva” de la clase del
iterador forward, como no lo hacen tampoco las demás. Andrew Koenig acerta-
damente afirma que habría que pensar en las relaciones entre iteradores como
“herencia conceptual”, pues un iterador es una familia de tipos relacionados en
razón de formas que no son directamente expresables en C++.

Además de los iteradores expuestos, la STL provee otros igualmente útiles: is-
tream_iterator y ostream_iterator (que operan como cin y cout respecto de streams
C++ de entrada y salida, respectivamente), reverse_bidirectional_iterator y rever-
se_iterator (que iteran hacia atrás), insert_iterator (inserta en la posición a que apun-
ta), back_insert_iterator y front_insert_iterator (sólo insertan al final y al principio de un
contenedor, respectivamente), etc.

¡Demasiados iteradores! -podría aquí perfectamente exclamar el lector: ¿Cómo


saber qué iterador aplicar en cada contenedor o algoritmo? Bueno, respecto de
los algoritmos sólo hay que mirar su signatura o prototipo; respecto de los conte-
nedores, he aquí la tabla:

Tipo de iterador Contenedores asociados


RandomAccessIterator vector, bit_vector, deque
BidirectionalIterator list, multiset, set, multimap, map
sin iteradores stack, queue, priority_queue

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 54
donde se explicita algo evidente: únicamente las “Colecciones de Primera Clase”
(esto es, contenedores secuenciales y asociativos) pueden ser accedidas por
iteradores. La ligazón entre contenedores, iteradores y algoritmos se produce así:
la descripción de los contenedores incluye la categoría de los tipos de iteradores
que proveen, mientras que los algoritmos genéricos incluyen (en su prototipo) la
categoría de iteradores con que trabajan. Así, para evitarle al usuario la consulta o
aprendizaje de tablas como la anterior, cada contenedor define una serie ade-
cuada de typedefs, de forma que la notación contenedor<class T>::iterator enmascara
los tipos:

typedef Categoría de iterador


vector<T>::iterator RandomAccessIterator
deque<T>::iterator RandomAccessIterator
bit_vector<T>::iterator RandomAccessIterator
list<T>::iterator BidirectionalIterator
ContenedorAsociativo<T>::iterator BidirectionalIterator

Pero además se dan los siguiente útiles typedefs:

iterator const_iterator reverse_iterator const_reverse_iterato


r

para iterar adecuadamente en colecciones “escribibles” o de “sólo-lectura”, hacia


adelante o hacia atrás. De esta manera, podríamos trabajar con los iteradores de
la siguiente guisa:

list<Politico> cuadrilla;
// aquí se rellena la lista, y seguidamente
// se define un iterador del tipo adecuado
// (que en este caso será bidireccional)
list<Politico>::const_iterator i = cuadrilla.begin();
// y ahora se utiliza el iterador
for ( i; i != cuadrilla.end(); i++ )
// supondremos que la clase Politico sobrecarga
// el operador de inserción (y esto no parece des-
// cabellado, pues los políticos lo sobrecargan todo).
cout << *i << “\n”; //imprime la lista
// Y ahora al revés
list<Politico>::const_reverse_iterator ri;
// y en lo siguiente aparecen dos nuevas funciones marcadas
// por una r (reverse) inicial: rbegin() devuelve un iterador
// posicionado en el ultimo ítem, mientras que rend() devuelve

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 55
// un iterador posicionado inmediatamente antes del primer ítem.
// Su uso es evidente para iteradores que operan al revés.
for ( ri = cuadrilla.rbegin(); ri != cuadrilla.rend(); ri++ )
cout << *i << “\n”;

dejando que los typedefs hagan el trabajo de elección del iterador correcto por no-
sotros. ¿No es sencillamente fantástico?

ALGORITMOS

He aquí el corazón de la STL: algoritmos reutilizables. Y es que a pesar de la im-


portancia de los algoritmos que acompañan a la biblioteca, lo verdaderamente
significativo se encuentra en el entorno-marco que procura protocolos exactos
para la adición de otros muchos algoritmos, tal y como hemos visto al revisar los
iteradores.

Los algoritmos en la STL hacen uso de iteradores y objetos-funciones (de entre


ellos los “predicados”) para acceder a los elementos de los contenedores. De
entre los numerosos algoritmos que vienen con la biblioteca, y que el lector puede
escudriñar en el completo manual, podemos mencionar:

Algoritmo Descripción
for_each aplica una función a cada uno de los objetos en un rango dado
find localiza un ítem en una secuencia
adjacent_find localiza la secuencia consecutiva en un rango dado
find_if halla el elemento que satisface un predicado en un rango dado
count cuenta las ocurrencias de un valor en un rango determinado
count_if cuenta los elementos que satisfacen un predicado en un rango dado
search encuentra una secuencia contenida en otra
binary_search localiza un elemento perteneciente a una secuencia ordenada
fill rellena un determinado rango con un valor
fill_n rellena los n primeros elementos con un valor dado
min devuelve el minimo de dos elementos
min_element devuelve el minimo elemento dentro de un rango
max devuelve el maximo de dos elementos
max_element devuelve el máximo elemento dentro de un rango
sort ordena los elementos en un rango dado
partial_sort ordena los elementos de un rango hasta el término n-simo
set_difference genera un conjunto con la diferencia de las colecciones A y B (A-B)
set_intersection genera un conjunto con los elementos comunes a dos secuencias
set_union genera un conjunto con los elementos pertenecientes a AUB
merge integra dos listas ordenadas en una única lista ordenada
+++ +++

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 56
Pero veamos un ejemplo de un algoritmo eminentemente práctico: “for_each”, que
con signatura

template<class InputIterator, class Function>


Function for_each {
InputIterator primero_,
InputIterator ultimo_,
Function funcion_ }

tiene como propósito aplicar a cada elemento comprendido en el rango [primero_,


ultimo_) la función determinada por funcion_:

vector<Politico> listaNegra;
void detecta( const Politico& politico ) {
if ( politico.piensa() )
listNegra.pushBack( politico );
}
list<Politico> lista;
// se rellena convenientemente la lista
for_each( ++lista.begin(), lista.end(), detecta );

¿Qué es lo que aquí sucede? El algoritmo for_each aplica a cada elemento de la


lista (a partir del segundo) la función ‘detecta’, que toma como argumento el ele-
mento visitado en cada caso, y esta función discrimina a los políticos con ideas,
que son insertados en una lista negra para un rápido direccionamiento al ostra-
cismo. Pero en el ejemplo vemos también que una función se trata como un obje-
to. Y es exactamente eso.

¿FUNCIONES-OBJETO U OBJETOS-FUNCIONES?

Muchos de los algoritmos usan de funciones que son aplicadas a los elementos
de los contenedores por medio de iteradores. Tales funciones pueden ser bien
punteros a funciones normales de C/C++ o una generalización de éstas: funcio-
nes encapsuladas en objetos. Los objetos-funciones son instancias de clases con
un comportamiento definido por sus funciones miembros públicas, pero cuya sin-
taxis simula la de las funciones C mediante la sobrecarga del operador (); pero
además los objetos-funciones incorporan datos miembros y operan como el resto
de los objetos: se crean, se modifican y se destruyen. En general los objetos-
funciones, o simplemente funciones respecto de la STL, pueden dividirse en una-
rios o binarios, dependiendo si toman uno o dos argumentos, pero también, en
función de su propósito, en

Predicados devuelven un valor booleano, y se usan como condiciones de disparo


Comparadores devuelven un valor booleano, y se usan para ordenar elementos

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 57
Funciones Genera- sin funcionalidad predefinida, se aplican sobre elementos de contenedo-
les res

Pero veamos un ejemplo de cada uno, empezando por los predicados. Si consi-
deramos el algoritmo “find_if” con prototipo:

template<class InputIterator, class Predicate>


InputIterator find_if (
InputIterator primero_,
InputIterator ultimo_,
Predicate predicado_ )

que devuelve un iterador apuntando al primer elemento del contenedor que cum-
ple el predicado (esto es, la función de tipo Predicate aplicada a ese elemento
devuelve “true”):

bool traeMalaSuerte( int numero ) {


return numero == 13;
}
vector<int> numeros;
numeros.push_back( 4 );
numeros.push_back( 13 );
numeros.push_back( 13 );
vector<int>::iterator i;
i = find_if( numeros.begin(), numeros.end(), traeMalaSuerte );
// i apunta al segundo elemento del vector

Veamos seguidamente cómo operan los comparadores. Si consideramos el al-


goritmo “min” con prototipo:

template<class T>
const T& min( const T& a_, const T& b_, Compare comparador_ );

cuya funcionalidad es la de comparar los elementos a_ y b_ usando la función de-


terminada por comparador_, devolviendo el menor elemento:

bool comparaPoliticos(const Politico& uno, const Politico& otro)


{
return ( uno.votos() < otro.votos() );
}
Politico julio, jordi; // y se inicializan debidamente después
cout << min( jordi, julio, comparaPoliticos ) << “\nl”;

Por último examinemos las funciones generales, para lo que trataremos con el
algoritmo “count_if” con prototipo:

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 58
template<class InputIterator, class Predicate, class Size>
void count_if (
InputIterator primero_,
InputIterator ultimo_,
Predicate predicado_,
Size& n_ );

y que procura el número de ocurrencias del cumplimiento de un determinado pre-


dicado respecto de cada uno de los elementos de un contenedor. Como predica-
do usaremos el objeto-función logical_not, que devuelve “true” si el argumento que
se le pasa es cero, definido así en la STL:

template<class T>
struct logical_not : unary_function<T, bool> {
// ...
bool operator()( const T& x_ ) const {
return !x_;
}
};

y que encaja perfectamente en el ejemplo siguiente:

vector numeros[ 8 ] = { 0, 1, 0, 0, 1, 1, 1, 1 };
int n = 0;
count_if( numeros, numeros + 2, logical_not<int>(), n );
cout << n; // imprime ‘2’

Como vemos los objetos-funciones necesitan ser parametrizados y esta opera-


ción puede ser prolija: para facilitar esta labor aparecen los adaptadores de fun-
ciones. Así, por ejemplo, los “negadores” not1 y not2 toman como argumento pre-
dicados unarios y binarios y devuelven sus complementos. Pero no abundaremos
más en adaptadores, cuyo estudio se deja al ávido lector.

DE LOS PELIGROS DE LA EXTENSIBILIDAD

Examinemos, por ejemplo, el algoritmo “count()”, que cuenta el número de ítems en


un contenedor que satisfagan un determinado valor. Su prototipo es:

template<class InputIterator, class T, class Size>


void count
{
InputIterator first_, // iterador en el primer ítem
InputIterator last_, // iterador tras el último ítem
const T& value_, // el valor a comparar

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 59
Size& n_ // el contador a incrementar por cada ocurren-
cia
}

O sea, este algoritmo recorre una coleccion o contenedor desde el primer ele-
mento hasta pasado el último, y compara cada ítem encontrado con “value” apli-
cando el operador ==. Si la comparación devuelve “true” entonces se sumará uno
a “n”. Pero el lector, siempre atento, notará que el algoritmo no inicializa “n” a cero,
y ni siquiera devuelve el número de ocurrencias, que parece lo más intuitivo, sino
“void”. Pero tal es natural, pues la reusabilidad de un algoritmo se sustenta en no
fijar presunciones que lesionen su genericidad: de esta manera podemos pasar
por referencia a “count()” una variable con un valor anterior para que sea modifica-
da con las ocurrencias buscadas (caso que se da, por ejemplo, en la concatena-
ción de dos o más búsquedas sobre la misma o distinta colección). Se deja, así,
al usuario de la STL que especifique expresamente su intención:

// definimos un array (arreglo?!) de ocho elementos


int vector[] = { 1, 2, 2, 3, 3, 3, 2, 2 };
int contador = 0;
count( vector, vector + 8, 2, contador );
cout << contador; // devuelve ‘4’ (ocurrencias del 2)
count( vector, vector + 8, 1, contador );
cout << contador; // ‘5’ (ocurrencias del 2 ó del 1)

Bien, bien -reconoce aquí el lector-: de acuerdo con la genericidad pero ¿y si yo


quiero algo más sencillo? O sea, ¿No resulta peligrosamente tendente al error
tanta genericidad? Por otra parte, si ya conocemos el contenedor sobre el que se
aplica el algoritmo, ¿por qué repetirlo en los dos primeros argumentos? ¿Y si se
me olvida inicializar el contador? ¿No sería más adecuada una versión “simplifi-
cada” de tales algoritmos, quizás por derivación de clases? Bueno, el lector ha
puesto el dedo en la llaga, como siempre. Pero ¿es la llaga correcta? Hay que
pensar que esto mismo le achacaban los pascalistas al C: “Demasiada libertad
es peligrosa” (cualquier remembranza política es deliberada). Veamos los peli-
gros de dejar “demasiada responsabilidad” en manos del usuario:

vector<Politico> listaMunicipal;
vector<Politico> listaAutonomica;
// seguidamente se “llenan” los vectores con criterios
// que escapan a los propositos de este capítulo y por
// lo común a la lógica y la prudencia.
Politico discolo;
// se inicializa debidamente del objeto díscolo (o sea,
// se ponen algunas ideas en él).
int contador; // ¡oops! No se ha inicializado.
// seguidamente querremos contar cuántos políticos
// díscolos hay en una determinada lista.
count( listaMunicipal.begin(),

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 60
// seguidamente se cambia de vector (¡Horror!)
listaAutonomica.end(),
discolo,
contador ); // error: sin inicializar
// ¿El resultado? ¡Impredecible!

Visto lo anterior parece que para la mayoría de los casos un algoritmo como el
siguiente sería mucho más legible y seguro:

int politicosDiscolos = listaMunicipal.count( discolo );

a lo que se podría llegar derivando de la clase “vector” en la STL una nueva clase
que incorporara como función miembro una función “count()” que, a su vez, usara
del algoritmo “count()”. Algo así como:

template<class T> class vectorEspacial : public vector<T> {


public:
int count( const T& valor_ ) const {
int n = 0;
count( begin(), end(), valor_, n );
return n;
}
// sigue resto protocolo clase
};

De esta manera las listas anteriores deberían ser instanciadas como objetos de
nuestro vector personalizado:

vectorEspecial<Politico> listaMunicipal;
vectorEspecial<Politico> listaAutonomica;

Y ahora sí se podría utilizar la función miembro, como antes se ha visto. De hecho


este es el enfoque que han adoptado la mayoría de implementaciones comercia-
les de la STL, con el objetivo de facilitar el uso de la biblioteca a los usuarios. Pe-
ro este enfoque tiene muchos puntos negros. Veámoslos:

• Para cada algoritmo deberá añadirse una función miembro a nuestra clase.
Así, y aparte de la duplicación de clases que la derivación supone (para cada
clase en la STL habrá de crearse una nueva clase derivada con funciones
miembros reflejando cada algoritmo), dada la naturaleza de la STL, que invita a
añadir nuevos algoritmos, el interfaz de las clases derivadas debería ser cam-
biado con relativa frecuencia.

• La eficacia inherente a la STL se ve perjudicada por la introducción de jerar-


quías de herencia y destructores virtuales.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 61
• La facilidad por derivación enfatiza el uso de clases “no-normalizadas”, de ma-
nera que el código generado no se acoje a normas de estandarización y por
tanto plantea problemas para su extensión y acoplamiento a distintos entornos.

Un enfoque sustancialmente distinto es el que adopta STL<ToolKit>, de ObjectS-


pace Inc. Para facilitar el uso de los algoritmos de la STL, ObjectSpace ha añadi-
do unos cuantos llamados “algoritmos de ayuda”, que anteceden a los identifica-
dores estándar de la STL con el prefijo “os_”, de forma que así se podría codificar:

vector<Politico> listaMunicipal; // vector estándar STL


int PoliticosDiscolos = os_count( listaMunicipal, discolo );

donde os_count sería codificada como:

template< class Contenedor, class T > inline


int os_count( const Contenedor& c, const T& valor ) {
int n = 0;
count( c.begin(), c.end(), valor, n );
return n;
}

Personalmente me parece una solución que, además de respetar el espíritu de la


STL, resulta tan eficiente como la original (por su implementación “inline”), pero
más fácil de usar para la mayoría de situaciones, aparte de no incurrir en ninguna
de las anteriores desventajas (el mismo algoritmo de ayuda funciona, por ejem-
plo, para todos los contenedores de la STL).

LEVES CRÍTICAS

Si la STL es tan endiabladamente buena, ¿qué impide su adopción generaliza-


da? ¡Nada! Bueno, sí, quizás una cierta candidez cultural, semejante a la de aque-
llos ciudadanos que no comen marisco en público por temor -mayormente funda-
do- a no saber manejarse con los cubiertos. En realidad la propia sintaxis del len-
guaje es mucho más prolija y compleja que la estructuración en que se basa la
STL, de forma que cualquiera que use C++ debiera usar STL o, más aún, la Bi-
blioteca Estándar del lenguaje que incluye, en ella diluida y sin forma explícita, a la
STL. ¡Ah, pero biblioteca no es bibliotenaje! Esto es, como titularía Pitigrilli, “El
pollo no se come con la mano”: hay que estudiar y entender la STL antes de usar-
la, así como apreciar los leves inconvenientes derivados de su uso, que en la ac-
tualidad se reducen a dos: la dificultad de depuración del código erróneo (debida
no tanto a la estructura de la biblioteca como a sus actuales implementaciones) y
el aumento del tamaño de los ejecutables (que Cargill cifra en un 25%). Hay que
afinar la balanza, señores, pero sinceramente no creo que tales dificultades afec-
ten al grueso de los programadores en C++.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 62
APRENDIZAJE Y DOCUMENTACIÓN

La implementación de STL de Hewlett Packard, que con una decisión encomiable


liberó todas las licencias afectas a la biblioteca convirtiendo la STL y el trabajo de
muchos años en “freeware” puede ser conseguida, junto con el manual de Stepa-
nov y Lee en postscript, mediante ftp anónimo bien en ftp://butler.hpl.hp.com/stl/ bien
en ftp://ftp.cs.rpi.edu/pub/stl/, pero también en distintos foros electrónicos (como el de
Microsoft Spain en CI$). En el World-Wide Web habría que echarle un vistazo a la
página (home page) que mantiene David R. Musser, del Instituto Politécnico
Rensselaer, en http://www.cs.rpi.edu/~musser/stl, con documentación on-line y varios
enlaces. En general los enlaces de OOA/D/P pueden ser accedidos a través de
“La Página Orientada-a-Objetos” (The Object-Oriented Page: The OOPage) que
yo mismo edito para Galaxy en el Web14 (http://galaxy.einet.net/galaxy/Engineering-and-
Technology/Computer-Technology/Object-Oriented-Systems/ricardo-devis/oo.html), o también a
través de mis propias páginas personales en The Well
(http://www.well.com/user/ritchie).

En cuanto al aprendizaje y uso, en repetidas ocasiones he mencionado el manual


que acompaña a la STL. Naturalmente este manual es perfecto si la perfección
estilística se entiende a la manera de Borges: esto es, eliminando adjetivos en un
proceso iterativo que genera un documento denso y completo, pero que hay que
releer varias veces, y donde lo esencial ni siquiera se separa de lo accesorio, en
aras de la completitud formal, claro. Bien: el manual de Stepanov y Lee es perfec-
to (perfectamente técnico) y un cierto ejemplo sobre cómo debe componerse una
buena documentación. Sin embargo, para iniciarse en la STL (o para el gran pú-
blico, que dicen los legalistas de C++) quizás sea mejor atender a otros textos (y
es que Voltaire acuñó una frase omnipresente en informática). Pero, ¿qué textos?
Pues los de las implementaciones comerciales de la STL, que son bastante bue-
nos. ¡Un momento, un momento! ¿Implementaciones comerciales? ¡Pues claro!
La STL hace un uso extensivo e intensivo de las plantillas y, por ende, carece de
un soporte pre-construido que cubra la mayoría de los compiladores del mercado,
características que proporcionan los vendedores de STL, que además incorporan
diversas golosinas, ciertamente prácticas, que aumentan la facilidad y funcionali-
dad de la STL.

Las implementaciones comerciales actuales arrostran un precio usualmente no


superior a los $300,00 y básicamente (existen algunas más) se deben a Modena
Software Inc. (Stl++), con soporte para tablas hash, y ObjectSpace Inc.

14
¡Ah, lo siento! Para mí es “el Web” y no “la Web”. Y es que pese a la traducción de “Telaraña a
lo ancho del mundo” (que a mí me resuena a un cierto Capitán Tan), los barbarismos “WWW”,
“World-Wide Web”, “Web” ó “W3” me resultan terminantemente masculinos. Claro que una solu-
ción “políticamente correcta” sería la de “el/la Web”, tan común hoy en día en los textos nortea-
mericanos. Pero, vaya, tras leer la versión “políticamente correcta” de “Los Tres Cerditos” creo
que me batiría en duelo por reivindicar el sano derecho a la incorrectitud individual.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 63
(Stl<ToolKit>), con soporte para la mayoría de los compiladores C++ actuales,
aunque Rogue Wave ha anunciado también la inclusión de la STL en su conocida
biblioteca de clases Tools.h++. Restringiéndonos a los dos primeras, ambas con
buenos manuales y varios añadidos, yo recomendaría la segunda: STL<ToolKit>,
que provee un manual de 421 páginas perfectamente pedagógico, con más de
250 ejemplos de uso y una distribución de la información sencillamente perfecta
(tanto es así que parece que en breve se publicará el manual como libro en Pren-
tice Hall). Además ObjectSpace, accesible en el Web por http://www.objectspace.com
provee extensiones “multi-thread”, multitud de “algoritmos de ayuda” y soporte
para asignadores dinámicos de memoria.

REFERENCIAS DIRECTAS
• Cargill, T. STL Caveats, C++ REPORT, 7(6), julio-agosto 1995.
• Barreiro, J.-Fraley R. & Musser D.R. Hash Tables for the Standard Template
Library, X3J16/94-0218, WG21/N0605, enero 95.
• Koenig, A. The ideas behind STL, X3J16/94-0134, WG21/N0521, 1994.
• Musser D.R. The Standard Template Library Home Page, WWW 26/07/95.
• ObjectSpace Inc. STL<ToolKit> User Guide, mayo 1995.
• Stepanov, A. & Lee, M. The Standard Template Library, X3J16/94-0140,
WG21/N0527, julio 1994.
• Stroustrup, B. Making a vector fit for a standard, C++ REPORT, 6(8), octubre
1994.
• Vilot, M. An Introduction to the Standard Template Library, C++ REPORT,
6(8), octubre 1994.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 64
MANEJO DE EXCEPCIONES EN C++
4
xcepciones? ¿Es que no basta con las reglas15? ¡Oh, difícilmente las
¿E construcciones humanas pueden tratar con la inflexible y perfecta exacti-
tud reglada! Ya decía Goethe que “apenas hablamos, empezamos ya a
equivocarnos”. Y si la vida está repleta de errores, equívocos e imponderables,
imagínense el software, pobre y desdibujado reflejo de aquélla. Así que para ma-
nejarnos con cierta soltura debemos reglar también lo que no cabe en la propia
norma, estableciendo ciertas estructuras de control que nos permitan, cuando
menos, construir una somera malla en que las excepciones queden atrapadas: lo
excepcional se convierte así en previsible, en alguna medida. Esta es la ventaja,
claro. Pero desafortunadamente el diseño e implementación de un sistema de
captación y manejo de excepciones posee también sus reglas, sutilezas y des-
ventajas. Si encima añadimos a éstas la prolijidad y los problemas usualmente
achacados al lenguaje C++, bueno, el asunto adquiere tintes inopinadamente ma-
lignos. Pero quizá el tratamiento de excepciones constituya una excepción res-
pecto del comportamiento usual de C++: la sintaxis es relativamente sencilla, pero
su aplicación en sistemas software reales requiere de una estrategia previa cui-
dadosamente medida (y más vale que el lector me crea a pies juntillas). Entre
ambos extremos se sitúan, empero, los aspectos semánticos de uso de las ca-
racterísticas del lenguaje asociadas al manejo de excepciones y que son de los
que este capítulo se ocupará.

LOS CÓDIGOS DE ERROR

La mayoría de las estrategias de control de excepciones/errores/imprevistos se


basan en que todo procedimiento/función/mensaje debe devolver a su usuario,
considerado en un sentido amplio, un valor indicativo de su exito o fracaso. Así,
por ejemplo, es común encontrar código como el siguiente:

int adiciona( Plantilla& plantilla, const Empleado& empleado )

15
Quien piense que el proverbio latino Exceptio probat regulam significa “la excepción confirma la
regla” debería repasar su latín.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 65
{
if plantilla.incluye( empleado )
return -21545; //absurdo código de error
else {
plantilla.adiciona( empleado );
return 1;
}
}

de manera que pueda hacerse el siguiente uso de la función:

if ( !adiciona( plantilla, fernandez ) )


muestraDialogoError( “Empleado ya existe” );

Naturalmente lo que aquí se plantea resulta relativamente simple cuando se trata


de funciones que debieran devolver void. Cuando no es así las cosas se compli-
can:

Politico& PartidoPolitico::politicoHonrado()
{
// devuelve un político honrado del partido
// El problema es que si tal político no existe
// ¿qué se devuelve?
}

Una posible solución pasaría por reconvertir la función en otra que devuelva un
codigo de error:

int PartidoPolitico::politicoHonrado( Politico* politico )


{
// si existe, asigna el politico encontrado al
// puntero que se le pasa, y devuelve 1
// si no existe devuelve ‘0’ ó algún valor negativo
}

de tal manera que el código cliente deberá siempre comprobar el valor de retorno
antes de operar con el posible Politico encontrado, pues si realmente no se en-
cuentra (el caso más frecuente) tal puntero apuntará ... ¿a qué?. Humm, esta solu-
ción, aparte de no ser especialmente elegante y resultar intrusiva 16, ni siquiera
puede aplicarse en todas las situaciones: pensemos, por ejemplo, en los cons-
tructores, que no pueden devolver código alguno.

16
Cuando se quiere recomponer artificialmente código muerto, el producto final tiene bastantes
posibilidades de acabar como el monstruo de Victor Frankentein: bien en la hoguera bien como
leitmotiv de películas absolutamente infames.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 66
D situaciones
La devolución de códigos de error por funciones no puede aplicarse en muchas
y, por tanto, no debe constituirse en estrategia genérica de manejo de
excepciones.

La clara alternativa sería construir/devolver un objeto con un cierto estado interno


relativamente estable y que, a la vez, indicara la “inestabilidad” del mismo: o sea,
un objecto zombi:

class Persona {
long dni;
public:
Persona::Persona(
if ( unDNI < 0 ) {
dni = 0;
return;
}
// código consructor persona
}
int comprueba() {
return dni;
}
// sigue resto descripción de clase
};

Pero esto arrostra nuevos problemas: ahora obligaremos bien a los clientes de la
clase a validar cada objeto antes de usarlo:

if ( fulano.comprueba() )
cout << fulano;

bien a incluir código de comprobación en cada función miembro de la clase Per-


sona, que a su vez deberían devolver, así, ciertos códigos de error.

D
Los objetos “zombis” generan una ingente cantidad de código cliente de compro-
bación y bifurcación usualmente dependiente de unas especificaciones volátiles.
La aplicación indiscriminada de este enfoque ocasiona sistemas software de du-
dosa mantenibilidad.

¿Adivina ahora el lector por qué las estrategias usuales de error suelen resultar
tan artificiosas, frágiles y díficiles de mantener? Basta con echarle un vistazo a los
manuales corporativos de referencia de códigos de error: una barbaridad tal que
al final proporciona ciertos gusto y adicción malsanos17. Naturalmente no se afir-

17
Des Esseintes, el sofisticado protagonista del “À rebours” de Huysmans, gustaba de coleccio-
nar orquideas naturales que parecieran absolutamente artificiales: las corporaciones intentan
alcanzar el aberrante nivel opuesto asimilando a cada error un código que resulte humanamente
intuitivo (¡Diantre! ¡La psicología industrial se ha desquiciado!).

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 67
ma aquí que los códigos de error resulten dañinos per se, pues de alguna manera
(con algún código) ha de comunicarse la parte que genera un error con la parte
que la maneja. Pero el cómo es fundamental.

CONCEPTOS BÁSICOS

Como solución a estas cuitas, lo que C++ provee son tres nuevas construcciones
explicitadas en tres nuevas palabras reservadas:

Ü la expresión throw18, que lanza excepciones.

Ü el bloque catch, que define manejadores de excepciones.

Ü el bloque try, que indica las secciones de código que son sensitivas
respecto de las excepciones.

y su funcionamiento, en breve, es el siguiente: en cualquier sección del código


puede lanzarse, mediante throw, una excepción, pero tal excepción sólo será pro-
cesada si tal sección está incluida, directa o indirectamente, en un bloque try, y de
tal proceso se encargarán los manejadores definidos por catch y afectos al blo-
que try en que se incluya la excepción lanzada.

La expresión throw puede aparecer en cualquier parte del código de la siguiente


guisa:

throw 5; // lanza el objeto 5 (de tipo int)


throw Persona(); // lanza un objeto por defecto de tipo Persona
throw Racional( 1,2 ); // lanza el objeto Racional ½
throw “Error de suma”; // lanza un objeto de tipo char*

mientras que el bloque catch ha de ir forzosamente antecedido por un bloque try,


de la misma manera que un else ha de ir precedido de un if. Pero tras un try (in-
mediatamente después) pueden asociarse varios bloques catch. El bloque try no
posee argumentos, pues su propósito es indicar sobre qué porción de código (la
encerrada entre las llaves del try) se aplicarán los manejadores catch que le si-
guen. Los manejadores catch, sin embargo, sí poseen argumentos: desde la elip-
sis (...) hasta cualquier tipo definido-por-el-usuario:

try {
// una porción de código cualquiera
}

18
Como señala Stroustrup, “raise” o “signal” hubieran sido palabras más apropiadas, pero éstas
ya existían en la biblioteca estándar de C.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 68
catch ( int entero ) { // inmediatamente después del bloque try
cout << “Código de error: ” << entero;
}
catch ( Persona ) {
// en este “catch” sólo importa el tipo de error
// y no el objeto concreto lanzado por “throw”
relanzaProcesoDePersona();
}
catch ( Racional& racional ) {
racional += 1;
cout << racional;
}
catch ( const char* mensaje ) {
VentanaDeMensaje( mensaje );
}

El manejo de excepciones en C++ es, en esencia, un mecanismo que permite la


comunicación entre dos partes distintas de una aplicación (y esto provoca no po-
cas críticas, al estar tales partes usualmente bien distantes y supuestamente rom-
per, así, uno de los principios establecidos por Meyer: evitar la propagación de
errores en tiempo de ejecución). En realidad hay una cierta similitud entre tal me-
canismo y el de llamada y resolución de funciones: la expresión throw equivaldría
a la llamada a la función, con un argumento dado, mientras que el bloque catch
representaría la definición de la función. La diferencia fundamental consiste en
que la ligazón de una llamada a una función con el cuerpo de ésta se produce en
tiempo de compilación, mientras que la resolución del mecanismo de manejo de
excepciones se produce en tiempo de ejecución. Esto significa que el compilador
no sabe qué bloque catch (si acaso alguno) manejará una determinada excep-
ción. De hecho lo único que puede hacer el compilador es generar ciertas estruc-
turas que soporten la información necesaria para que funcione el mecanismo en
tiempo de ejecución. Así cada vez que el compilador encuentra una expresión
throw o catch crearía un “descriptor de tipo” para cada una de ellas, permitiendo
su comparación en tiempo de ejecución. El encaje de ambos descriptores es, en
gran medida, igual al encaje de argumentos en funciones: un operando de throw
con tipo “E” casará con un manejador catch con argumento de tipo “M”, “const M”,
“M&” o “const M&” si:
• E y M son del mismo tipo
• M es una clase base inambigua de E
• M y E son punteros y se da una conversión estándar de E a M
Resulta, entonces, que si se lanza la siguiente excepción, significada por un obje-
to que se construye para la ocasión:

throw Mensaje( “That’s all folks” );

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 69
la expresión throw inicializaría un objeto temporal del tipo estático del operando
(éste es, “Mensaje”), y tal objeto se usaría a su vez para inicializar, de la misma
manera que un argumento en la llamada a una función, la variable establecida
como operando del catch adecuado. Vamos, que el mecanismo semeja el de una
expresión return. En tal sentido debe tenerse en cuenta que el paso por valor sig-
nifica el uso del constructor de copia, y que a pesar que la memoria para el objeto
temporal se asigna de una forma dependiente de la implementación, el destructor
del objeto deberá ser finalmente usado, de manera que tal funcionalidad mínima
tendrá que ser prevista en la clase.

C cer
Los objetos que se utilicen como operandos de expresiones throw deben pertene-
a clases en que el constructor de copia y el destructor sean accesibles (estén
declarados en la sección pública).

Cuando se “entra” en un manejador catch que toma como operando un objeto (no
una referencia a un objeto) de una clase dada, se produce una copia del objeto
lanzado por la expresión throw correspondiente, que se destruye cuando se sale
del ámbito del manejador. Si el catch espera un objeto no-constante, los posibles
cambios realizados en la copia del objeto dentro del manejador serán locales a
dicha copia y, por tanto, se perderán a la salida del catch.

MANOS A LA OBRA

Seguidamente vamos a detallar un ejemplo elemental en el que se han señalado


las líneas de interés con números para permitir su rápida localización en la densa
explicación que sigue al código:

class MensajeDeSocorro {
public:
MensajeDeSocorro( char* cadena );
char* cadena() const;
// resto descripción clase
};

void Programador::codifica()
{
desactivaSalvaPantallas();
escribe();
escribe();
escribe();
modifica();
piensa();
throw MensajeDeSocorro( “¿Qué había que hacer?” );
// (1)
}

extern Programador* programador;

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 70
void Analista::analiza()
{
try { // (2)
desactivaSalvaPantallas();
escribe();
escribe();
escribe();
discute();
piensa();
programador->codifica();
throw “¿Qué había que hacer?”; // (3)
}
catch ( char* ) { // (4)
VentanaModalDeAviso( “Cambiar de analista” );
}
}

extern Analista* analista;


void empiezaProyecto()
{
try { // (5)
analista->analiza();
}
catch( ... ) { // (6)
throw;
}
catch( MensajeDeSocorro mensaje ) { // (7)
cout << “Esta línea es inalcanzable”;
}
}

void ejecutaProyecto() //nunca mejor dicho


{
try { // (8)
empiezaProyecto();
terminaProyecto();
}
catch (MensajeDeSocorro mensaje ) { // (9)
cout << mensaje.cadena();
}
}

Empecemos con la última función “ejecutaProyecto()”: en primer lugar se llama a


la función “empiezaProyecto()”, que a su vez llama a la función “Analis-
ta::analiza()”, que llama a la función “Programador::codifica()” que genera una ex-
cepción del tipo “MensajeDeSocorro” lanzando un objeto de tal clase construido
con un argumento de tipo “String” (línea 1). Bien. Ahora sólo hay que ver cómo se
maneja esta excepción.

Obviando otras consideraciones, lo primero que se hace es mirar si la excepción


está contenida directamente en un bloque try. Como no es así, se busca en el

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 71
ámbito en que se llama a la función “Programador::codifica()”, dentro de la función
“Programador::analiza()”, que sí está incluida en un bloque try (línea 2). Seguida-
mente miramos si el bloque try tiene uno o más bloques catch asociados. Y así
es: en la línea 4 se da un catch que admite objetos del tipo “char*”. Pero el objeto
que se lanzó en el throw era de tipo “MensajeDeSocorro”, por lo que este catch no
es de aplicación. Así que se busca un nuevo bloque try exterior, que se encuentra
en la línea 5 y que tiene asociados dos bloques catch en las líneas 6 y 7. En se-
guida se comienza la comparación del tipo lanzado con el que espera cada uno
de los bloques catch, examinados en estricto orden de aparición. Así tenemos
que el primero (línea 6) admite cualquier tipo de argumento (...), por lo que casa
perfectamente con el tipo “MensajeDeSocorro” que se lanzó en el throw inicial.
Pasa inmediatamente a ejecutarse el catch(...), y en ese mismo momento la ex-
cepción se entiende manejada (exactamente así, querido lector). Lo que sigue es
una instrucción “throw” sin más que, simplemente, relanza la misma excepción
que había captado, significada en el mismo objeto de tipo “MensajeDeSocorro”: o
sea, es como si se repitiera la línea 1, pero en esta nueva posición:

G
Una expresión throw sin operando relanza la excepción que en ese momento se
estaba manejando sin copiarla. Si no se está manejando ninguna excepción se
genera una llamada a “terminate()”.

Claro que ahora el lector despistado podría pensar: “Bueno, perfecto. El segundo
catch de la línea 7, que espera un objeto de tipo “MensajeDeSocorro” manejará
perfectamente la excepción relanzada”. ¡En absoluto! Hay que pensar que las
conjunciones “try-catch” se asemejan sobremanera en su comportamiento a las
conjunciones “switch-case” (suponiendo un break al final de cada case): para ca-
da try se ejecuta, a lo sumo, solo uno de los catch asociados. Es fácil ver, así, que
como el catch de la línea 6 capta cualquier excepción de cualquier tipo, el catch
de la línea 7 no será alcanzado por ninguna excepción en ningún caso, y de hecho
un buen compilador emitiría el siguiente error (sí caro lector: error, no aviso):

error EDC3194: "catch(MensajeDeSocorro)"


will never be reached because of previous "catch(...)".

C entre los manejadores.


Si siguiendo a un bloque try se da un catch(...), éste debe ocupar el último lugar

Pero volvamos al flujo de nuestra excepción. Como ya se ha ejecutado el catch(...)


al relanzar la excepción se vuelve a buscar un bloque try en un ámbito exterior,
que se encuentra en la línea 8, dentro de la función “ejecutaProyecto()”. Este try
tiene asociado un solo catch (línea 9) que admite el tipo “MensajeDeSocorro”, y
por tanto casa con el tipo de nuestra excepción, por lo que tal catch la manejará. Y
como en este bloque catch se explicita un identificador para el objeto (que no
aparecía en el catch de la línea 7), puede usarse tal objeto en su interior: en este

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 72
caso para llamar a una función miembro, “cadena()”, que devuelve la cadena de
caracteres de su representación interna (ésta es, “¿Qué había que hacer?”). Re-
capacite el lector que este último catch (línea 9) admite un objeto (no una referen-
cia) de forma que, como ya se notó en el parágrafo anterior, si se produjera algún
cambio en el mismo, éste se perdería al salir del ámbito del manejador. Hay que
fomentar aquí, también, las mismas consideraciones de eficiencia que aconsejan
el uso de referencias a objetos como argumentos de funciones.

Incidentalmente el lector habrá también notado que la línea 3, en que está codifi-
cado directamente el lanzamiento de una excepción asociada a la función “Analis-
ta::analiza()”, nunca será alcanzada, pues la excepción lanzada con anterioridad
por la función “Programador::codifica()” modifica el flujo secuencial del programa,
como es fácil suponer y más adelante veremos. De esta manera el software imita
al mundo real: el error del programador impide que el del analista resulte visible.

CLASES DE EXCEPCIONES

El lector habrá apreciado que en el ejemplo anterior se lanzan tanto objetos de


tipo predefinido como objetos de clases definidas-por-el-usuario. Lo que el lector
habrá también asimilado a estas alturas es que las clases de excepciones se
corresponden con las clases (por tipos) de los objetos lanzados como excepcio-
nes. Resulta, así, que en una estrategia de manejo de excepciones en C++ es
imprescindible la creación de un cúmulo de clases adecuadas para tratar cada
error. Naturalmente podemos crear clases independientes para cada excepción,
pero ¿por qué no usar del mecanismo de la herencia que provee C++ y construir
una jerarquía de clases de error? Una de las principales ventajas (discutidas, no
obstante, por ciertos autores y organizaciones) de este enfoque es el tratamiento
uniforme que se daría a la especificación de excepciones, que examinaremos en
breve. Pero no adelantemos acontecimientos y fiémonos ahora simplemente de
las supuestas bondades universales de la derivación como mecanismo “bueno-
para-casi-todo”.

JERARQUÍAS DE CLASES DE EXCEPCIONES

Si construimos una jerarquía de clases representativas de excepciones/errores,


parece prudente que la clase base (TStandardException, por ejemplo, en el caso
del Taligent Application Environment) contenga una mínima funcionalidad (o nin-
guna, si se piensa que el estándar del lenguaje puede llegar a reglar este extre-
mo) más un destructor virtual (imprescindible en las jerarquías de derivación, co-
mo el lector ya debería saber). De cualquier forma y en aras de la pedagogía
examinaremos un ejemplo más simple:

class Error { /* clase base */ };

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 73
class ErrorBaseDeDatos : public Error { /*...*/ };
class ErrorDeActualizacion : public ErrorBaseDeDatos { /*...*/ };

void actualizaClientes() {
throw ErrorDeActualizacion(); // (10)
}

try { // (11)
actualizaClientes();
}
catch ( ErrorDeActualizacion ) { /*...*/ } // (12)
catch ( Error ) { /*...*/ } // (13)
catch ( ErrorBaseDeDatos ) { /*...*/ } // (14)

Como vimos anteriormente, la excepción lanzada en la línea 10 salta hasta el blo-


que try de la línea 11 y seguidamente pasa a comprobar sus manejadores aso-
ciados. Como éstos se examinan en estricto orden de aparición, es fácil ver que
el primero de ellos (línea 12) es del mismo tipo que la excepción lanzada, y por
tanto casa perfectamente con ella. Las cosas cambiarían, sin embargo, si la ex-
cepción lanzada fuera

void actualizaClientes() {
throw ErrorBaseDeDatos();
}

pues al empezar la comparación de descriptores de tipo con los de los bloques


catch encontraría que el de la línea 12 no casa, pero sí lo hace el siguiente de la
línea 13, ya que el operando de la expresión throw es un objeto de una clase deri-
vada públicamente de “Error”. De aquí se sigue, como el lector ya se está apresu-
rando a proclamar, que la línea 14 no será alcanzada en ningún caso.

C ensiempre
Si en un conjunto de bloques catch tras un bloque try se manejan tipos de datos
jerarquía, los bloques catch con argumento de clases derivadas deberán
anteceder a los catch que manejen excepciones de sus clases base
respectivas..

EL DESBOBINADO19 DE LA PILA

Cuando se lanza una excepción el programa “salta” hacia un nivel superior donde
el error podrá ser procesado20 o, lo que es lo mismo, se transfiere el control de la

19
Bueno, “desbobinado” no existe en castellano, pero rebobinado, el vocablo más ajustado, re-
presenta la acción de “desbobinar” para bobinar en otro carrete (un tipo de pila, al fin y al cabo).
Por si el lector abriga alguna duda, “desempilar” o “desapilar” tampoco existen.
20
¿No les viene a la cabeza una poca piadosa semblanza con la actitud de algunos ante la su-
puesta vida eterna? ¡Yerra, yerra y sufre, que de todo se ocupará un nivel superior! Ah, pero, ¿y

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 74
ejecución del programa al primer bloque try que lo encierre. Normalmente los ob-
jetos en la pila (stack) se destruyen cuando finaliza el ámbito en que fueron decla-
rados, pero al realizar el “salto” quedarían objetos abandonados entre el
lanzamiento y el manejador. Para evitar esto se invocan los destructores de todos
los objetos automáticos construidos hasta que se encuentra el bloque try, y este
proceso de denomina “desbobinado de la pila” (stack unwinding). Pero
pongamos las cosas más difíciles: imaginemos que se genera una excepción en
el bloque de inicialización de un constructor, de tal forma que algunos de los
objetos que debería inicializar quedan sin construir. El desbobinado de la pila, a
resultas de tal excepción, llamará solamente a los destructores de los objetos
totalmente construidos.

CONSISTENCIA DE ESTADOS

Stroustrup relata con detalle las dudas que surgieron en el proceso de diseño del
mecanismo de manejo de excepciones en C++ respecto de sí debía ser continua-
tivo o conclusivo: o sea, si tal mecanismo debía constituirse en una bifucarción
que, tras ser tratada, pudiera devolver el flujo del programa a donde se lanzó la
excepción o bien debería adoptarse el actual enfoque. La decisión final es ac-
tualmente evidente. Las razones fueron, en esencia, que el mecanismo continuati-
vo representaba graves complejidades como estrategia y, sin embargo, resultaba
fácilmente codificable tomando como base el enfoque conclusivo.

Es relativamente usual, por ejemplo, que una función miembro necesite completar
una serie de acciones para mantener la consistencia del estado interno del objeto
a que se aplica. Echemos, si no, un vistazo a la siguiente función:

void Politico::adjudicaContrato( long soborno )


{
recibeDinero( soborno );
cometeTurbiosManejos();
emiteDictamenFavorable();
}

El problema es que si en la funcion “cometeTurbiosManejos()” se genera una ex-


cepción, entonces no se alcanzará nunca la función “emiteDictamenFavorable()” y
el político, sin embargo, se habrá embolsado el dinero del soborno. Pero esto,
aunque refleja fielmente la realidad, no parece convenir comercialmente. ¿Cómo
solucionar, pues, este claro problema de contraprestaciones?

void PoliticoHonrado::adjudicaContrato(

vivir? Esta es, naturalmente, una de las razones por las que el hombre mata a Dios (pero su
sombra es larga, que decía Nietzsche).

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 75
Adjudicatario& X, long soborno )
{
recibeDinero( soborno );
try {
cometeTurbiosManejos();
}
catch(...) {
devuelveDinero( X, soborno );
throw; // relanza la excepción
}
emiteDictamenFavorable();
}

Claro que aquí el lector descontento podría exclamar: “Oh, esto es demasiado
prolijo. Si tengo que codificar de esta manera todas mis funciones el código final
crecerá una barbaridad. Y eso sin hablar de los errores tipográficos que acechan
en cada línea”. Bueno, en primer lugar he de reconocer que el lector se expresa
muy bien, para después proclamar que, como ya anuncié, el manejo de excepcio-
nes en C++ no es tarea simple.

ADQUISICIÓN DE RECURSOS VÍA INICIALIZACIÓN

Otra cuestión frecuente es la que se refiere a la liberación de los recursos adqui-


ridos. Veamos un ejemplo de la vida real:

void Politico::cobra()
{
HombreDePaja* testaferro = new HombreDePaja();
// Aquí se suceden distintas llamadas
// a funciones y métodos (a cuál peor)
delete testaferro; // se eliminan las pruebas
}

El problema es que si entre la creación del “testaferro” y su destrucción se genera


alguna excepción, la memoria asignada (primaria o secundaria, si incluimos la
persistencia) nunca se liberará. Naturalmente la solución podría pasar por un es-
quema similar al del parágrafo anterior, insertando una expresión try-catch entre
la creación y destrucción del objeto, pero una mejor solución sería crear una clase

class PunteroAHombreDePaja {
public:
PunteroAHombreDePaja() : testaferro( new HombreDePaja; ) {}
~PunteroAHombreDePaja() { delete testaferro; }
HombreDePaja* testaferro; // ¡anatema!
};

de tal manera que la función se recodificaría de la siguiente guisa:

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 76
void Politico::cobra()
{
PunteroAHombreDePaja testaferro;
// Transacciones financieras varias
}

Si no se produce una excepción tras la creación de “testaferro”, al salir del ámbito


de la función se llamará al destructor del objeto “testaferro” y al ejecutarse éste se
liberará la memoria dinámica asignada. Si se genera una excepción tras la crea-
ción del objeto, al ponerse en marcha el desbobinado de la pila se llamará tam-
bién al destructor, obteniendo el mismo resultado.

Este planteamiento, resultando efectivo, no es, sin embargo, suficientemente sa-


tisfactorio: por un lado tenemos que el código original operaba con punteros, y
éste lo hace con objetos, de manera que bien habría que cambiar el código (lo
que frecuentemente es inaceptable) bien dotar a la clase con una función u ope-
rador* que devolviese el puntero para así poder operar con él; por otro lado resul-
ta que con un tal esquema habría que crear una clase distinta para cada tipo de
dato que se deseara manejar con esta técnica. La solución es echar mano de las
plantillas:

template <class T >


class New {
public:
New() : t( new T ) {}
~New() { delete t; }
New( T* tt ) { t = tt; }
operator T*() { return t; }
private:
T* t;
protected:
// se declaran no-públicos el constructor de copia y
// el operador de asignación para evitar problemas
New( const New& n );
const New& operator=( const New& n );
};

De manera que ahora podríamos escribir:

class HombreDePaja : public PersonaFisica { /* lo que sea */ };

void firma( HombreDePaja* hdp ) { /* ... */ }

void Politico::cobra()
{
New< HombreDePaja > testaferro;
// ahora puede usarse testaferro como un puntero

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 77
// a un HombreDePaja, merced al operator*.
firma( testaferro );
}

Otra solución, a falta de un buen recolector de basura para C++, sería usar de
Punteros Inteligentes (Smart Pointers) para gestionar el manejo de excepciones.
Pero esto se sale del ámbito de lo aquí pretendido, así que el lector interesado
puede echarle un vistazo al interesante artículo de Steve Churchill.

ESPECIFICACIÓN DE INTERFACES

Resulta desafortunadamente usual que un programador, a poco de aprender (oh,


¿dije aprender?) alguna nueva característica de un lenguaje, proceda bien a ob-
viarla bien a aplicarla sin control ni concierto. Imaginen el desbarajuste que puede
ocasionar un programador(de los hiperactivos: segundo caso) lanzando excep-
ciones a diestro y siniestro. Ahora magnifiquen el desastre: piénsense usando el
código así generado por otra persona. Naturalmente los programas generados, al
no tener claro conocimiento de las excepciones lanzadas (pues el código de
implementación de las funciones generalmente no es accesible), no podrán
manejarlas prudentemente, así que todo serán abruptas finalizaciones. ¿No
habría alguna manera de especificar, de una forma expresa y clara, el tipo de
excepciones que una función puede lanzar? ¡Pues claro! Para eso está la
especificación de excepciones.

La idea es matizadamente simple: en el declarador de una función bien en su de-


claración bien en su definición (pero no en ambas, como tampoco en un typedef)
puede detallarse la lista de tipos de excepciones que tal función puede legalmen-
te lanzar. Veamos unos ejemplos:

void f() throw( int );


int g() throw( Overflow, ErrorDB );
Poltico& encarcelar() throw ( PoliticoHonesto );

Para evitar un cambio impensable en todo el código existente, la no declaración


de una especificación de excepciones equivale a:

void funcionNormal() throw( ... );

En justa correspondencia, para especificar que una función no puede legalmente


lanzar ninguna excepción se escribe:

void funcionSinExcepciones() throw();

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 78
Bueno, esto resulta perfecto: ahora cada uno sabrá a qué atenerse cuando mane-
ja funciones ajenas, dotando a su código si es necesario de los manejadores
adecuados (y antes de los bloques try) para controlar las posibles excepciones.
¿Perfecto? Humm, seguro que el lector ha notado un cierto retintín en la repetición
del adverbio “legalmente”. ¿Qué significa aquí está apreciación jurídica? Pues
exactamente lo siguiente: si una función con una especificación de excepciones
de ciertos tipos lanza una excepción no contemplada en la lista, entonces el sis-
tema llamará automáticamente a la función “unexpected()”, que por defecto llama-
rá a “terminate()”, que por defecto, a su vez, llamará a “abort()”, y el programa
acabará abruptamente. ¡Vaya! ¡Funciones nuevas! Echémosles un vistazo.

EL FINAL DE LA CUERDA

C++ provee dos funciones esenciales para el manejo básico de excepciones,


incluidas en la cabecera <exception> de la biblioteca estándar del lenguaje: “void
terminate()” y “void unexpected()”. Veamos cada una por separado.

La función “terminate()” se llama, en breve, cuando una excepción no encuentra


manejador, cuando se genera una excepción en el proceso de destrucción de un
objeto en el desbobinado de la pila (stack unwinding), o cuando se da algún error
interno.

N
No se debe lanzar una excepción desde el interior del cuerpo de un destructor,
pues si su lanzamiento se produce durante un desbobinado de la pila se ignorará
el mecanismo de manejo de errores y se llamará indefectiblemente a “termina-
te()”.

La función “terminate()” llama a su función manejadora asociada de tipo

typedef void (*terminate_handler)();

y cuyo comportamiento por defecto es llamar a la función “abort()”. El manejador


asociado a “terminate()” puede, no obstante, ser cambiado haciendo uso de la
función

terminate_handler set_terminate( terminate_handler f ) throw();

que admite un puntero no nulo a una función (que necesariamente debe terminar
sin devolver el control al usuario) y retorna el anterior manejador.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 79
VULNERACIÓN DE LA ESPECIFICACIÓN DE EXCEPCIONES

Si una función con una especificación de excepciones asociada lanza una excep-
ción no contemplada en tal lista, se producirá una llamada a la función “void unex-
pected()”, que a su vez llamará a su manejador asociado de tipo

typedef void (*unexpected_handler)();

que por defecto llamará a la función “terminate()”. Tal manejador puede cambiar-
se, empero,mediante la función

unexpected_handler set_unexpected( unexpected_handler f )


throw();

que devuelve el manejador hasta entonces válido y admite un nuevo puntero no


nulo a una función que puede lanzar una excepción ya normal ya de tipo
bad_exception, o bien llamar a “exit()” o “abort()”

¡Demonios! ¡Otra nueva palabra! Bueno, sufrido lector, no me negará que esta
situación excepcional lo disculpa casi todo: “bad_exception” es el nombre de una
clase derivada públicamente de “exception”, perteneciente a la biblioteca están-
dar del lenguaje, y cuya razón de ser es precisamente evitar el problema sugerido
por Taligent y que se refiere al peligro de usar funciones con especificación de
excepciones en un código robusto: cuando se lanza una excepción no prevista,
directa o indirectamente, la acción por defecto es terminar el programa. Y, claro,
esto resulta inaceptable. Debe procurarse algún mecanismo que permita obviar
conscientemente este problema sin eliminar las útiles especificaciones de inter-
faz. La solución la proporciona la clase “bad_exception”. Pero veámoslo en deta-
lle.

Si el tipo de la excepción lanzada por una función no está en la lista de especifi-


caciones de ésta, entonces, como el lector bien sabe, se llama a unexpected(). Si
“unexpected()” lanza a su vez una excepción permitida en tal lista, entonces conti-
núa la búsqueda de manejador en el ámbito de la llamada a la función primera. Si,
por el contrario, el manejador de “unexpected()” lanza una excepción de un tipo no
permitido (no incluido en la lista), si acaso no se había incluido el tipo
“bad_exception” en la especificación, se llamará igualmente a “terminate()”; si
contrariamente se había incluido, como por ejemplo en

void funcion() throw (int, Overflow, bad_exception );

entonces la excepción no permitida y lanzada se sustituye por un objeto, definido


por cada implementación particular, del tipo “bad_exception” y se continúa la bús-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 80
búsqueda de un nuevo manejador en el ámbito de la llamada a la función con la
especificación referida.

CONCLUSIONES

Como bien afirma Hortsmann, “Las excepciones deberán reservarse para cir-
cunstancias inesperadas en el flujo normal de una computación y cuya ocurrencia
crea una situación que no puede ser resuelta en su actual ámbito”. De hecho él
mismo establece unas cuantas indicaciones de uso de excepciones:
• Captar únicamente aquellos errores que se puedan manejar.
• Las excepciones deben usarse en circunstancias excepcionales.
• No apoyarse en excepciones si se puedes validar el código.
• No lanzar una excepción si se puede continuar.
• Permitir a los usuarios de bibliotecas decidir cómo desean que los errores sean manejados.
• Usar excepciones cuando se den fallos en constructores.
• No pierdas recursos durante el procesado de excepciones

David Reed, por su parte, expone un plan metódico para incorporar las excepcio-
nes a un código ya existente:
• Implementar manejadores especiales para las funciones “terminate()” y “unexpected()”.
• Añadir bloques try/catch que cubran las excepciones pre-existentes.
• Diseñar y utilizar clases de excepciones.
• Añadir especificaciones de excepciones a las funciones.
• Localizar y reparar pérdidas de recursos.

Como el lector puede apreciar, el tema da para mucho: quedáronse muchas


ideas y técnicas de uso de las excepciones en el procesador de textos, como el
mantenimiento de invariantes, el tratamiento de precondiciones y postcondicio-
nes, etc. etc.

& encasC++
Lea, lector, lea. Porque cualquier intento de aplicar el manejo de excepciones
sin conocer exactamente los recursos del lenguaje y las característi-
del compilador troca imposible la definición de una estrategia exitosa que
M estructure las excepciones en una arquitectura eficaz, efectiva y fácilmente
mantenible. Lo contrario es, sin duda, una bomba de relojería gobernada por un
reloj estropeado.

REFERENCIAS DIRECTAS
• Working Paper for Draft Proposed International Standard for Information Sys-
tems - Programming Language C++, Documento X3J16/95-0185-
WG21/N0785, 26 de septiembre 1995.
• Exception Handling: Supporting the Runtime Mechanism, Josée Lajoie, SIGS
Publications, C++ Report, marzo-abril 1994.
• Using C++ Exceptions, David Reed, SIGS Publications, C++ Report, marzo-
abril 1994.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 81
• Excepctions: Pragmatic issues with a new language feature, David R. Reed,
SIGS Publications, C++ Report, octubre 1993.
• Designing and Coding Reusable C++, Martin D. Carroll & Margaret A. Ellis,
1995, Addison-Wesley, 0-201-51284-X.
• The Design and Evolution of C++, Bjarne Stroustrup, 1994, Addison-Wesley,
0-201-54330-3.
• Mastering Object-Oriented Design in C++, Cay S. Horstmann,1995, John Wi-
ley & Sons, 0-471-59484-9.
• C++ Strategies and Tactics, Robert B. Murray, 1993, Addison-Wesley, 0-201-
56382-7.
• Exception Recovery with Smart Pointers, Steve Churchill, SIGS Publications,
C++ Report, enero 1994.
• Taligent’s Guide to Designing Programs: Well-Mannered Object-Oriented
Design in C++, Taligent Inc, 1994, Addison-Wesley, 0-201-40888-0.
• Designing with Exceptions, Grady Booch y Michael Vilot, SIGS Publications,
C++ Report, Mayo 1994.
• Designing with Exceptions, Grady Booch y Michael Vilot, SIGS Publications,
C++ Report, julio-agosto 1994.
• C++ Primer, 2nd Edition, Stanley B. Lippman,1991, Addison-Wesley, 0-201-
54848-8.
• The C++ Programming Language, 2nd Edition, Bjarne Stroustrup, 1991, Ad-
dison-Wesley, 0-201-53992-6.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 82
ASIGNACIÓN EN C++
5
L
a asignación en C++ no es, como no lo son otras muchísimas características
del lenguaje, asunto baladí. C++ es sorprendentemente copioso en sutilezas
sintácticas, lo que plantea dos cuestiones: la seguridad de las herramientas
generadoras de código y la eficacia de los cursos-relámpago de C++ avanzado
(se han llegado a publicitar risibles reclamos de la guisa “Programación Avanza-
da Orientada-a-Objetos en C++, Visual Basic y Clipper en 15 días”). C++ es ex-
tenso, prolijo, complejo y, afortunadamente, imperfecto en su sentido más prácti-
co: amigo de lo bueno, como podría haber dicho Voltaire. Lo que sigue es, pues,
una concentración de técnicas, sintácticas y de construcción de programas, que
pretende mostrar al programador novel -y aun intermedio- en C++ las soluciones
a distintos problemas bien conocidos, como fuego en carne, por los expertos21.

CONCEPTOS BÁSICOS

Como bien sintetiza Winston, “El operador de asignacion se utiliza para cambiar
el valor de una variable” y en C++ viene representado por los símbolos:

= *= /= %= += .= >>= <<= &= ^= |=

de los cuales en adelante representativamente usaremos el primero: la asigna-


ción simple.

Dado el carácter esencialmente mutable de las variables, todos los lenguajes de


programación incorporan un operador de asignación con una funcionalidad pre-
definida para los tipos incorporados. Así, en C++, el operador de asignación por
defecto es exactamente el operador estándar de C. ¿Qué ocurre, sin embargo,
con los tipos definidos-por-el-usuario o clases? Pues que, dado que en C++ se
pretende una equiparación de las clases con los tipos predefinidos, el compilador

21
Al decir de Oscar Wilde, “la experiencia es el nombre que cada uno da a sus propios errores”,
mientras que para Auguez es “la suma de nuestros desengaños”. Un experto es, por tanto, una
persona que, a más de equivocarse muchísimo, sufre abundantes rechazos y desencantos. La
línea entre un experto y un psicótico resulta, así, sorprendentemente frágil.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 83
garantizará la declaración y, en su caso, definición de un operador de asignación
implícito para las clases en que expresamente tal no se codifique, y que en esen-
cia consistirá en la copia miembro a miembro, secuencial y recursiva, de los
miembros no-estáticos y de las correspondientes porciones de las clases base
de la dada. De esta manera tenemos que (suponiendo que un político es un obje-
to con una cierta oscura funcionalidad) la siguiente línea:

unPolitico = otroPolitico;

equivale a

unPolitico.operator=( otroPolitico );

El operador implítico, o por defecto, garantiza una intuitiva codificación de la co-


pia por asignación en objetos de cualquier tipo, pero ¿que normas rigen respecto
de tal operador implícito? ¿No deben ser declaradas en C++ todas las funciones?
¿Existe alguna diferencia respecto de otros operadores? ¿Qué ocurre con la
asociatividad? ¿Las clases vacías también disponen de operador implícito? Ata-
quemos, sin más, la parte sintáctica del tema.

EL OPERADOR DE ASIGNACIÓN POR DEFECTO

Si no se declara explicitamente el operador de asignación para una clase dada,


el compilador automáticamente declarará (con las excepciones que más adelante
veremos) un operador de asignación implícito, o por defecto, que se definirá la
primera vez que se utilice una asignación a un elemento de tal clase. Asi, si tene-
mos, por ejemplo:

class Politico { /* clase vacía */ }; // sin comentarios


Politico unPolitico, otroPolitico;
unPolitico = otroPolitico; // todos los políticos son iguales

en la última línea se producirá la definición implícita del operador de asignación


(definiendo antes, si procediera, los operadores de asignación declarados tam-
bién implícitamente correspondientes a las clases bases directas de la dada y a
los datos miembros no-estáticos de la misma), en calidad de función miembro no-
estática perteneciente al protocolo público (public) de la clase, con el prototipo:

Politico& Politico::operator=( Politico& )

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 84
y cuya implementación realiza una copia bit-a-bit del objeto otroPolitico en el obje-
to unPolitico. De hecho, y según el borrador del estándar C++ (en adelante WP22:
working paper), “un operador de asignación por copia, operator=, es una función
miembro no-estática de la clase X con exactamente un parámetro de tipo X& ó
const X&”. El prototipo de operador por defecto, generado por el compilador, que
hemos visto antes, cambiaría empero su parámetro a constante

Politico& Politico::operator=( const Politico& )

únicamente si, y sólo si, todos los miembros de la clase Politico y sus posibles
clases base directas (muy variadas, atendiendo al sorprendente resultado) pose-
yeran operadores de asignación con argumento constante.

El operador de asignación se dice trivial si y sólo si es implícito y todas las clases


bases directas de la dada, además de todos los datos miembros no-estáticos de
tipo no-predefinido, poseen un operador de asignación también trivial.

El esquema de copia por defecto es, en definitiva, el siguiente: para cada dato
miembro no-estático de la clase se busca, recursivamente, si el tipo o clase a que
tal miembro pertenece posee operador de asignación, de forma que si existe se
usa para copiarlo y si no se genera una copia bit-a-bit.

LOS LÍMITES EXPLÍCITOS DE LA ASIGNACIÓN IMPLÍCITA

El compilador no generará un operador de asignación por defecto en los siguien-


tes casos:

• Si la clase contiene un dato miembro no-estático de tipo referencia o de tipo


constante, como por ejemplo en:

class Politico {
const long ambicion; //una constante política
Persona& padrino; // imposible empezar sin él
// sigue descripción de clase
};

El compilador no podrá generar el operador implícito para tal clase, pues, co-
mo el leído lector conoce, los datos miembros de tipo referencia y constantes
tienen que ser inicializados y asignados a la vez: esto es, no pueden ser prime-

22
El nombre completo es “Working Paper for Draft Proposed International Standard for Informa-
tion Sytems -- Programming Language C++”, y su última versión ha sido sometida a revisión pú-
blica, con petición de comentarios, en los países cuyo capítulo nacional de estandardización así
lo ha requerido (no España, naturalmente).

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 85
ro inicializados para luego asignarles un valor. Precisamente por esto tales
miembros han de ser construidos forzosamente en la lista de inicialización de
todos los constructores, y no tienen cabida en una mera copia por asignación,
que es la que proporciona el operador implícito. El compilador originará, pues,
sendos errores (parece, así, que la ambición, por ser constante, y el padrino,
por constituirse en referencia, impiden, o cuando menos dificultan, la sustitu-
ción de los políticos).

• Si la clase contiene un dato miembro no-estático con un operador de asigna-


ción inaccesible o deriva de una clase base con un operador de asignación in-
accesible. Naturalmente la inaccesibilidad se refiere bien a la imposibilidad de
generación del operador de asignación por defecto bien a la restricción en la
cualificación de acceso de tal operador, implícito o no. En cuanto a la inexis-
tencia, veamos un ejemplo:

class PartidoPolitico {
Politico Presidente; // sin operador implícito o explícito
// y un largo etcétera
};

Si consideramos a la clase Politico con una ambición constante (como aca-


bamos de ver), la clase PartidoPolitico contendrá un miembro con un operador
de asignación inaccesible (de imposible generación implícita), y por tanto no
generará su propio operador de asignación por defecto. O sea, un Partido Po-
lítico no podrá ser cambiado, sustituido o absorvido por otro si su presidente
posee una ambición constante.

En cuanto a la cualificación de acceso, veamos este otro ejemplo:

class Politico {
private:
Politico& operator=( const Politico& );
// sigue descripción de clase
};

Aquí hemos proporcionado un operador definido-por-el-usuario, evitando la


necesidad de un operador implícito. Tal operador, no obstante, ha sido decla-
rado como privado, de tal forma que no es “accesible”, así que la clase Parti-
doPolitico no podrá generar tampoco su operador por defecto. De la misma
forma, la clase derivada

class Alcalde : public Politico { /* clase vacía */ };

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 86
no podrá generar, en cualquier caso de los expuestos, su propio operador de
asignación por defecto. Así, cuando el compilador se encuentre con la primera
expresión de asignación exclamará algo así como:

error EDC3209: class "X" does not have a copy assignment operator

LA COPIA DE UN PUNTERO GENERA ... ¡OTRO PUNTERO!

Atendamos a un error muy extendido. Como el lector bien sabe, el operador de


asignación de los datos miembros, si existe, implícito o no, únicamente se usará
cuando estos sean objetos, no punteros. Esto es, si tenemos el código:

class Politico {
String* nombre;
String alias;
};
otroPolitico = unPolitico;

y suponemos que la clase String tiene definido un operador de asignación (implí-


cito o no), la última línea hara que se copien los datos miembros de un objeto de
tipo Politico al otro de la siguiente forma: la dirección del puntero a “nombre” se
copiará en la variable correspondiente del objeto “otroPolitico”, mientras que para
la copia del dato miembro “alias” se usará del operador de asignación de String.
Naturalmente lo ideal sería que el compilador emitiera un “aviso” cuando un ope-
rador de asignación implícito copiara un puntero. La realidad, sin embargo, está
ahí para indicarnos que estas son cuitas del programador y que los compiladores
ya tienen demasiado trabajo. ¡Ah, la sin par realidad!

ASIGNACIÓN NO ES INICIALIZACIÓN

El siguiente código:

Politico unPolitico;
Politico otroPolitico = unPolitico;
unPolitico = otroPolitico;

equivale a:

Politico otroPolitico( unPolitico );


unPolitico.operator=( otroPolitico );

Esto es, la inicialización (realizada por un constructor) se da cuando se crea un


nuevo objeto, mientras que la asignación (realizada por el operador de asigna-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 87
ción) muta los valores en objetos ya creados. Por eso en la línea en que aparece
el símbolo “=” y donde se crea el objeto otroPolitico interviene el constructor (de-
nominado constructor de copia) en lugar del operador de asignación.

EL OPERADOR POR DEFECTO RESULTA DEFECTUOSO

Naturalmente la copia por defecto no funciona adecuadamente en numerosas


ocasiones, en concreto cuando las clases envueltas contienen miembros que re-
quieren la asignación dinámica de memoria. Así, por ejemplo, si nuestra clase
contuviera una cadena de caracteres de la forma:

class Politico {
public:
Politico( int numero = 0 ) : numeroDeLista( numero ){
cout << “Se crea el nuevo ideario ”
<< numeroDeLista << “\n”;
ideario = new String;
}
~Politico() {
cout << “Se destruye el ideario “
<< numeroDeLista << “\n”;
delete ideario;
}
private:
int numeroDeLista;
String* ideario;
// resto definición de clase
};

y nos fiáramos del operador de asignación implícito, obtendríamos resultados


inesperados, de forma que el siguiente código

Politico* unPolitico = new Politico( 1 );


Politico* otroPolitico = new Politico( 2 );
*unPolitico = *otroPolitico; // ¿pueden dos políticos compartir
// exactamente el mismo ideario?
delete otroPolitico; // imperativos electorales
delete unPolitico; // ¡la hecatombe!

originaría el siguiente resultado (en mi compilador IBM VisualAge C++):

Se crea el nuevo ideario 1


Se crea el nuevo ideario 2
Se destruye el ideario 2
Exception = c0000005 occurred at EIP = 12705.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 88
¿Qué ha ocurrido? Vemos que, en primer lugar, la copia implícitamente definida
origina que se copie en el objeto receptor ... el puntero a la cadena (como vimos
en el parágrafo anterior) y no la cadena en sí (para lo que habría que reservar es-
pacio y luego copiar los caracteres haciendo uso, por ejemplo, de strcpy(...) su-
poniendo que la representación interna de String sea un puntero a char y que
además sea accesible), de forma que tenemos dos objetos apuntando a la mis-
ma cadena de caracteres, siendo así que si uno de ellos cambiara la cadena ésta
cambiaría en ambos. Esta situación, en que ambos comparten exactamente la
misma cadena, causa que al operar el destructor de uno de ellos eliminando tal
cadena, el otro se quede conteniendo un puntero ... ¿a qué? ¡A basura! De esta
manera, cuando se aplica el destructor sobre el segundo objeto, el resultado es ...
el desastre. Naturalmente, y sin necesidad de aplicar el segundo destructor, el
segundo objeto está en estado inestable, y cualquier operación con él puede pro-
curar resultados indeseables. Incidentalmente cabe notar, también, que el ideario
primero queda libre ocupando memoria y ... ¡sin que nadie lo destruya!, gastando
así los preciados recursos del sistema. Por fin, y esto lo dejo en ejercicio al inteli-
gente lector, siempre desconfiado de las apariencias, ¿qué ocurriría si cambiá-
ramos el orden de destrucción de los objetos?

Realmente esto no funciona, así que, como afirman Meyers, Cargill y otros mu-
chos, siempre se definirá un operador de asignación para clases que usen de
memoria dinámica. Stroustrup propone una asimilación, a este respecto, entre
constructor de copia, destructor y operador de asignación, que Horstman formula
como regla de esta interesante forma: “Cualquier clase con un destructor no-
trivial necesita un operador de asignación definido-por-el usuario que realice
las copias de manera adecuada”, queriendo indicar que si el destructor tiene tra-
bajo que realizar, probablemente el operador de copia/asignación tenga pareci-
das tareas, lo que descalificaría al operador por defecto.

EL OPERADOR DE ASIGNACIÓN EXPLÍCITO

Si el operador por defecto no funciona correctamente en ciertas clases, habrá


que dotar a éstas con operadores de asignación expresos, solapando al opera-
dor implícito cuando exista o simplemente dotando de él a las clases en otro ca-
so.

En primer lugar hay que insistir en que, como ya hemos visto, el operador = debe
ser una función miembro (regla que se extiende a los tres operadores (), [] y ->),
de manera que su declaración como función global (o amiga, un caso especial de
aquélla), como por ejemplo

extern void operator=( Politico&, const Politico& );

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 89
originaría un error en compilación. El operador ha de ser también forzosamente
no-estático (no hay que olvidar que su funcionalidad se refiere a las instancias de
la clase).

Recordemos, por otro lado, que la asociatividad de un operador predefinido se


mantiene en sus sobrecargas. Tenemos, de esta manera, que cualquier operador
de asignación (predefinido, definido-por-el-usuario, implícito o explícito) es aso-
ciativo a la derecha, esto es, de derecha a izquierda, de manera que la expresión

a = b = c = d = e;

siempre se evaluará en el siguiente ordén:

( a = ( b = ( c = ( d = e ) ) ) );

DESIGNACIÓN NO ES ASIGNACIÓN

Pero veamos cómo añadir nuestros propios operadores de asignación: En prin-


cipio, cuando definamos expresamente tal operador ya no se generará el opera-
dor implícito, siempre que la declaración de nuestra función admita un solo pará-
metro del tipo const Politico& ó Politico&. Esto quiere decir que si declaramos el
operador de asignación únicamente como la siguiente función miembro no-
estática:

Politico& Politico::operator=( Politico* );

sí se producirá la definición del operador de asignación implícito, pese a que qui-


zás semánticamente se produzca una copia completa de un objeto en otro. De
esta manera tenemos que es sintácticamente aceptable la siguiente codificación:

class Politico {
public:
// operador ortodoxo explícito
Politico& operator=( const Politico& );
// sobrecarga (en el argumento, no en el tipo de retorno)
void operator=( Politico& );
// operador no-ortodoxo
Politico operator=( Politico* );
// etc., etc.
};

De hecho, para establecer la diferencia entre cualquier operador de asignación y


el operador ortodoxo, a este último se le denomina “operador de co-
pia/asignación”.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 90
Ahora bien, ¿es semánticamente aceptable que un operador de copia/asignación
devuelva un tipo distinto de Politico&? ¿Es razonable que se devuelva ‘void’ (aun-
que alguno pueda alegar que un político nunca devuelve nada)? ¿Qué tipo debe,
en definitiva, devolver el operador de asignación/copia?

LO QUE LA ASIGNACIÓN PRODUCE

Como el lector sobradamente sabe, la sobrecarga de operadores en C++ debe


intentar mimetizar el comportamiento predefinido de tales operadores, ajustándo-
lo a la idiosincrasia de la clase afectada. Si atendemos, pues, al comportamiento
de los tipos predefinidos, encontramos que el operador de asignación, como to-
dos los operadores de C++, produce un valor que, por convención, es el mismo
que el valor asignado (así el valor de la expresión ‘variable = 17’ es 17). Precisa-
mente el hecho que los operadores devuelvan un valor permite codificaciones
habituales en C++ como:

if ( unPolitico == otroPolitico ) {
// sigue ...

Vemos así que nuestro operador de asignación explícito ha de devolver el objeto


al que se asigna, a fin de permitir que las expresiones de asignación pueden apa-
recer como subexpresiones anidadas en expresiones más grandes. Naturalmente
podemos estimar que no deseamos codificar tales expresiones encadenadas, y
que precisamente para evitarlo devolvemos “void”. Bueno, esto puede ser cierto e
incluso correcto respecto de los operadores no-ortodoxos (con argumento distinto
de Politico& ó const Politico&), pero dada la reutilización latente de todo el códi-
go C++, es lógico suponer que cualquier usuario de nuestro código presupondrá
lo más intuitivo: esto es, que puede utilizar tales expresiones anidadas y que, por
tanto, no ha de cambiar su forma de codificar por el arbitrario capricho de otra
persona. Pero, exactamente ¿qué devolvería nuestro operador? ¿Un objeto? Evi-
dentemente no, porque si así fuera operaríamos con una copia del objeto. Debe
devolver, pues, una referencia a un objeto. Pero, ¿una simple referencia o una
referencia a un objeto constante? Fácilmente podemos advertir en el siguiente
ejemplo

int uno = 1;
int dos = 2;
int tres = 3;
cout << ( uno = dos = tres ) << dos << tres; // imprime ‘333’
cout << ( ( uno = dos ) = tres ) << dos << tres; // imprime
‘323’
cout << ( uno = dos = uno ) << dos << tres; // imprime
‘113’
cout << ( ( uno = dos ) = uno ) << dos << tres; // imprime
‘223’

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 91
que el resultado de una asignación puede ser, a su vez, asignado, así que el ope-
rador predefinido devuelve una referencia no constante. Pero en el ejemplo adver-
timos, también, los un tanto inesperados resultados de aplicar la asociatividad de
izquierda a derecha en la última línea. Debemos considerar, pues, a la hora de
codificar nuestra función de asignación si vamos a permitir tal comportamiento. Si
no deseamos que el resultado de una asignación pueda ser usado a la izquierda
de una asignación (como lvalue), que suele ser lo más prudente, debemos proto-
tipar así nuestro operador:

const Politico& Politico::operator=( const Politico& );

CONVERSIÓN, CONSTRUCCIÓN Y ASIGNACIÓN

¿Se debe limitar, con todo, el prototipo del operador de asignación explícito a las
sobrecargas con un argumento Politico& y const Politico&? Diríase que no. Exa-
minemos el siguiente código:

class Politico {
public:
Politico( char* promesas = 0 );
const Politico& operator=( const Politico& );
// ...
};
Politico puedoPrometerYPrometo;
puedoPrometerYPrometo = “Jamás dimitiré”;

En la última línea, dado que el operador de asignación de la clase espera una


referencia a un objeto Politico constante y encuentra un puntero a char, se produ-
ce una conversión implícita y entra en juego el constructor de la clase, creando a
partir de la cadena un objeto temporal Politico que, después de ser asignado a
“puedoPrometerYPrometo” se destruirá antes de finalizar el ámbito en que se
ubica la expresión. Si deseamos evitar la construcción de un objeto temporal
deberemos dotar a nuestra clase con una función como:

const Politico& Politico::operator=( char* );

evitando así la puesta en marcha del esquema de conversiones del lenguaje. Na-
turalmente la regla se puede generalizar de la siguiente forma: Se replicará la
sobrecarga de parametros unitarios de los constructores respecto del operador
de asignación.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 92
LA ASIGNACIÓN EN JERARQUÍAS DE HERENCIA

El operador de asignación evidentemente no se hereda, pues, como ya hemos


visto, si no se declara expresamente en una clase derivada, el compilador pro-
veerá uno por defecto, solapando así, en cualquiera de los casos, la función de la
clase base. Pero, atención, esto significa que se solaparán todas las funciones
con nombre “operator=(...)” y no sólo la función con un argumento Base& o const
Base&. Y es que el lector debe aprender que asignación y herencia no son pareja
de recibo: si la herencia es múltiple los problemas aumentarán; si virtual, los
problemas pueden resultar demasiado sutiles (al decir de los notarios: las
asignaciones de herencias pueden conllevar muchos problemas).

ASIGNACIÓN NO ES TRANSMUTACIÓN

Es difícil que un patán se convierta instantáneamente en educado, pues solo co-


piará -si acaso- algunas características de aquél (y si no, recordemos a El Bur-
gués Gentilhombre). ¿A qué viene esto? Bien: si a un determinado objeto se le
asigna otro, parece intuitivo esperar que el objeto a la izquierda del operador sea
sustituido por el objeto a la derecha del mismo. Pero esperar lo intuitivo en infor-
mática es tan aventurado como apostar por la lógica en derecho. Y es que, como
el lector ya sabe, en C++ un objeto no puede cambiar de clase. Así que el opera-
dor de asignación, implícito o explícito, en ningún caso cambiará la disposición o
tamaño del objeto a la izquierda del mismo (ni por supuesto del de la derecha).
Esto es, si tenemos:

Persona persona;
Politico politico;

y suponemos definido el operador de asignación en la clase Politico admitiendo


como parámetro una referencia a un objeto de tipo Persona, la línea

politico = persona;

no conseguirá que el político se convierta en una persona, pese a mucho que se


porfíe. Y es que eso sería tanto como convertir el plomo en oro. Pero veámoslo en
detalle:

class Persona {
public:
virtual void disculpa() {
cout << “Lo siento\n”;
}
};

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 93
class Politico : public Persona {
public:
void disculpa() {
cout << “Estaba así cuando llegué\n”;
}
};

Persona persona;
Politico politico;
// llama a Persona::operator=( const Persona& )
// pues se produce conversión implícita del tipo
// Politico a su clase base pública Persona
persona = politico;
// pero veremos que no se observa cambio alguno:
persona.disculpa(); // “Lo siento”
politico.disculpa(); // “Estaba así cuando llegué”
// lo que sigue es un error, porque intenta acceder
// con un objeto de tipo Persona a la función de
// asignación implícita, que tiene un prototipo
// Politico& Politico::operator=( const Politico& )
politico = persona;
// lo que sigue, sin embargo, sí funciona, porque
// se asigna una persona a la porción de Persona
// que existe en un Politico (al menos en teoría).
// Así que en realidad se aplica la llamada:
// ((Persona&)politico).Persona::operator(persona);
( Persona& )politico = persona;
// pero, como se ve, el político no cambia:
politico.disculpa(); // “Estaba así cuando llegué”

RECURSIVIDAD EXPLÍCITA: CÉSAR O NADA

Cuando el compilador provee un operador de asignación implícito está cargando


con un tedioso trabajo, posiblemente recursivo, de copia. Si codificamos nuestro
propio operador el compilador entenderá, sin embargo, que no debe en absoluto
intervenir. O sea, que no deberemos contar con una ayuda similar a la que se da
en los constructores: si no codificamos expresamente la copia de un dato miem-
bro tal copia no se producirá. Pero, cómo no, el problema se intensifica en una
jerarquía de herencia: el operador implícito automáticamente asigna las porciones
de las clases base directas de la dada (en el mismo orden en que fueron decla-
radas en la definición de la clase), pero en el explícito este trabajo de copia habrá
de ser expresamente codificado. Esto es, deberemos codificar algo como lo si-
guiente (y en adelante usaré structs discreccionalmente para evitar las etiquetas
de acceso público):

struct Persona {
Persona& operator=( const Persona& );
};

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 94
struct Politico : public Persona {
Politico& operator=( const Politico& politico ) {
// llama al operador de la clase base
Persona:operator=( politico );
// y seguidamente copia los datos miembros
}
};

Bien, pero ¿qué ocurriría si la clase base no dispusiese de operador de asigna-


ción explícito? ¡Nada nuevo! No olvidemos que el compilador proveería entonces
un operador implícito, que es una función miembro de pleno derecho y que, por
tanto, puede ser llamada expresamente, cual es el caso en el ejemplo anterior, o
también de la siguiente forma:

( Persona& )*this = politico;

Esta es la mejor manera de copiar la porción de una clase base y, de hecho, la


única pausible, pues de otra forma tendríamos que copiar uno a uno los datos
miembros de la clase base, pero estos usualmente son privados, por lo que ha-
bríamos de amigar las clases y ... bueno, esto nos plantea que habremos de tener
buenas razones para prescindir del operador de asignación por defecto.

CUIDADO CON LA AUTO-ASIGNACIÓN

Puestos a codificar nuestro propio operador de asignación, examinemos el si-


guiente ejemplo:

class Animal { /* definición */ };


class Persona : public Animal { /* ¡evidente! */ };
class X : public Animal { /* ¿quién sabe qué? */ };
class Politico : public Persona, public X {
public:
Politico ( char* unNombre = 0 ) {
ponNombre( unNombre );
}
const Politico& operator=( const Politico& politico ) {
delete[] nombre;
ponNombre( politico.nombre );
}
private:
void ponNombre( char* unNombre ) {
if ( unNombre ) {
nombre = new char[ strlen( unNombre ) + 1 ];
strcpy( nombre, unNombre );
} else {
nombre = new char[ 1 ];

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 95
nombre = ‘\0’;
}
char* nombre
};

Y ahora añadamos estas líneas de pura maldad:

Politico* politico = new Politico( “Fulano” );


Persona* persona = &politico;
*politico = *persona;

¿Qué ocurre en la peligrosa última línea? Pues que se ejecuta el código conteni-
do en el cuerpo de la función de copia/asignación, y en ésta lo primero es des-
asignar la memoria dinámica del dato miembro “nombre” en el objeto *politico,
para después asignar memoria suficiente (midiendo a partir del dato miembro
“nombre” de *persona) para seguidamente copiar la cadena de caracteres. ¿El
problema? Que al eliminar la cadena de caracteres del objeto *politico ... ¡se está
eliminando la cadena “nombre” del objeto *persona!, por lo que las subsiguientes
operaciones de medición y copia originan ... ¡el desastre! Pues claro -exclamará
el atento lector-: ¡los dos punteros apuntan al mismo objeto y -de nuevo claro- esto
no funciona! ¡Hay que cambiar el orden de las sentencias y modificar ligeramente
el código para solucionar este pequeño problema! (dejemos que el hirsuto lector
tome las riendas y veamos dónde nos lleva). “Basta con copiar la cadena del ob-
jeto a la derecha de la asignación (*persona en nuestro caso) a una cadena tem-
poral en calidad de variable local, a la que habrá que asignar memoria dinámica
suficiente midiendo la longitud de la cadena a copiar, para después poder elimi-
nar esta cadena sin problemas y luego -aquí el lector empieza a mostrar signos
de preocupación-, bueno luego habría que ver si hay más punteros o, mejor, co-
piar todo el objeto en un objeto temporal del mismo tipo, para así poder restituir
las variables y...” (en este momento el lector, totalmente avergonzado, reconoce
que su estrategia en absoluto funciona, y proyecta serios planes de enmienda).
En fin, retomemos el capítulo: si el problema sólo se produce en caso de auto-
asignación (directa o indirecta, por medio de lo que se denomina autoexplicativa
y bárbaramente “aliasing”), chequeemos esta circunstancia para adoptar diferen-
cialmente la estrategia adecuada, que evidentemente es ... ¡no copiar nada! Es
natural que si lo que se pretende es copiar dos objetos exactamente -bueno, in-
mediatamente matizaremos este adverbio- iguales, el mejor trabajo es no realizar
trabajo alguno, con lo que la copia quedará hecha. Nuestro operador quedaría de
la siguiente guisa:

const Politico& Politico::operator=( const Politico& politico ) {


if ( *this == politico )
return *this;
delete[] nombre;
ponNombre( politico.nombre );
}

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 96
¡Ah, esto parece perfecto! Pero las apariencias no son más que eso: pura vani-
dad. Si atendemos a la línea de chequeo vemos que se comparan los objetos a
ambos lados del operador de asignación (objetos, no punteros), y además
haciendo uso de un operador de comparación que ha de suponerse definido para
la clase Politico. Atendamos a los hechos.

IDENTIDAD, IGUALDAD Y ... ¿FRATERNIDAD?

¿Cuál es el criterio que nos permitirá comparar dos objetos respecto de la auto-
asignación, como hemos hecho en el parágrafo anterior? ¿La igualdad o la iden-
tidad? Parece claro que, en general, la igualdad no, pues ésta se basa en la
exacta similitud de los valores de dos objetos, de forma que, por ejemplo, dos
objetos de una clase con un único atributo de nombre serían iguales si sus nom-
bres coincidieran. La identidad, sin embargo, se refiere al hecho diferencial del
objeto: yo soy yo, independientemente de los valores que asuma mi representa-
ción interna. Claro que en la práctica las cosas no son tan sencillas. ¿Serían por
ejemplo idénticas dos personas con el mismo NIF? Esto es, ¿sería adecuada la
siguiente función?

bool Persona::operator==( const Persona& persona ) {


return nif == persona.nif;
}

Pues bien, sólo si el establecimiento de NIFs asegurara su biunivocidad respecto


de cada persona, y únicamente dentro del espacio geográfico español, y sólo si
se descarta a los menores. Demasiadas condiciones. Meyers sugiere la imple-
mentación de una función virtual que devuelva una cadena identificativa especial y
única para cada objeto. Claro que esto podría acarrear problemas en un entorno
distribuido donde la asignación de identificadores no estuviera sincronizada. ¿Y
por qué no usar del mecanismo de identificación de objetos propio de C++? En
C++ todos los objetos son transitorios (transient), esto es, no sobreviven al proce-
so o CPU que los creó, y su naturaleza mutable está representada por su direc-
ción en memoria. ¿Por qué no usar, pues, tal dirección y comparar simplemente
punteros para verificar la identidad? Pues porque, como hemos visto en el ejem-
plo del parágrafo anterior de una clase con herencia múltiple, un objeto puede ser
referenciado a través de distintos punteros (correspondientes a las distintas por-
ciones de las clases base que lo componen), con lo que el esquema alternativo
en que sólo se comparan punteros:

if ( this == &politico )
return *this;

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 97
no tiene por qué necesariamente funcionar. El modelo de objetos propuesto por el
OMG (Object Management Group) en CORBA (Common Object Request Broker
Architecture) propone unos identificadores de objetos (denominados “objrefs”)
que no tienen por qué ser únicos para un sólo objeto en tanto cada uno de estos
puede existir en distintos contextos no solapados. De cualquier manera el enfo-
que habitual en bases de objetos es la asignación automática y opaca por el sis-
tema de un OID (Object Identifier) a cada objeto. Pero estamos entrando en un
tema que examinaremos con más detenimiento -¿cómo no?- en el capítulo dedi-
cado a Bases de Objetos o Bases de Datos Orientadas-a-Objetos. A fin de cuen-
tas este enfoque de “luego veremos” es parte sustancial de la Orientación-a-
Objetos: involución permanente y citas circulares.

SOBRE LA ASIGNACIÓN BIDIRECCIONAL

Hasta ahora hemos visto cómo una referencia o puntero de una clase derivada de
una dada se convierte convenientemente en un puntero o referencia a la clase
base para encajar con su operador de asignación, implícito o no. Pero seamos
exigentes: si tenemos

struct Persona {
const Persona& operator=( const Persona& );
};

struct Politico : public Persona {


const Politico& operator=( const Politico& );
};

Persona persona;
Politico politico;

y quisiéramos codificar

politico = persona;

o, en general, deseáramos una jerarquía de clases en que los valores de los obje-
tos pudieran ser asignados con independencia de las clases involucradas (siem-
pre en la misma jerarquía derivativa), necesitaríamos añadir, en el presente caso,
una nueva función a la clase Politico:

const Politico& Politico::operator=( const Persona& );

que manejara adecuadamente la asignación de clase base a clase derivada (lo


usual es exactamente lo contrario, de clase derivada a clase base, haciendo nor-
malmente uso de la conversión implícita de tipos en caso de ámbitos con deriva-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 98
ción pública). Pero esto significa que ahora tendremos en nuestra clase Politico
dos funciones, una con argumento “const Persona&” y otra con argumento “const
Politico&”, de parejo contenido (relacionadas cuando menos por una inclusión no-
estricta). Y si la jerarquía es una larga cadena de derivación tendremos que cada
clase constará de un operador de asignación más que la anterior, y que la adición
de una clase en medio de tal jerarquía obligaría a añadir una nueva sobrecarga
del operador de asignación en las clases derivadas de la insertada. Bueno, es un
panorama ciertamente poco elegante y difícil de explicitar en una jerarquía de uso
comercial. ¿No habría una forma de usar tan sólo un operador que reuniera la fun-
cionalidad de los otros con argumentos de clases bases? En realidad el compor-
tamiento de tales funciones resulta seguir el siguiente esquema:

const Politico& Politico::operator=( const Persona& persona ) {


// se chequea la autoasignación
Persona::operator=( persona );
// y aquí las escasas particularidades semánticas
return *this;
}

const Politico& Politico::operator=( const Politico& politico ) {


// se chequea la autoasignación
Persona::operator=( politico );
// y aquí la copia del resto de datos miembros
return *this;
}

¿Y si dispusiéramos de un sólo operador con argumento de referencia a la clase


base y la actuación se determinara mediante un cast dinámico? Veámoslo:

const Politico& Politico::operator=( const Persona& persona ) {


if ( *this == persona )
return *this;
const Politico* politico =
dynamic_cast< const Politico* >( &persona );
// si politico es distinto de cero significa que “persona”
// es una referencia a Politico o a una clase derivada, con
// lo que la función con argumento “const Politico&” ya no
// es necesaria, pues ésta hace el trabajo de ambas.
if ( politico )
// aquí se copiarían los datos miembros de Politico
Persona::operator= ( persona );
return *this;
}

Con esta solución eliminaríamos la posible explosión combinatoria (es un decir) y


conservaríamos una sola signatura para el operador de copia/asignación a través
de la jerarquía. Naturalmente si se produjera una modificación en la jerarquía el
cuerpo de tales funciones debería ser también modificado, pues se llama expre-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 99
samente al operador de la clase base directa. ¿Es, pues, una buena solución?
¡Ea! Seguro que el inteligente lector ya supone, a estas alturas, que con el opera-
dor de asignación en C++ no hay solución única. Podríamos decir que el opera-
dor de asignación implícito proporciona lo que se denomina una “copia superfi-
cial” (shallow copy), mientras que nuestra recursividad manual procura la llamada
“copia profunda” (deep copy). La copia superficial presenta escasos problemas
conceptuales, mientras que la copia profunda, bueno, por citar sólo un problema:
¿qué ocurre cuando se dan referencias circulares en la recursividad hacia atrás
planteada? ¿Qué ocurre, por otro lado, con el operador de comparación para el
chequeo de la autoasignación? ¿No debería tal operador codificarse de la misma
manera que el de asignación montando casts dinámicos? ¡Seguro que sí! Diga-
mos que el aumento de complejidad supone un parejo aumento de la capacita-
ción del programador respecto del lenguaje. Así que, como dicen en Tráfico, “si
no está realmente seguro, no adelante”, so pena de encontrarse con demasiados
problemas y otras tantas peligrosas implicaciones. Y si no veamos qué ocurre
cuando el problema se hace virtual (¡Problemas virtuales! Gracián caeríase muer-
to ya).

POLIMORFISMO EN ASIGNACIÓN

Como quiera que un operador de asignación es una función miembro de una cla-
se (se trate de un operador de copia/asignación o de un operador de asignación
cualquiera), en tal calidad puede ser perfectamente declarado como virtual. La
única salvedad a considerar aquí es que el operador de copia/asignación de la
clase derivada no se constituye en destinatario del mecanismo virtual respecto
del operador de copia/asignación en la clase base, lo cual es ciertamente lógico,
merced a la diferencia del tipo del argumento y al especial mecanismo implícito
del operador. Examinemos, así, el siguiente código:

struct Persona {
virtual int operator=( char* );
virtual Persona& operator=( const Persona& );
};

struct Politico : Persona {


virtual int operator=( char* );
virtual Politico& operator=( const Persona& );
virtual Politico& operator=( const Politico& );
};

Politico unPolitico, otroPolitico;


Persona* persona = &unPolitico;
// llamada a Politico::operator=( char* )
persona->operator=( “Disraeli” );
*persona = “Disraeli”;
// llamada a Politico::operator=( const Persona& )
persona->operator=( otroPolitico );

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 100
*persona = otroPolitico;
// llamada a Politico::operator=( const Politico& )
unPolitico = otroPolitico;

¡Ah, todo perfecto! Si se definiera, no obstante, únicamente la función

Politico& Politico::operator=( const Politico& );

en la clase Politico, desechando la que observa un argumento const Persona&, sí


se solaparía el mecanismo virtual, pues tal función escondería a la de la clase ba-
se. Este comportamiento resulta, por ejemplo, en la siguiente lógica situación: si
el operador de asignación se declara virtual y una clase derivada no implementa
operador explícito, el mecanismo virtual se resolverá, finalmente, en una llamada
al operador de asignación correspondiente a la clase base directa de la dada.
¿Qué pasaría, sin embargo -y esto se lo dejo de elemental ejercicio al inquieto
lector-, si tal clase base no dispusiera de operador de asignación explícito? Po-
dríamos evitar problemas, no obstante, si codificáramos en cada clase de la je-
rarquía la función con cast dinámico que hemos visto antes. Con tal enfoque con-
vendría declarar siempre el operador de asignación como virtual, lo que permitiría
codificaciones del tipo:

Politico& creaPolitico( const Persona& ) {


persona = personaLobotomizada;
}

pudiendo pasar como un argumento una referencia a una Persona o una referen-
cia a una clase derivada de ésta.

CUANDO LA ASIGNACIÓN ES INDESEABLE

¿Qué ocurre si no se desea que el cliente de una clase utilice la semántica de


copia para cambiar los valores de un objeto de tal clase? Lo usual cuando no se
desea procurar cierta funcionalidad es evitar su inclusión en el protocolo de la
clase. Aquí, sin embargo, la elipsis haría que entrara en marcha el mecanismo
implícito, generando de cualquier forma la función indeseada. ¿La solución? Plum
y Saks proponen declarar el operador de asignación pero sin dotarlo de cuerpo,
de manera que su uso generaría un error en el enlazado del programa. No es és-
ta, empero, una solución segura, pues al estar declarada la función en la defini-
ción de la clase, el usuario puede añadir la definición en un aparte. Un procedi-
miento más seguro consiste en declarar además el operador de asignación en la
sección privada de la clase, de forma que el compilador señalaría como error
cualquier uso del mismo, así como impediría la declaración del operador implícito
en las clases derivadas. El problema es que si en la jerarquía en cuestión el ope-
rador de asignación ha sido declarado como virtual, el lector ya sabe que la cuali-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 101
ficación de acceso al operador que se resuelve en tiempo de ejecución depende
de la cualificación de acceso de la clase a que pertenece el puntero o la referen-
cia desde la que se accede a la función. Esto es, si tenemos:

struct Persona {
virtual Persona& operator=( const Persona& );
};

class Politico : public Persona {


// operador privado
Politico& operator=( const Persona& );
};

Politico unPolitico, otroPolitico;


Persona* persona = &unPolitico;
persona->operator=( otroPolitico ); // OK

la última línea accede al operador privado a través del puntero a la porción de


Persona en político, con lo que se vulnera nuestra intención inicial. Tenemos, así,
que lo mejor sería que el operador de asignación no fuera virtual, replicando en
este caso en cada clase los argumentos del operador de asignación de sus cla-
ses base. Como vemos, se trata de un enfoque sustancialmente distinto al hasta
ahora expuesto. En fin, que en C++, como en el mus, objetivamente no puede ga-
narse a todo (otra cosa es que en la práctica una buena pareja saque el máximo
partido a su juego y fuerce continuamente la victoria).

LA ASIGNACIÓN CON CLASES BASE VIRTUALES


Retomemos un ejemplo anterior el en que se da derivación múltiple:

class Animal {};


class Persona : public Animal {};
class X : public Animal {};
class Politico : public Persona, public X {};

Politico unPolitico, otroPolitico;


unPolitico = otroPolitico;

La última línea originará que se defina el operador de copia/asignación implícito


para la clase Politico, que a su vez llamará a los operadores implícitos de las cla-
ses bases directas Persona y X (por este orden), los cuales, a su vez, llamarán al
operador implícito de la clase Animal, que resultará llamado dos veces (una por
cada clase derivada, correspondientes a las dos porciones físicas de la clase
base Animal contenidas en Politico). Bien, aparte que esto pueda hacer pensar al
lector, ¿qué ocurriría si la derivación fuera virtual? Esto es, si se tuviera:

class Animal{};

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 102
class Persona : virtual public Animal {};
class X : virtual public Animal {};
class Politico : public Persona, public X {};

Pues que, de acuerdo con el WP, queda sin especificar si la porción de la clase
base virtual Animal se asignará una o dos veces mediante el operador de co-
pia/asignación definido implícitamente en la clase Politico. El comportamiento
quedará, en cada caso, determinado por el compilador concreto utilizado, con lo
que viene a colación el consejo de Cargill: “No trates de aprender la semántica de
la herencia múltiple de tu propio compilador”. Naturalmente tal inespecificidad
puede ser salvada -¿cómo no?- codificando expresamente todos los operadores
de asignación y procurando manejar las asignaciones de todas las porciones, tal
y como muestra el mismo Cargill. De cualquier forma en el ARM se reconoce que
llegar a un estado de consistencia en jerarquías virtuales con operadores de asig-
nación es francamente difícil.

ALGUNOS CONFUSOS CONSEJOS

Ante todo lo expuesto, ¿qué hacer? ¿Codificar o no el operador de asignación?


¿Declarar el operador virtual? ¿Usar del mecanismo de resolución dinámica de
tipos? Atendamos a lo que opinan los expertos: Saks afirma que se debe trabajar
con el operador implícito a menos que haya una razón funcional que lo impida,
mientras que Taligent indica que hay que explicitarlo todo, que es mejor codificar
un costoso operador de asignación, cuya funcionalidad sea la misma que la del
operador implícito, que dejar a la imaginación del cliente de la clase el motivo de
una supuesta elipsis. Evidentemente aquí se oponen dos criterios bien distintos:
el del programador individual y el del programador de marcos o bibliotecas de
clases, que serán utilizadas de forma intensiva por otros programadores o usua-
rios finales. ¿Sabe el lector que aconseja la Guía de Taligent? ¡Pues que en caso
de duda se consulte a un arquitecto de software! Y es que, como dijera Disraeli,
“cuánto más fácil resulta ser crítico que correcto”. Así están, con todo, las cosas.
La conclusión, así, es la misma que enunciara Bernard Shaw: respecto del ope-
rador de asignación “la regla de oro es que no hay reglas de oro”.

REFERENCIAS DIRECTAS
• Working Paper for Draft Proposed International Standard for Information Sys-
tems -- Programming Language C++, ANSI X3J16, 28-abril-1995.
• The C++ Programming Language, 2nd Edition, Bjarne Stroustrup, 1991, Ad-
dison-Wesley, 0-201-53992-6.
• Mastering Object-Oriented Design in C++, Cay S. Horstmann, 1995, Wiley, 0-
471-59484-9.
• On To C++, Patrick Henry Winston, 1994, Addison-Wesley, 0-201-58043-8.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 103
• C++ Programming Guidelines, Thomas Plum & Dan Saks, 1991, Plum Hall, 0-
911-537-10-4.
• C++ Strategies and Tactics, Robert B. Murray, 1993, Addison-Wesley, 0-201-
56382-7.
• Class Construction in C and C++, Roger Sessions, 1992, Prentice Hall, 0-13-
630104-5.
• Effective C++: 50 Specific Ways to Improve Your Programs and Designs,
Scott Meyers, 1992, Addison-Wesley, 0-201-56364-9.
• Advanced C++: Programming Styles and Idioms, James O. Coplien, 1992,
Addison-Wesley, 0-201-54855-0.
• The Design and Evolution of C++, Bjarne Stroustrup, 1994, Addison-Wesley,
0-201-54330-3.
• Taligent’s Guide to Designing Programs: Well-Mannered Object-Oriented
Design in C++, Taligent, 1994, Taligent Press, 0-201-40888-0.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 104
PATRONES DE DISEÑO
6
E
n el capítulo “Gestión de Proyectos Orientados-a-Objetos” se expone de
forma breve el panorama aparentemente confuso y un tanto desolador en el
que parece estár sumido el Diseño Orientado-a-Objetos (OOD), necesita-
do de soluciones formales claras, sectorialmente completas, efectivas, compren-
sibles y aplicables en la gestión diaria de proyectos software. Se trata, en definiti-
va, de aplicar la modularidad propugnada para la Programación Orientada-a-
Objetos (básicamente según los criterios y principios que expone Bertrand Meyer
en “Object-Oriented Software Construction”) a las áreas del Análisis y Diseño
Orientados-a-Objetos. Y es en este contexto donde aparecen, como sacados de
un cuento de Perrault, los patrones de diseño (inevitable traducción, quizás des-
afortunada, de Design Patterns: DPs). Basados en la obra del arquitecto y mate-
mático estadounidense Christopher Alexander, los patrones, cual solución balsá-
mica, pretenden encerrar la esencia cualitativa de distintas soluciones sectoriales
software y generar componentes de diseño universalmente reutilizables. Bien: el
propósito es encomiable, y merece la pena examinarlo en algún detalle.

LA CALIDAD SIN NOMBRE

Los analistas/diseñadores con gran experiencia aplican, de forma mayormente


intuitiva y automática, criterios precisos que, en un sentido global, solucionan de
forma elegante y efectiva los problemas de modelado software de sistemas re-
ales. Usualmente estos diseñadores utilizan métodos, estructuras y subsistemas
que son, a la vez, herramientas del diseño y partes de la solución final, de una
manera que difícilmente puede transmitirse, en un sentido formal, a especialistas
menos expertos. Estos bien-dotados diseñadores poseen, también, un sentido
especial que “detecta” la completitud, en un sentido eminentemente arquitectóni-
co, de un determinado diseño, con independencia de las posibles métricas y pa-
radigmas utilizados. Naturalmente lo ideal sería extraer la quintaesencia de estos
afortunados diseños para formular una suerte de “bálsamo de fierabrás” que pu-
dieran ingerir los diseñadores noveles. Aunque, ¿existe en verdad una parte co-
mún en los buenos diseños, a veces tan dispares entre sí? Alexander así lo afir-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 105
ma, y da a esta parte la elusiva calificación de “la calidad que no se puede nom-
brar”.

Alexander sostiene que existe un “algo innombrable” que no puede ser modelado
únicamente por medio de un conjunto arbitrario de requerimientos. Esto es, que
los sistemas poseen una esencia cualitativa que les otorga verdadera identidad y
equilibra sus fuerzas internas. Y si bien tal calidad no tiene nombre, sí pueden ad-
jetivarse los sistemas que la poseen: vivos, completos, libres, exactos, desperso-
nalizados y eternos. Bien, esto puede sonar un tanto místico, pero así suena
Alexander todo el tiempo. Pongamos un ejemplo arquitectónico práctico: si nos
fijamos en las construcciones de una determinada zona rural observaremos que
todas ellas poseen apariencias parejas (tejados de pizarra con gran pendiente,
etc.), pese a que los requerimientos personales por fuerza han debido ser distin-
tos. De alguna manera la esencia del diseño se ha copiado de una construcción a
otra, y a esta esencia se plegan de forma natural los diversos requerimientos. Di-
ríase aquí que existe un “patrón” que soluciona de forma simple y efectiva los pro-
blemas de construcción en tal zona. Este patrón, no obstante, para ser considera-
do como tal, y siempre según Alexander, debe encerrar la capacidad de conectar
con la cualidad buscada.

PATRONES DE DISEÑO SOFTWARE

Tal y como Alexander expone, “cada patrón describe un problema que ocurre una
y otra vez en nuestro entorno, para describir después el núcleo de la solución a
ese problema, de tal manera que esa solución pueda ser usada más de un millón
de veces sin hacerlo siquiera dos veces de la misma forma”. Pero pasemos ya al
área software: un patrón de diseño23 es, pues, una solución a un problema en
un determinado contexto. Tal solución es, empero, a la vez parte del “qué” y del
“cómo” del sistema completo a construir: esto es, la pieza que conforma el patrón
software es como la pieza del patrón de sastre que se utiliza para confeccionar
vestidos y trajes, pues tal pieza, aparte de contener las especificaciones de corte
y confección del producto final, representa a la vez, en apariencia, una parte de tal
producto textil. Pero vayamos a un ejemplo práctico.

Es usual que en el diseño de distintos sistemas software nos encontremos con


similares problemas a resolver, y uno de los más frecuentes es el que nos enfren-
ta a un conjunto de objetos (o estructuras de datos -en fin, seamos comprensivos-
) cuyo contenido debemos recorrer, objeto a objeto, con el fin de operar (compa-
rar, coleccionar, etc.) con los valores que encontremos. Usualmente, también, la
solución consiste en usar de un objeto “iterador” (o función iteradora) que recorre
la colección de elemento en elemento. Pero pronto constatamos que el problema

23
También denominado “patrón software” o “patrón generativo”.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 106
necesita una solución matizadamente distinta en cada contexto en el que se plan-
tea: así, por ejemplo, no resulta igual iterar por una colección que por un objeto
compuesto por capas envolventes, como tampoco es exactamente igual un reco-
rrido que la conjunción concurrente de varios iteradores sobre la misma colec-
ción, ni resultan iguales los iteradores concretos que aquellos otros polimórficos,
aunque, de alguna manera, la esencia de la iteración es la misma para todos los
problemas de ese tipo. Aquí tenemos, por tanto, un “patrón”, conocido como “Ite-
rador” o “Cursor”, cuya descripción podría darse de acuerdo con el siguiente es-
quema (extraído del libro del GOF):

• Intención: sucinta descripción de lo que se pretende conseguir con el patrón.

• También Conocido como: otros nombres del mismo patrón.

• Motivo: explicación justificativa de la necesidad de que el patrón exista como


entidad autónoma.

• Aplicabilidad: lista de usos para los que resulta especialmente adecuado el


patrón que se describe.

• Estructura: descripción gráfica de los comportamientos, acciones y relaciones


de los objetos que participan en el patrón.

• Participantes: diccionario de las partes que componen el patrón.

• Colaboraciones: diccionario de las relaciones e interacciones entre los parti-


cipantes en un patrón.

• Consecuencias: detalle de los posibles beneficios y perjuicios que pueden


derivarse del uso del patrón.

• Implementación: detalle de las posibles implementaciones y catálogo de las


decisiones de diseño en la codificación de soluciones concretas basadas en el
patrón.

• Código de Ejemplo: planteamiento de código práctico referido a un ejemplo (o


ejemplos) suficientemente representativo del uso del patrón.

• Usos Conocidos: detalle de bibliotecas, productos y sistemas en que se ha


utilizado el patrón.

• Patrones Relacionados: referencias a otros patrones que bien sean


directamente utilizados por el descrito bien representen soluciones
complementarias o suplementarias al mismo.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 107
Si accedemos a un catálogo que contenga este patrón (“iterador”), encontrare-
mos que se refiere al acceso secuencial a objetos sin entrar en su representación
interna: esto es, sin directamente cambiarlos. Porque resulta que si lo que que-
remos es actuar de alguna manera sobre los objetos a recorrer, entonces debe-
remos usar de otro patrón: el conocido como “visitador”. Vemos, pues, que ya
existe una granulación diferencial de patrones que pretende encapsular la expe-
riencia adquirida en muchos proyectos software de forma límpida y reutilizable. Y
es que, como el lector fácilmente comprenderá, lo difícil de los patrones es preci-
samente descubrirlos, pulirlos y formalizarlos, porque la realidad, como una al-
fombra, necesita ser extendida y desenrollada para que se puedan apreciar sus
“patrones”, pues si no éstos aparecen desfigurados y confusos24.

CATÁLOGOS DE PATRONES

Si aceptamos, pues, que los patrones pueden resultar útiles en el desarrollo de


software, el siguiente paso es reunirlos en catálogos de forma que resulten acce-
sibles mediante distintos criterios, pues lo que necesitamos no es tan sólo la
completa descripción de cada uno de los patrones sino, esencialmente, la co-
rrespondencia entre un problema real y un patrón (o conjunto de patrones) deter-
minado. Desafortunadamente, y a pesar de la segmentación en la exposición de
los patrones detallada anteriormente, tal objetivo todavía no se ha conseguido:
existen catálogos de patrones (como el libro del GOF, que se detalla más adelan-
te), pero la capacidad de elección todavía se basa en el conocimiento completo
de los mismos. Pero esto se apreciará mejor en un ejemplo.

Recientemente un cliente nos enfrentó al diseño de una biblioteca genérica de


impresión multiplataforma cuyo uso principal sería generar en tiempo de ejecu-
ción objetos concretos de tipo “Impresora”, con distintas implementaciones, a los
que dirigir mensajes de impresión en base a un interfaz genérico predefinido.
Tras el pertinente estudio estimamos que el problema básico de diseño se podía
resolver utilizando dos patrones (y en esta elección influyó notoriamente el lengua-
je a utilizar, C++): “Prototipo”, para diferir hasta tiempo-de-ejecución la creación
de un determinado objeto impresora, clonando un objeto estático, y “Puente”, para
separar el interfaz (establecido en una clase base “gruesa” que encerraría toda la
funcionalidad) de las posibles implementaciones (los lenguajes de impresora)
que se establecen en una jerarquía paralela aparte 25. Naturalmente este resultado
se basó en el anterior conocimiento de un suficiente número de patrones, de ma-
nera que, en realidad, si no hubieran existido patrones se habría solucionado de

24
¡Vaya, esto suena profundo!, pero debo reconocer que el mérito, en traducción libre, es de
Themistocles.
25
En realidad se necesitó algo más que dos patrones (como, por ejemplo, un subsistema gestor
de implementaciones), pero esa es otra historia (nadie ha dicho que los patrones sean autosufi-
cientes para completar sistemas software).

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 108
parecida manera, pues se trataba de problemas que ya se habían abordado ante-
riormente. Pero, claro, lo que se pretende con un catálogo de patrones no es
favorecer al diseñador experto (que quizás no necesite en absoluto de los
patrones, aunque el estudio de la formalización del conocimiento es siempre
recomendable), sino más bien ayudar al diseñador inexperto a adquirir con cierta
rapidez las habilidades de aquél, como también comunicar al posible cliente, si
es el caso, las decisiones de diseño de forma clara y autosuficiente. Se trata, en
definitiva, de un medio para comunicar la experiencia de forma efectiva,
reduciendo (o aplastando, aplanando o suavizando) lo que sorprendentemente se
conoce como “curva de aprendizaje” del diseño.

¿PATRONES ORIENTADOS-A-OBJETOS?

¿Hablamos de diseño de software o más bien de diseño-orientado-a-objetos de


software? Parecería que, según lo hasta ahora visto, los patrones habrían de aco-
plarse bien a cualquier esquema. Bien: en teoría sí, pero en la práctica la casi to-
talidad de los trabajos y catálogos en este campo (como el libro GOF) se refieren
a sistemas orientados-a-objetos. Parece que esto responde a que, simplemente,
el “patrón de conducta” en diseño es ahora el OOD. Claro que algunos pretende-
rían una solución para todo26, pero parece claro que las ideas de modularidad
reutilizable en que se sustentan los patrones encuentran asiento natural en siste-
mas de diseño con las facilidades modulares de la orientación-a-objetos.

TELEPREDICADORES DE SOFTWARE

La Programación Estructurada, la Orientación-a-Objetos, y ahora ... ¡los Patrones!


En la comunidad software, como en la religiosa y en la política, aparecen cada
tanto soluciones pretendidamente salvadoras que ofrecen a sus postulantes cam-
bios tan radicales que resultan tentadoramente creíbles27. Y es que, ¿con qué
herramientas se mide la idoneidad de una teoría empírica? Usualmente infierien-
do su correctitud desde exitosos ejemplos prácticos. Entonces, si examinamos
los patrones, ¿por qué no examinar, en primer lugar, su aplicación en la arquitec-
tura tradicional? ¿Por qué no estudiar los exitosos trabajos de Alexander? Pues
porque el brillante prestigio teórico de Alexander (un tanto discutido en su propia
área) está ligado a una desazonadora ejemplificación práctica de sus proyectos:
desde inesperados -por no decir turbadores- resultados a fastuosos fracasos

26
Es como si desearan una suerte de quinta sinfonía de Prokofiev (la fusión del clasicismo
tradicional y la vigorosa modernidad), pero sólo obtuvieran el poderoso scherzo del segundo movi-
miento. ¡Ahí es nada!
27
Remy de Gourmont afirmaba que los hombres son tan estúpidos que dando un nombre nuevo a
una cosa vieja creen haber pensado algo nuevo. Bierce, más práctico, ironizaba: “Cogito cogito
ergo cogito sum”.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 109
(como el de la comunidad de Mexicali). ¿Entonces -preguntará el ahora decidi-
damente inquieto lector- los patrones no funcionan en arquitectura? Bueno, queri-
do lector, piense que Alexander, en sí, no es la piedra angular de ningún sistema
software. Recordemos, por ejemplo, que Collodi fue un mediocre escritor antes
de “Pinoccio” y siguió siéndolo después de éste. A veces una solitaria buena idea
genera resultados extraordinarios, y Alexander expone, en un inglés exaltado, va-
rias excelentes ideas. Piénsese, también, que los hijos no resultan nunca como
los padres porfían han de ser, y es que en la comunidad software a Cronos se lo
comerían sus hijos sin darle tiempo siquiera a posar para Goya. Parece, por otro
lado, que los patrones son “nuevos”, pero en absoluto es así, pues existen patro-
nes-marcos formales desde hace tiempo: MVC de Smalltalk, MacApp, etc., así
como patrones-de-rutinas que todos conocemos: las bibliotecas de algoritmos.
Resulta, al fin, que nos encontramos en una fase incierta de captura y formaliza-
ción de la rica experiencia en diseño extendida en multitud de maduros sistemas
software, y aparece claro que el sentido de la marcha es irreversible. ¿Patrones?
¡Sí, pero con prudencia!

PATRONES ELECTRÓNICOS

Dados la juventud y el estado “de permanente construcción” de los patrones, el


lector quizás desee tener acceso a algunas de las fuentes on-line. Helas aquí:

World-Wide-Web: obviaré las referencias Gopher y de ftp-sites, que pueden ser


accedidas mediante el WWW.

• La Página de Patrones (Patterns Home Page) en el Web está mantenida por


Richard Helm (sí, uno del GOF) y su URL es: http://st-
www.cs.uiuc.edu/users/patterns/patterns.html. Aquellos que no puedan acceder
al Web pueden obtener esta página vía e-mail, pues existe un servidor en el
CERN que envía por correo electrónico la página WWW que se le pida: sólo
hay que envíar un mensaje via correo electrónico a <co-
de>listproc@www0.cern.ch. La línea del asunto no tiene importancia, pero en el
cuerpo del mensaje debe figurar <code>"www [URL completa]" (en este caso
“www http://st-www.cs.uiuc.edu/users/patterns/patterns.html”).

• También en el Web, en “http://g.oswego.edu/dl/pd-FAQ/pd-FAQ.html” o en


“http://gee.cs.oswego.edu/pd-FAQ.html” puede encontrarse un FAQ (Frequent
Answers & Questions: Frecuentes Preguntas y Respuestas) de patrones.

Listas de correo: esta es la materia viva de la que el lector podrá extraer lo último
en patrones. Es aquí, también, donde se pueden exponer ideas, pedir consejos,
etc.

• Discusión de Patrones (patterns-discussion): En esta lista se discuten la teoría


y aplicación de patrones, incluidos los trabajos de Alexander y si sus ideas

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 110
pueden o no aplicarse al software, así como herramientas y métodos que so-
porten patrones en el desarrollo de software, y si los patrones software son si-
milares o diferentes de otras clases de patrones. Para añadirse a la lista debe
enviar un mensaje a listserver@cs.uiuc.edu con el siguiente cuerpo: “subscribe
patterns-discussion Nombre Apellidos”. Si tienen problemas para obtener la
subscripción y contraseña, pónganse en contacto con Ralph Johnson (sí, ama-
ble lector, otro del GOF), el propietario de la lista, en johnson@cs.uiuc.edu.

• Patrones de Negocio (business-patterns): En esta lista se pretenden dejar de


lado los patrones de bajo-nivel para tratar de aquéllos con relevancia para el
mundo empresarial. Para suscribirse hay que enviar un mensaje a business-
patterns-request@cs.uiuc.edu con la línea “subscribe” como cuerpo.

• Patrones del Clan-de-los-Cuatro (Gang-of-4-Patterns): Esta es una lista cen-


trada en el libro del GOF, aunque sus autores se pasean por las otras listas
también. Para añadirse a la lista debe enviar un mensaje a listser-
ver@cs.uiuc.edu con el siguiente cuerpo: “subscribe gang-of-4-patterns Nom-
bre Apellidos”.

• Compuserve: En la sección 3 (Study Hall) del forum CLMFORUM de Compu-


serve se está discutiendo actualmente el libro del GOF.

Conferencias: aparte de las que seguidamente se mencionan hay que reseñar la


sección de OOPSLA dedicada a los patrones: en realidad la mayoría de confe-
rencias sobre Orientación-a-Objetos empiezan a considerar -si es que ya no lo
han puesto en práctica- la inclusión de una sección específica de patrones.

• PLoP ‘94: La Primera Conferencia Anual sobre Patrones (PLoP: Pattern Lan-
guages of Programs) se celebró del 4 al 6 de agosto de 1994 en Monticello,
Illinois, USA. El planteamiento de esta conferencia resultó particularmente gra-
tificante, porque los trabajos no fueron expuestos directamente por sus autores,
sino por un grupo de revisores pre-seleccionados que en cada caso expusie-
ron sus gustos, quejas y sugerencias, fomentando así la participación entre los
asistentes.

• PLoP '95: La Segunda Conferencia Anual sobre Patrones se celebró del 6 al 8


de septiembre de 1995 en Monticello, Illinois , USA.

• PLoP ‘96: La Tercera Conferencia Anual sobre Patrones. Los interesados pue-
den contactar con los organizadores en plop95@parcplace.com, o dirigirse

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 111
bien Richard P. Gabriel en rpg@parcplace.com bien a Kent Beck28 en
70761.1216@compuserve.com.

• EuroPLoP ‘95 : La Conferencia Europea sobre Patrones (European Pattern


Languages of Programs), celebrada del 7 al 11 de agosto de 1.995 en Aarhus,
Dinamarca, en conjunción con ECOOP ‘95.

28
Kent Beck, que pertenece al Grupo de Patrones de Hillside, presentó en OOPSLA ‘89, junto
con Ward Cunningham, las fichas CRC, como el lector orientado-a-objetos posiblemente ya sa-
brá.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 112
ROLES Y OBJETOS EN C++
7
E
mpecemos con una pequeña disgresión linguística: el vocablo “rol” no exis-
te en castellano, pues habitualmente se utiliza “papel” en el sentido de “car-
go o función que uno desempeña en alguna situación o en la vida (RAE)”.
Lamentándolo mucho <grin> tal término “papel” parece referirse más bien a per-
sonas, y además, al poseer un signicado contextual, casi siempre tendríamos que
utilizar la frase “papel desempeñado/interpretado/jugado/acometido por”, mien-
tras que el barbarismo “rol” (del inglés: role) nos permite expresar sin ambigüe-
dad situaciones como, por ejemplo, “el rol de médico” o “el rol de la impresora”,
tal y como hacen los psicólogos y los malos literatos. El barbarismo es, pues, más
directo y claro. Eugenio D’Ors decía que el estilo, como las uñas, es más fácil
tenerlo brillante que limpio, pero en informática quizás haya de ser simplemente
eficaz. Algún lector se habrá preguntado, con todo, por qué enfatizo tanto las de-
cisiones lingüísticas. Pues porque resulta que el modelado software del mundo
real se basa en requerimientos, verbales o escritos, en los que los vocablos y la
sintaxis juegan un papel esencial. De hecho buena parte de los métodos de
OOA/OOD se basan en la bien-establecida técnica de Abbott que sustituye sus-
tantivos por clases y verbos o frases verbales por responsabilida-
des/relaciones/métodos/mensajes. Resulta al fin que, como afirma Nietzsche, es-
tamos “presos de las redes del lenguaje”, y los filólogos tienen un gran futuro en el
campo informático.

Sigamos con otra diminuta disgresión: la conceptualización y modelado de roles


han sido conveniente y abundantemente estudiados en el área de Inteligencia Ar-
tifical: marcos (frameworks), ranuras (slots), redes semánticas, etc. resultarán
familiares al lector avisado en esas lides (y si no qué mejor que ojear los textos de
Winston, Norvig y tantos otros). Pero aquí no tratamos de capturar la representa-
ción del conocimiento ni otro parecido elevado proposito, sino más bien mostrar
distintas técnicas arquitectónicas para solucionar en C++ la codificación de es-
cenarios en que los roles aparecen. Así que ... al tema.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 113
CLASES DE ROLES

Planteemos, sin más, un problema típico al que se enfrenta la mayoría de solucio-


nes software de gestión para entornos comerciales: un cliente, sea persona física
o jurídica (en su acepción más amplia, no necesariamente fiscal), puede a la vez
ser también proveedor, acreedor, comisionista, colaborador, socio, etc. Natural-
mente el problema es cómo modelar los roles -usualmente simultáneos- de tal
cliente, garantizando, a la vez, que se mantiene la integridad referencial en la por-
cion de cada rol que conforma el común subconjunto de datos intersección de
todos ellos (el nombre, el nif <sic>, etc.). La primera cuestión es: ¿constituye cada
rol una clase distinta? Shlaer y Mellor exponen que uno de los trucos (o técnicas)
para encontrar objetos en la fase de análisis consiste en buscar roles desempe-
ñados por personas u organizaciones (doctor, enfermera, paciente, agente, clien-
te, empleado, supervisor, propietario, etc.). Según esto los roles concretos son
claramente instancias de una clase representativa del rol, y así tendríamos las
clases Cliente, Proveedor, Acreedor y demás. Intentemos una primera codifica-
ción, en la que a cada rol se asocia una clase independiente:

class Cliente {
public:
String nombre();
void nombre( String* );
Date fechaDeAlta();
PlazoDePago plazoDeCobro();
// etc., etc.
private:
String* nombre_;
String* apellidos_;
Date* fechaAlta_;
PlazoDePago* plazoDeCobro_;
// etc., etc.
};

class Proveedor {
public: // un interfaz similar al de cliente
private:
String* nombre_;
String* apellidos_;
Date* fechaAlta_;
PlazoDePago* plazoDePago_;
// y sigue y sigue
};

Es fácil ver que el resultado es ingenuamente ineficaz: los roles comparten mucha
información que aquí simplemente se replica (con las dificultades de manteni-
miento que esto conlleva). Necesitamos, pues, que estas clases de “rol” posean
como porción común la representativa de la clase “Persona”. Y rápidamente aquí
el lector exclamará: “hay que derivar”, con la misma segura solemnidad con que

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 114
Luis XIV exclamara “¿Ha olvidado Dios todo lo que he hecho por Él?”. Pero no
tan deprisa. En realidad una de las causas del fracaso de primeros proyectos en
C++ es la sobredosis de herencia. Intentemos, no obstante, la sugerencia del lec-
tor, muy avisado ya en estas cuitas.

UNA BUENA HERENCIA ¿LO ARREGLA TODO?

¿Qué tipo de herencia necesitaríamos? ¿Privada, pública o protegida? Parece


claro que protegida no, desde luego. Entonces, ¿privada o pública? Y aquí, de
nuevo, el impenitente lector rápidamente exclamará: “Como se trata de una rela-
ción ES-UN, procede la derivación pública”. Bien, bien, bien. Pero, ¿se trata real-
mente de una relación de subtipación ES-UN? En fin, dejémonos llevar de nuevo
por el lector:

class Persona { /* clase base abstracta */ };


class PersonaFisica : public Persona {
public:
String nombre();
void nombre( String* );
// etc., etc.,
private:
String* nombre_;
String* apellidos_;
// etc., etc.
};

class Politico : public PersonaFisica {


public:
PartidoPolitico partido();
// se hereda el interfaz público de PersonaFisica
private:
PartidoPolitico* partido_;
};

Así las cosas, ¿un Político ES-UNA Persona? Bueno, esto significaría que allí
donde se espere una persona podría perfectamente aparecer un político. Y, claro,
aparte del lógico disgusto, esto es defendible -al menos jurídicamente. De esta
manera tendríamos que funciones con prototipo similares a

void encarcela( PersonaFisica* ) { /* código */ }


void soborna( Persona* ) { /* código */ }

felizmente admitirían sendos punteros a objetos de tipo Politico, clase derivada


públicamente de Persona y PersonaFisica. La derivación privada, al no permitir
en tal ámbito la conversión implícita de punteros de clases bases a clases deriva-
das, parece totalmente inapropiada para su aplicación a los roles.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 115
DINÁMICA DE CLASES

Bien, parece que la derivación puede funcionar y que los roles son objetos (por
clases) de pleno derecho, como establecen Shlaer y Mellor. Pero resulta que és-
tos afirman poco después que “este conjunto de objetos (los representativos de
roles) deberá contemplar el caso de una enfermera que momentáneamente es
paciente en un hospital”. O sea, que los roles, como evidencia la realidad, son
esencialmente dinámicos. Según Grady Booch, “el rol de un objeto denota la se-
lección de un conjunto de comportamientos que están bien-definidos en un punto
concreto en el tiempo”, siendo tal rol “la cara que un objeto presenta al mundo en
un momento dado”, de forma que “la mayoría de objetos interesantes interpretan
diferentes roles durante su vida”, como por ejemplo “en el curso de un día, la mis-
ma persona puede interpretar el rol de madre, doctor, jardinera y crítico de cine”.

El carácter dinámico de los roles no encaja, empero, con la disposición estática


de la herencia en C++. Esto es, en C++ un objeto no puede cambiar de clase, por
lo que si tenemos un objeto rol de tipo Medico que en un momento dado asume el
rol de Paciente, no podremos mutarlo de Medico a Paciente, sino que más bien
deberemos crear un nuevo objeto de tipo Paciente al que traspasar el núcleo de
PersonaFisica embebido en Medico. Necesitaríamos, pues, algo como lo si-
guiente:

class PersonaFisica : public Persona {


public:
PersonaFisica( PersonaFisica* pPF) {
nombre = pPF->nombre;
nacimiento = pPF->nacimiento;
// se asignan los demás datos miembros
}
PersonaFisica personaFisica() {
return PersonaFisica( this );
}
// sigue resto descripción de clase
};

class Medico : public PersonaFisica {


public:
Medico( const PersonaFisica& );
// etc., etc.
};

class Paciente : public PersonaFisica {


public:
Paciente( const PersonaFisica& );
// resto de descripción doliente
};

// aquí tenemos a un objeto de tipo Medico


Medico cardiologoJefe;

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 116
// ...
// poco después el médico enferma (el tabaco, ya saben)
Paciente paciente( cardiologoJefe.personaFisica() );

Este mecanismo un tanto soez permite replicar la porción de PersonaFisica en


cada uno de los objetos roles. Pero piénsese que no es la copia lo que necesita-
mos, sino que paciente y médico sean la misma persona. Y es que la misma rea-
lidad desvirtúa el esquema estático de derivación en C++, al menos en lo que a
roles se refiere. Imaginemos, por ejemplo, que tenemos una clase-rol para “Minis-
tro de Interior” y otra clase-rol para “Ministro de Justicia”, y ocasionalmente el pre-
sidente del gobierno decide unificar ambos ministerios. El nuevo rol resultante
“Ministro de Interior y Justicia”, ¿Es realmente nuevo? ¿Es una clase distinta?
¿Nos encontramos ante un caso de herencia múltiple? ¿Podemos usar las dos
clases anteriores simplemente replicando la porción correspondiente a la única
persona que los detenta? ¡No, no, no y por supuesto que no, respectivamente!

LA HERENCIA INNECESARIA

Si la herencia no nos soluciona el problema, ¿por qué no renunciar a ella? Exami-


nemos, pues, la opción más natural: si necesitamos una porción de Persona en
cada rol, ¿por qué no añadirla simplemente?:

class Medico {
public:
String nombre() {
return persona->nombre();
}
Medico( PersonaFisica* pPF ) {
persona_ = pPF;
// resto inicialización
}
PersonaFisica* persona() {
return persona_;
}
// resto interfaz público
private:
PersonaFisica* persona_;
EspecialidadMedica* especialidad_;
};

Bueno, esto ofrece un mejor aspecto. Pero, ¿no cumple la misma función la
herencia privada en C++, en el sentido de “insertar” en los objetos de clases deri-
vadas una porción “privada” de la clase base? En esencia sí, pero sin procurar-
nos una porción “separada” representativa de tal parte privada. Y es que la heren-
cia es como las armas, el ingenio o la cucharilla del postre: sólo hay que usarla si
es inevitable.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 117
Tenemos, pues, que la opción de capas expuesta (layering) permite, de forma
sencilla, que dos objetos-rol compartan al mismo individuo de fondo:

PersonaFisica* juanNadie = new PersonaFisica();


// aquí se inicializa debidamente el
// objeto juanNadie (apellidos, etc.)
Medico* jefeDePlanta = new Medico( juanNadie );
// se opera con el médico (o éste opera), y
// seguidamente, cuando éste ingrese como
// paciente, se construye tal objeto con la
// misma persona que asumía el rol de médico
Paciente tuberculoso1002( jefeDePlanta->persona() );

La diferencia respecto del anterior esquema derivativo es que aquí existe un obje-
to diferencial de tipo PersonaFisica al que pueden apuntar distintos roles. La per-
sona representada por “juanNadie” no abandona -al menos no necesariamente- el
rol de médico cuando asume el rol de paciente, a la vez que se preserva la noción
de identidad (una noción fundamental que repasaremos cuando examinemos las
Bases de Objetos) respecto del sujeto que desempeña el rol. Sin embargo aquí
se nos plantea un nuevo problema: ¿cuándo se destruye el objeto de tipo Persona
contenido en un objeto de tipo Medico? Tal y como lo hemos planteado, en el
destructor de la clase Medico podemos hacer dos cosas igualmente malas: bien
eliminar el objeto Persona, con lo que otros objetos, como por ejemplo de tipo
Paciente, que apunten hacia el mismo quedarían en estado indefinido; bien no
eliminar tal objeto, de tal forma que se irán consumiendo valiosos recursos de
memoria: en definitiva, dos vertientes del desastre. Necesitamos, pues, de la ma-
tización de una estructuración conocida como “Clase Carta de Singleton”, bien
explicitada en el texto de Coplien (y de forma sintetizada en el de Murray), y que,
en breve, se basa en que una clase, a modo de sobre o envoltura, sirve de inter-
faz respecto del mundo exterior de la carta (por clase) que contiene:

class PersonaFisicaRep {
friend class PersonaFisica;
private:
PersonaFisicaRep() {
// inicialización básica datos miembros
}
// sigue resto de constructores
String* nombre;
String* apellidos;
Date* fechaNacimiento;
int contador;
};

class PersonaFisica {
public:
PersonaFisica() {
// aquí se accede al constructor privado

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 118
// de la representación de PersonaFisica
rep = new PersonaFisicaRep();
rep->contador = 1
}
// sigue resto de constructores
PersonaFisica& operator=(
const PersonaFisica& pPersonaFisica ) {
// incrementamos el contador de objetos
// que apuntan a la Persona del argumento.
pPersonaFisica.rep->contador++;
// si la representación de la persona
// contenida en el presente objeto ya no
// es apuntada por nadie, se elimina.
// (sin contar el presente objeto, claro)
if ( --rep->contador <= 0 )
delete rep;
// se sustituye una representación por otra
rep = pPersonaFisica.rep;
return *this;
}
~Persona() {
// comprueba si existe más de un objeto que
// apunte a la representación interna de
// Persona. En otro caso lo elimina.
// (más de uno, porque al menos uno lo
// apunta: el presente objeto: *this)
if ( --rep->contador <= 0 )
delete rep;
}
String* getApellidos() {
return rep->apellidos;
}
// sigue resto interfaz público
private:
PersonaFisicaRep* rep;
};

De esta manera deberíamos cambiar el interfaz de nuestra clase Medico de la


forma:

class Medico {
public:
Medico( PersonaFisica* pPF ) {
// se cambia la asignación a objetos
// en lugar de la anterior a punteros,
// para que actúe el operador de asignación
// redefinido anteriormente.
*persona_ = *pPF;
// resto inicialización
}
// resto definición de la clase
};

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 119
Así que ¿ésta es la solución? ¡Claro que no! Bueno, esto funciona, pero ... es de-
masiado artificioso (respecto del problema que pretende resolver), pedestre e
inseguro: debemos definir en cada rol un método que devuelva la porción de
PersonaFisica (o PersonaJuridica, en su caso); se establece, además, un
contrato con los programadores (usar asignación con objetos, etc.) sin establecer,
no obstante, precondiciones y postcondiciones en otro lugar que no sea la
documentación. Y es que al renunciar a la herencia hemos renunciado a algo muy
importante: al grueso del polimorfismo en C++. Resulta que ahora no contamos,
entre otras cosas, con la conversión implícita de tipos, y que funciones como las
siguientes:
void tributaAHacienda( Persona* );
void aprendeDeAnterioresErrores( PersonaFisica* );

no podrán aplicarse a punteros a objetos de tipo Medico o Politico, pues tales no


derivan públicamente de Persona o de PersonaFisica. Por supuesto que una po-
sible solución (de carácter casi policial) sería

tributaAHacienda( politico->persona() );

Lo malo es que si hemos redefinido en la clase Politico alguna función ya esta-


blecida en la clase PersonaFisica, tal nueva funcionalidad no se aplicará en este
caso, pues en el cuerpo de la función trabajaremos no con un objeto Politico ac-
cedido mediante un puntero a su porción de PersonaFisica, sino con un objeto de
tipo PersonaFisica que no sabe nada de Políticos o Médicos. Y es que las fun-
ciones virtuales (lo que aquí necesitamos) sólo funcionan, por su propia naturale-
za, en esquemas derivativos. Así que, ¡señor notario, dé marcha atrás!

DIVIDE Y HEREDARÁS

Ya que ni la simple herencia ni la composición por capas nos solucionan satisfac-


toriamente el problema de los roles, ¿por qué no usar ambos esquemas en una
mixtificación que reúna lo mejor de cada uno? Así, en primer lugar, tendremos una
jerarquía de clases significativas de las entidades que pueden asumir roles, que
por el momento simplificaremos a las siguientes, en clara relación de herencia:

class Sujeto (abstracta)


class Persona (abstracta)
class PersonaFisica
class PersonaJuridica

Seguidamente necesitamos modelar una estructuración de clases representati-


vas de los roles de tal forma que no haya que repetir para cada clase la gestión

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 120
del Sujeto que cada una contenga. Pero los sujetos pueden pertenecer a distintas
clases, por lo que parece adecuada una jerarquía como la que sigue (y aquí tam-
bién abreviaremos la descomposición):

class Rol (abstracta)


class RolDePersona (abstracta)
class Politico
class Cliente
class RolDePersonaFisica (abstracta)
class Medico
class Paciente
class Empleado
class AmoDeCasa

Y ahora intentaremos aplicar todo lo anterior: la clase Rol contendrá un puntero a


un objeto de tipo Sujeto, que será

class Rol {
public:
Rol( Sujeto* pSujeto ) {
*sujeto = *pSujeto;
}
virtual Sujeto* getSujeto() {
return sujeto;
}
private:
Sujeto* sujeto;
};

class RolDePersona {
public:
RolDePersona( Persona* pPersona ) :
Rol( pPersona ) {}
// Atención: la próxima función es virtual,
// porque aunque el tipo de retorno es distinto
// del de la clase Rol, es un puntero a un tipo
// que deriva públicamente de aquél.
Persona* getSujeto() {
return (Persona*)sujeto;
}
// resto definición de clase
};

class RolDePersonaFisica {
public:
RolDePersonaFisica( PersonaFisica* pf ) :
RolDePersona( pf ) {}
PersonaFisica* getSujeto() {
return (PersonaFisica*)sujeto;
}
// resto descripción protocolo de clase

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 121
};

class Empleado {
public:
Empleado( PersonaFisica* pf ) :
RolDePersonaFisica( pf ) {}
Salario* getSalario();
private:
Salario* salario;
Date* fechaContratacion;
// resto definición de clase;
};

Como vemos los roles concretos únicamente deben preocuparse de incluir códi-
go en su constructor, mientras que la asignación del sujeto, y por tanto el nudo de
los problemas, se circunscribe a la clase base Rol. Necesitamos, por fin, una je-
rarquía de representaciones internas de los sujetos (las cartas de los sobres de
Singleton, que son las clases derivadas de Sujeto), y cuya definición se obviará
por ser idéntica a la anteriormente expuesta:

class PersonaFisicaRep;
class PersonaJuridicaRep;
// etc., etc.

Bien, esto parece funcionar, así que es momento de que el lector se cuestione:
“¿Cuál es el fallo? Porque cuando el autor afirma que “parece funcionar” es segu-
ro que algo falla”. Bueno, bueno, ya nos vamos conociendo y se aprecian ciertos
leitmotivs wagnerianos. Y sí, algunas cosas no encajan bien.

Lo que de todo lo expuesto en primer lugar se infiere es que, como ahora parece
evidente, un rol NO-ES una persona, ni un tipo de persona, aunque, por ejemplo,
Politico es claramente un tipo de Rol. La separación de jerarquías aparece, pues,
inapelable. ¿Qué pasa, sin embargo, con la interoperabilidad en el mundo real
respecto de los roles y de quienes los asumen por la que asimilamos, verbigracia,
un médico a la persona que ejerce de médico? ¿Qué ocurre, por otro lado, con el
cambio dinámico de roles? Piénsese, por ejemplo, que tenemos la posibilidad de
cambiar el objeto, de tipo Sujeto o derivado, que ostenta el rol mediante una fun-
ción del tipo:

void Rol::cambiaSujeto( Sujeto* pSujeto )


{
*sujeto = *pSujeto;
}

que será heredada por todos los roles concretos. Pero, ¿realmente sirve de algo
esta posibilidad de cambio? ¿Puede un rol cambiar de persona, o es más bien la

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 122
persona la que cambia de rol? Parece que, por un lado, existen ciertas capacida-
des básicas o comunes asociadas a determinados roles, pero es claro que es el
sujeto el que cambia, o más bien simultanea, roles. Por otro lado, ¿no les resulta
molestamente repetitivo el mimetismo de la jerarquía de roles respecto de la suje-
tos? ¿Por qué reflejar en aquélla la estructuración de esta última? ¿Qué ocurre,
por otro lado, con el fin del rol? ¿Se deberá añadir a los objetos de tipo Rol un
atributo de término del Rol que será establecido justo antes de eliminar el objeto?
Quizás todo este asunto necesite un re-enfoque, ¿y quién mejor que los expertos
para asesorarnos? Leamos el oráculo.

EL PATRON DE ROLES INTERCAMBIABLES

¡Oh, oh! ¡Patrones! Bueno, no podía ser de otra manera, pues lo que se pretende
es la transmisión formal de la experiencia. El problema de los roles no es nuevo y
parece lógico pensar que existan distintas sintetizaciones del mismo. Como es
costumbre, empezaremos por la más débil: Peter Coad establece que el proble-
ma del rol se enmarca en un patrón estructural genérico que denomina “Patrón de
Roles Intercambiables” (Changeable Roles Pattern) y que en definitiva consiste en
que existe un objeto que actúa de Intérprete del rol y que se relaciona con un solo
objeto a la vez de una clase derivada de Rol, que a su vez es una clase abstracta
base de una jerarquía similar a la expuesta. Coad establece la siguiente configu-
ración mínima:

class Interprete {
private:
Rol* rol;
// ...
};

class Rol {
private:
Time* momentoDeComienzo;
Time* momentoDeFin;
// ...
};

De la clase Rol derivarían clases representando roles específicos (Medico, Politi-


co, etc.). Como vemos, los papeles han cambiado: ahora se operará únicamente
con intérpretes (por sujetos), y éstos contendrán la información sobre qué rol asu-
men en cada momento. Este enfoque, demasiado simplista y general (Could
Coad code?), presenta la ventaja de su efectividad comunicativa y dos evidentes
dificultades: por un lado impide la concurrencia temporal de roles (¿No puede un
Cliente ejercer de tal aun estando calificado como Proveedor?); por otro deja al
criterio del lector la implementación de la funcionalidad de los roles respecto del
intérprete, pues es al intérprete al que se dirigirán los mensajes relativos a los

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 123
roles. Respecto del primer problema (la concurrencia) el lector me permitirá una
elegante elipsis hasta solucionar en alguna medida el segundo. Bien: este patrón
nos ha dejado insatisfechos, así que, ¿por qué no más de lo mismo?

EL PATRON DE ESTADOS

Pree, tras constatar la inoperante genericidad del patrón de Coad, afirma que el
problema de la implementación de la relación entre Intérprete (o Jugador) y Rol se
soluciona con otro patrón: el denominado “Patrón de Estados (State Pattern)”,
que se detalla en el libro GOF de Gamma y otros. El cometido básico de tal pa-
trón es permitir a un objeto modificar su comportamiento cuando quiera que su
estado interno cambie, apareciendo como que el objeto cambia de clase. Su im-
plementación en C++ es, básicamente, la siguiente: en vez de que una clase dis-
ponga de datos miembros específicos cuyos valores en distintos objetos reflejen
sus distintos estados y afecten, así, al comportamiento de sus funciones miem-
bros, tales datos miembros y funcionalidad asociada son extraídos y encapsula-
dos en objetos aparte, de forma que el objeto original mantiene un puntero a los
mismos y posee métodos para cambiar tal apuntamiento de un objeto representa-
tivo, por ejemplo, del “estado de recepción” a otro valedor de “estado de envío”.
Si sustituimos en el patrón original los nombres “Contexto” y “Estado” por los ya
usados “Sujeto” y “Rol” tendremos:

class Sujeto {
public:
setRol( Rol* unRol ) {
rol = unRol
}
unMensajeCualquiera() {
rol->maneja( this );
}
private:
Rol* rol;
};

class Rol {
public:
virtual Rol* maneja( Sujeto*);
};

class RolConcreto : public Rol {


public:
virtual RolConcreto* maneja( Sujeto* );
};

de manera que los mensajes (relativos al rol) dirigidos a un Sujeto serían pasa-
dos, como una llamada a una función virtual, al rol concreto contenido en cada
caso en el objeto Sujeto. Bien: esto es mejor que lo anterior pero, a mi juicio, Pree

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 124
peca de ingenuidad (lo cual es lógico, dado el carácter anecdótico con que trata
este tema, pues su libro está enfocado hacia Metapatrones y Hot-Spots). La apli-
cación de este patrón no nos soluciona bien el problema planteado: piénsese
que, en primer lugar, todas las funciones de todas las clases derivadas de Rol
deberían declararse como virtuales en la clase base Rol, y además luego deberí-
an definirse en la clase Sujeto para permitir el redireccionamiento previsto. Así,
cada vez que añadiéramos un nuevo rol deberíamos modificar la clase Rol y la
clase Sujeto, y ¡recompilarlo todo! Esto, naturalmente, no encaja agradablemente
con la modularidad propugnada por la Tecnología de Objetos. ¿Es necesario,
pues, cambiar de patrón? ¡No! Yo más bien diría que hay que añadir algún patrón
más.

EL PATRÓN PROXY DE SUBROGACIÓN

Este patrón establece cómo controlar el acceso a un objeto desde otro distinto.
Exactamente -es un decir- lo que queremos, pues se pretende controlar el acceso
a la funcionalidad embebida en los roles a través de objetos de tipo Sujeto. Ne-
cesitamos, pues, llamar en objetos de la clase Sujeto a funciones que no pertene-
cen a esta clase y sí a cualquiera derivada de Rol, pero no queremos replicar este
conjunto de funciones en Sujeto. ¿Cómo solapar, pues, el chequeo estático del
compilador relativo a tales funciones? Pues con la sobrecarga del operador pun-
tero. Apliquemos, sin más, el patrón al código:

class Sujeto {
public:
setRol( Rol* unRol ) {
rol = unRol;
}
Rol* operator->() {
return rol;
}
private:
Rol* rol;
};

class Persona : public Sujeto { /* ... */ };


class PersonaFisica : public Persona { /* ... */ };

class Rol {
public:
virtual void noPiensoDimitir();
};

class Politico : public Rol {


public:
void noPiensoDimitir() {
cout << “Lean mis labios: ”;
cout << “N-O--P-I-E-N-S-O”;

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 125
};

De esta manera ahora podríamos codificar lo siguiente:

PersonaFisica unaPersona( /* inicialización */ );


Politico* politico =
new Politico( /* inicialización */ );
unaPersona.setRol( politico );
unaPersona->noPiensoDimitir();

Examinemos con más detenimiento la última línea: se le manda el mensaje “->” al


objeto unaPersona, de la clase PersonaFisica, y como en la clase Sujeto, base
de aquélla, se define una sobrecarga del operador puntero, ésta se aplica, y por
tanto el método asociado responde devolviendo un puntero al objeto rol contenido
en unaPersona (exactamente contenido en la porción de Sujeto embebida en
unaPersona). El puntero que se devuelve apunta a un objeto de tipo Politico, pero
como el operador -> devuelve un puntero a Rol, se produce una conversión implí-
cita de Politico* a Rol*, merced a la derivación pública de la primera clase res-
pecto de la última. Sobre este puntero se aplica ahora el operador puntero global,
para que por su mediación se llame a la función “noPiensoDimitir()”. Tal función
es virtual por lo que, aunque se la llame a través de un puntero a la clase base
Rol, se ejecutará el cuerpo de la función correspondiente a la clase del objeto,
ésta es, Politico.

Bien, esto funciona y parece suficientemente elegante, pero, como siempre, hay
que explicitar algunas consideraciones, matizar algunos comportamientos y co-
rregir ciertas inconveniencias.

CONVERSIONES E INDIRECCIÓN

Ahora tenemos dos distintas jerarquías: la de Sujetos o Intérpretes y la de Roles,


pero es habitual que, en el mundo real, donde se espera la actuación en base a
un rol se admita a una persona para acometer tal acción. O sea, necesitamos que
una función que espere un Rol admita también un Sujeto. Debemos, pues, definir
los operadores de conversión:

class Sujeto {
public:
operator Rol*() {
return rol;
}
operator Rol&() {
return *rol;
}
private:
Rol* rol;

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 126
};

de manera que se puede codificar lo siguiente:

void sobornaA( Politico* unPolitico ) {}


void encarcelaA( Politico& unPolitico ) {}
PersonaFisica unaPersona;
Politico politico* = new Politico;
unaPersona.setRol( politico );
sobornaA( unaPersona );
encarcelaA( unaPersona );

Así, en las dos últimas líneas se produce, mediante la aplicación de los operado-
res definidos, una conversión del objeto Persona en la parte de Rol que contiene:
en la última línea en forma de objeto, y en la penúltima en forma de puntero a tal
objeto. Naturalmente se podría objetar que lo que se necesita es precisamente lo
contrario: que funciones que esperan objetos de tipo Sujeto admitan objetos de
tipo Rol o derivados. Pero no, realmente no encaja bien este esquema cuando
hemos estimado que el rol carece de sentido autónomo funcional (aunque no de
comportamiento definido) sin el sujeto que lo desempeña.

Fíjese el lector, con todo, que la sintaxis se va complicando: a la función que es-
pera un puntero a Politico se le pasa ... un objeto de tipo Persona. Pero es que si
el lector retrocede un poco más verá también como el operador puntero se aplica
... ¡a objetos! Esto conlleva algunas consecuencias:

PersonaFisica* punteroAPersona = new PersonaFisica;


PersonaFisica objetoPersona = *punteroAPersona;
punteroAPersona->setRol( politico ); // OK
objetoPersona->noPiensoDimitir(); // OK
punteroAPersona->noPiensoDimitir(); // ERROR

pues, como el hábil lector ya habrá adivinado, la última línea usa del operador ->
predefinido y, por tanto, no se produce redirección alguna, y dado que la función
que se llama no pertenece a la clase PersonaFisica (ni a la sección pública de
sus clases base), el compilador justamente se queja. Otra consecuencia es que el
mecanismo virtual no funciona directamente mediante el operador -> sobrecarga-
do, pues aquél sólo funciona sobre punteros y referencias, no sobre objetos.

Hemos solucionado, con el patrón de subrogación, una parte del problema de


replicación del interfaz, pero no completamente. Seguimos teniendo que añadir
todas las funciones de las clases derivadas en la clase base Rol y recompilar ca-
da vez. ¿Alguna solución? Bueno, podríamos añadir una capa más de indirec-
ción:

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 127
class Sujeto {
public:
Sujeto() {
controlRol = new ControlRol( this );
}
~Sujeto() {
delete controlRol;
}
ControlRol* operator->() {
return controlRol;
}
Rol* getRol() {
return rol;
}
private:
Rol* rol;
ControlRol* controlRol;
};

class ControlRol {
public:
ControlRol( Sujeto* unSujeto ) {
setSujeto( unSujeto );
}
void setSujeto( Sujeto* unSujeto ) {
sujeto = unSujeto;
}
// aquí seguiría el interfaz que antes
// debía replicarse en la clase base Rol:
// cada función se reencamina hacia la
// clase adecuada mediante casts.
void noPiensoDimitir() {
((Politico*)(sujeto->getRol()))
->noPiensoDimitir();
}
private:
Sujeto* sujeto;
};

De esta manera las operaciones dirigidas a objetos de tipo Sujeto mediante el


operador puntero serían redirigidas al objeto de tipo ControlRol que tal contiene, y
desde aquí serían redirigidas, a su vez, hacia la clase concreta, sobrepasando el
mecanismo virtual. ¿Cuál es la ventaja respecto del anterior esquema? Pues que
cada vez que añadamos un nuevo rol únicamente habremos de modificar esta
clase externa, sin tocar ni roles ni sujetos. Parece, así, que el mal se convierte en
mal menor: nos evitamos las recompilaciones, cuando menos.

Richard Helm me ha confirmado que en el GOF discutieron repetidamente el Pa-


trón de Roles (Roles Pattern), pero que al final se decidió no incluirlo en su libro
catálogo, quizás por no disponer en el momento de su edición de una solución

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 128
que contemplara la intensa casuística a modelar. Afortunadamente parece que
Erich Gamma y Richard Helm han alumbrado un nuevo patrón (ExtensionObjects),
que se publicará próximamente en la columna bimensual que ambos escriben en
el Dr. Dobbs Journal Windows Sourcebook, y que serviría de base al esperado
Patrón de Roles. La idea es aportar una clase con un interfaz que permita pregun-
tar a un determinado objeto si soporta o asume un determinado interfaz o rol. El
resultado de tal consulta sería un objeto con la funcionalidad del rol buscado, que
naturalmente permitiría interactuar con el objeto inicial a través de su interfaz. Pa-
rece que tal nuevo objeto sería una suerte de Adaptador (véase el Patrón del
Adaptador en el libro GOF), y permitiría a sus clientes utilizar un determinado in-
terfaz para acceder a clientes solamente si pueden asumir un rol dado. Según
Helm, “lo magnifico de este patrón es que permitiría definir dinámicamente nue-
vos interfaces de clases sin compilarlos en la declaración de tales clases, evitan-
do así que las clases tuvieran interfaces hiper-hinchados para reflejar todos sus
posibles usos por clientes”. La idea parece interesantemente afortunada, y aun-
que aparenta tener puntos en común con la anteriormente expuesta, habrá que
esperar un poco más para discutirla.

CONCURRENCIA Y SECUENCIACIÓN DE ROLES

Todo lo anterior está relativamente bien, pero obvia una cuestión esencial: un
mismo sujeto, en un período de tiempo dado, suele asumir distintos roles concu-
rrentemente. El tratamiento secuencial que hasta ahora le hemos dado a la asun-
ción de roles presenta, con todo, serios inconvenientes: ¿Qué ocurre cuando sus-
tituimos en un sujeto un rol por otro? ¿Se crea cada vez un nuevo rol y se destruye
el sustituido? ¿Dónde queda la memoria de lo asumido? Naturalmente aquí no
funciona el esquema de Singleton, pues no se intenta compartir un mismo objeto,
sino más bien mantener “vivos” unos objetos de tipo rol. Es claro que si el objeto
de tipo Sujeto se destruye (ojo: no es lo mismo que un sujeto muera -esto es, que
definitivamente cambie de estado- que el objeto que lo representa sea destruido)
deberán desaparecer con el todas las referencias que a él apunten, junto con to-
dos los objetos que con exclusiva dependencia maneje. Bien: intentaremos solu-
cionar a la vez los problemas planteados por la secuencialidad y la concurrencia
de roles.

En lugar de un puntero a un único objeto de tipo Rol, necesitamos de una colec-


ción de punteros, así que recapitulemos:

class Rol {
public:
Bool activo();
Bool finalizado();
private:
Time* momentoDeComienzo;
Time* momentoDeFin;

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 129
// ...
};

class Sujeto {
public:
Sujeto();
~Sujeto();
ControlRol* operator->();
void setRol();
Rol* getRol();
operator Rol&();
operator Rol*();
private:
Rol* rol; // el rol actual
ColeccionDeRoles* roles;
ControlRol* controlRol;
};

Simplificando la realidad, aceptaremos que un sujeto puede mantener varios ro-


les concurrentemente, pero todos ellos permanecerían latentes menos uno, el rol
actual, sobre el que se actuaría en cada momento. Intentemos, pues, codificar la
memoria de roles:

void Sujeto::setRol( unRol ) {


if ( Rol* rolTemp =
roles->contieneMismoTipo( unRol ) )
if ( rolTemp->activo() ) {
rol = rolTemp;
return;
}
roles->inserta( rol = unRol );
}

Así distinguiremos entre sustituir un rol y finalizarlo, sirviendo el parámetro de se-


tRol(...) únicamente para establecer el tipo (mediante typeid) del rol que se pre-
tende activo. Naturalmente esta codificación no es, de lejos, la más eficiente o
elegante, pero resulta convenientemente didáctica y estratégicamente breve, so-
bre todo teniendo en cuenta que, dada la leve insatisfacción que la solución dada
ha generado, en seguida vamos a adoptar un enfoque radicalmente distinto.

EN LA ASOCIACIÓN ESTÁ LA FUERZA

Si, desde una óptica sindical, decidimos que hay escasos patrones que realmen-
te nos sirvan de ayuda, y que necesitamos nuevas luces, ¿qué mejor que la
asociación? Atendamos, de nuevo, a los expertos: Para Rumbaugh un rol es “un
nodo de una asociación”, puesto que “una asociación binaria tiene dos roles,
cada uno de los cuales puede tener un nombre de rol”. Esto resulta
significativamente importante. Hasta ahora hemos considerado que un Rol es un

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 130
portante. Hasta ahora hemos considerado que un Rol es un objeto de pleno dere-
cho, pero ... ¿y si no lo fuera? Consideremos un array o vector de objetos (de
cualquier tipo): el primer objeto es, en realidad, un objeto que juega el rol de “pri-
mero”; naturalmente tal rol es intercambiable mediante operaciones en el objeto
continente, pero ... ¿a alguien se le ocurriría modelar tal rol como una clase “Pri-
mero”? Bueno, seguro que a alguien, pero ¿es razonable tal modelado? En gene-
ral parece que no. El rol se transforma aquí en un mero vocablo que designa un
nodo direccional dentro de una relación. Pero leamos a más autoridades. Para
Embley, por ejemplo, un rol “es un atajo para expresar especialización”. De esta
manera un rol, al nominar una conexión entre objetos, es “una especialización de
la clase del objeto en la conexión cuyos objetos totalmente participan en el conjun-
to de relaciones en la conexión”, queriendo así expresar que la única diferencia
entre una especialización de rol y una generalización/especialización es el carác-
ter mandatorio de aquélla primera respecto de las clases que participan en la re-
lación: o sea, un Empleado de un hospital no tiene por qué tener pacientes asig-
nados, pero un médico siempre tendrá al menos uno: el conjunto de los emplea-
dos de hospital que poseen pacientes es el que determina la relación de rol. Pero
curiosamente Embley parte de un “Empleado” como clase, quizá derivada de
Persona, y según lo expuesto parece que “Empleado” es un rol desempeñado por
una persona en relación con una empresa: en realidad se trata de la cualificación
de uno de los nodos de una “Relación Laboral”. Y ... es hora de tomar un pequeño
respiro, que la cosa parece complicarse. Porque ¿realmente un rol no es un obje-
to? Examinemos esta afirmación con más detenimiento, usando a tal fin una Re-
lación Laboral. Este tipo de relación se establece entre una Persona y una Perso-
naFisica, de forma que la relación se definiría como (y obviaremos de momento
las cuestiones de jerarquía de relaciones):

class RelacionLaboral {
private:
Persona* empleador;
PersonaFisica* empleado;
long salarioAnual;
Date* fechaDeIncorporación;
Date* fechaDeCese;
RangoTemporal* horarioDeTrabajo;
// etc., etc.
};

Toda la información relativa a la relación queda así establecida en virtud de atri-


butos de la relación misma. Pero, ¿qué pasa con las Personas que respecto de
esta relación asumen roles bien determinados: empleado y empleador? ¿No po-
seen ahora un comportamiento adicional al que tenían como meras PersonaFisi-
ca y Persona, respectivamente, que no se modela en la propia relación? ¿No tie-
ne el empleado, por ejemplo, determinados derechos y obligaciones? Bueno,
tenemos dos opciones: bien significamos estos comportamientos en la propia
relación, con apropiados parámetros, bien aprovechamos todo lo expuesto y

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 131
añadimos sendos objetos de tipo Rol a la relación. Optaremos -razones de eco-
nomía tipográfica- por el segundo caso, así que roles siguen siendo objetos.
Veámoslo:

class RelacionLaboral {
private:
Persona* empleador;
Empleado* rolEmpleador;
PersonaFisica* empleado;
Empleador* rolEmpleado;
// resto protocolo clase
};

Pero, ¿por qué no encapsular los objetos de tipo Rol en los sujetos, como se ex-
puso anteriormente, en lugar de añadirlos a la clase Relacion? Pues porque de tal
manera los roles quedarían sin conexión con la relación que los genera, de mane-
ra que encapsulándolos en la relación podemos tener, verbigracia, tres roles de
tipo Empleado bien diferenciados. Como quiera, por otro lado, que todas las re-
laciones cualifican a los sujetos relacionados en virtud de roles direccionales,
necesitamos extender la asunción de roles a más entidades, para lo cual
debemos ampliar la jerarquía de intérpretes, que ahora pasarán a ser Entes,
resultando algo singularmente más extenso que lo siguiente:

Ente
Bien
BienInmueble
Finca
BienMueble
Arma
Prenda
Bolso
Cartera
Gafas
Joya
Vehiculo
Bicicleta
Ciclomotor
Motocicleta
Turismo
Persona
PersonaFisica
PersonaJuridica
AsociacionDeVecinos
SeccionOrganizativa
Ubicacion
ComunidadAutonoma
Localizacion
Provincia
Pais

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 132
Poblacion
Via
Calle
Carretera
Cruce

Bajo esta nueva situación se puede fácilmente hablar, por ejemplo, del rol de pro-
pietario que asume una sociedad limitada respecto de un vehículo, que a su vez
asume el rol de poseído, ambos roles siempre respecto de una relación binaria
de pertenencia. Pero sigamos: dado que todas las relaciones binarias encierran
entes asumiendo roles, ¿por qué no encapulsar tal comportamiento en una clase
base? Y de paso, ¿por qué no tipificar las relaciones entre distintos tipos de en-
tes? Intentémoslo:

class TipoDeRelacionEntreEntes {
public:
TipoDeRelacionEntreEntes( const Ente&,
const Ente&, Rol* = 0, Rol* = 0 );
~TipoDeRelacionEntreEntes();
private:
// los entes se tratan como referencias para
// obligar a su inicialización en el momento
// de la construcción de los objetos, y forzar
// la inmutabilidad de los tipos tras esto.
// El ente en sí no importa: sólo su tipo.
Ente& origen; // nodo inicial
Ente& destino; // nodo final
// nombre de la relación (v.b. "Propiedad")
String* nombreGenerico;
// relación del origen respecto del destino
// (v.b. "Posee" ó “es propietario de”)
String* nombreRelacion;
// relación del destino respecto del origen
// (v.b. "es poseído por" ó “es propiedad de”)
String* nombreRelacionInversa;
// objetos roles asumidos por los entes
Rol* rolOrigen;
Rol* rolDestino;
// etc., etc.
};

class RelacionEntreEntes {
public:
RelacionEntreEntes();
RelacionEntreEntes( Ente*, Ente*,
TipoDeRelacionEntreEntes* = 0 );
~RelacionEntreEntes();
// función de modificación
void set( Ente*, Ente*,
TipoDeRelacionEntreEntes* );
// funciones de acceso

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 133
Ente* getOrigen();
Ente* getDestino();
TipoDeRelacionEntreEntes* getTipoRelacion();
private:
Ente* origen;
Ente* destino;
TipoDeRelacionEntreEntes* tipoRelacion;
// etc., etc.
};

De esta manera contamos con la posibilidad de trabajar bien con meras asocia-
ciones nominadas bien con relaciones especializadas resultantes de aplicar la
herencia a aquéllas. Naturalmente sobre lo anterior cabría aplicar sendos interfa-
ces gráficos: En primer lugar deberíamos ocuparnos de una colección que man-
tenga de forma dinámica los tipos de relación activos en cada momento aplica-
bles a los Entes. Hay que tener en cuenta que los tipos de Entes en tales relacio-
nes representan la clase base a que son aplicables (la mínima base común), de
forma que la búsqueda de relaciones será fundamentalmen-te polimórfica. Segui-
damente el interfaz básico de los tipos admitidos para una determinada relación.
Lo que estamos haciendo es, en definitiva, aislar de la clase Relacion y de sus
derivadas el chequeo de tipos exactos de Entes aplicables para la asunción de
roles. Tal flexibilidad nos permitiría mdelar con facilidad, incluso gráficamente,
prolijas relaciones de roles entre objetos, como por ejemplo las siguientes:

FIGURA RELACIONES DINÁMICAS

Naturalmente estamos planteando una asunción dinámica de roles susceptible de


generar consultas también dinámicas. Como vemos ahora el objeto Rol está con-
tenido en objetos TipoDeRelacionEntreEntes, que a su vez están contenidos en
objetos de tipo RelacionEntreEntes o derivados, que a su vez contiene objetos de
tipo Ente. Todo el esquema singular anterior (objetos roles contenidos en objetos
de tipo Sujeto) deberá ser convenientemente modificado respecto de la clase
Ente: los operadores de conversión habrán de ser dotados de mecanismos de
resolución (se podrían aplicar distintos roles), los redireccionamientos (mediante
el operador ->) deberán buscar primero una relación adecuada para después
devolver el rol correcto, etc., etc. Pero esto se deja como ejercicio al inteligente
lector (le doy mi palabra que no aparece ningún concepto realmente nuevo o no
tratado en este capítulo ... bueno, al menos en una versión elemental).

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 134
LA CITA DEL DESASOSIEGO

Terminemos con una interesante puntualización de autor reconocido (y que nos


perdone Don Fernando): En buena parte de la literatura sobre Inteligencia Artifi-
cial se nota que los roles temáticos especifican la relación de un objeto con una
determinada acción, significada por un verbo o frase verbal, con los consiguientes
problemas de ambigüedad contextual que esto supone. Jacobson aboga porque
se embeba completamente la acción en un objeto rol explícito, así que propone
que se denomine a las relaciones que determinan un rol por un sustantivo mejor
que por un verbo: esto es, resultaría más adecuado decir “El objeto Persona in-
terpreta el rol de un Conductor respecto del Coche” que “Un Coche es conducido
por una persona” o “Una Persona conduce un Coche”, y basa su criterio en que un
nombre expresa mejor la unidireccionalidad de las asociaciones: parece claro
que en la relación “matrimonio” o “estar-casado-con”, el marido conoce a la espo-
sa y viceversa, pero en la relación, por ejemplo, “ser-rey-de”, el súbdito conoce al
soberano, pero no necesariamente viceversa, por lo que Jacobson concluye que
la mayoría de relaciones en el mundo real son unidireccionales y no bidirecciona-
les, como implícitamente sugieren las formas verbales “conduce” o “es-
conducido”, claramente reversibles. Quizás este criterio supone una esquemati-
zación extrema, pero la idea que en él subyace es la de la visibilidad inter-roles,
algo que el método OORASS trata con particular detalle.

REFERENCIAS DIRECTAS

Aparte de los títulos que seguidamente se detallan (por orden de aparición en el


capítulo) el lector inquieto puede estudiar el método originariamente denominado
OORASS (Object-Oriented Rôles Analysis, Synthesis and Structuring), desarrolla-
do por un equipo liderado por Trygve Reenskaug para Taskon, y que actualmente
se denomina OORAM. Este método, partiendo de un enfoque ciertamente origi-
nal, busca áreas de actuación (concern areas) para modelarlas usando agentes y
objetos que pueden asumir distintos roles, en la misma posición estructural, res-
pecto de distintos contextos. A los Roles, como objetos de primera categoría, les
son aplicables requerimientos, operaciones y competencias, y además tienen
relaciones con otros Roles, generalmente unidireccionales (un rol conoce de otro,
pero éste no conoce de aquél). En fin, demasiado para tan pocas líneas. Lea,
lector: lea.
• Program Design by Informal English Descriptions, R.J. Abbott, 1983,
Communications of the ACM, 26(11), 882-94.
• Object-Oriented Systems Analysis: Modeling the World in Data, Sally Shlaer
& Stephen Mellor, 1988, Prentice Hall, 0-13-629023-X.
• Object-Oriented Analysis and Design with Applications, 2nd Edition, Grady
Booch, 1994, Benjamin/Cummings, 0-8053-5340-2.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 135
• Advanced C++ Programming Styles and Idioms, James O. Coplien, 1992,
Addison-Wesley, 0-201-54855-0.
• Object-Oriented Patterns, Peter Coad, 1992, Communications of the ACM,
33(9).
• C++ Strategies and Tactics, Robert B. Murray, 1993, Addison-Wesley, 0-201-
56382-7.
• Design Patterns for Object-Oriented Software Development, Wolfgang Pree,
1995, Addison-Wesley, 0-201-42294-8.
• Design Patterns: Elements of Reusable Object-Oriented Software, Erich
Gamma, Richard Helm, Ralph Johnson y John Vlissides, 1995, Addison-
Wesley, 0-201-63361-2.
• Object-Oriented Modeling and Design, James Rumbaugh, Michael Blaha, Wi-
lliam Premerlani, Frederick Eddy & William Lorensen, 1991, Prentice Hall, 0-
13-629841-9.
• Object-Oriented Systems Analysis: A Model-Driven Approach, David W. Em-
bley, Barry D. Kurtz & Scott N. Woodfield, 1992, Prentice Hall, 0-13-629973-3.
• Patterns discussion mailing lists, GOF y otros, 1995.
• Asistencia Técnica para la Mecanización de la Policía Local de Logroño, Ri-
cardo Devis, 1994, Infoplus SL.
• Object-Oriented Software Engineering: A Use Case Driven Approach, Ivar
Jacobson, Magnus Christeron, Patrik Jonsson & Gunnar Övergaard, 1992,
Addison-Wesley, 0-201-54435-0.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 136
BASES DE DATOS ORIENTADAS-A-
8
OBJETOS

E
n alguno de los cursos de Tecnología de Objetos que imparto muestro una
transparencia con el título “Diseño de Bases de Datos” para seguidamente,
tras una estudiada pausa, superponer otra con una señal de prohibición, a
la vez que afirmo categórico: “No hay diseño sustantivo de bases de datos”. Y
punto: nada que añadir. Naturalmente el buscado efectismo intenta comunicar que
si en Tecnología de Objetos el abismo (gap) semántico entre Análisis y Diseño se
difumina, tal solapamiento debiera ampliarse a las etapas de codificación y al-
macenamiento. O sea, las entidades que se encuentran en la fase de análisis de
requerimientos se corresponderán biunívocamente con clases/objetos en las de
análisis y diseño, para después mantener su integridad modular y conceptual tan-
to en la implementación como en el esquema de persistencia. Para abreviar: el
objeto finalmente almacenado en la Base de Datos se corresponderá estructu-
ralmente con aquél encontrado en el dominio del problema, significando así que
queda sin sentido el trabajo de descomposición en estructuras de datos asumi-
bles por gestores concretos que luego habrán de ser nuevamente compuestas
para su uso efectivo. La cuestión es ¿desmontaría usted cada noche su coche en
piezas para reconstruirlo en la mañana siguiente, cuando desee usarlo? Eviden-
temente no 29, al menos directamente. El enfoque más elegante sería dejar que un
guardacoches responsable se ocupara del auto, sin necesaria sujeción a un gara-
je dado, y nos lo entregara cada mañana en perfecto estado de funcionamiento. Y
es aquí donde aparecen las bases de datos orientadas-a-objetos en sus distintas
graduaciones.

DEGRADADO DE BASES DE DATOS

Como las verduras en las pequeñas tiendas de barrio, la Orientación-a-Objetos


en Bases de Datos posee distintos grados de pureza. Helos aquí:

29
Cuando formulo esta pregunta ante equipos de análisis y diseño estructurados la respuesta
invariablemente es: “¡Por supuesto! ¡Es lo que hacemos a diario!”

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 137
• Bases de Objetos Puras (ODBMSs: Object DataBase Manager Systems): Los
productos que se enmarcan en esta categoría usualmente ofrecen la misma
funcionalidad que puede encontrarse en gestores relacionales maduros (inte-
gridad, concurrencia, distribución, persistencia, recuperación de errores, etc.),
con la particularidad de basarse en un estricto modelo de objetos, significados
por OIDs (Object Identifiers). La naturaleza avanzada de los mismos procura
además, por lo general, servicios de migración de objetos, soporte de trabajo
en grupo, transacciones distribuidas, etc.

• Gestores de Almacenamiento Persistente (PSMs: Persistent Storage Mana-


gers): En entornos de ingeniería y CAD, donde lo que importa es la focaliza-
ción del equipo en el trabajo “real”, los PSMs proporcionan servicios de persis-
tencia que aislan al usuario especializado de las tareas de segmentación, al-
macenamiento y composición de entidades, usalmente con identidad propia
temporal. Diríamos que se trata de OODBMSs especializadas que, en esa ra-
zón, carecen (o disponen pobremente) de algunos de los servicios genéricos
de éstas, como control avanzado de integridad de datos, evolución dinámica
de esquemas, etc.

• Envoltorios de Objetos (OWs: Object Wrappers): Se trata de capas software


que se constituyen, como el envoltorio de un caramelo, en interfaz entre el
usuario y el pegajoso interior relacional. Las estructuras segmentadas en ta-
blas pueden ofrecer, así, un interfaz orientado-a-objetos susceptible de interac-
tuar con otros similares interfaces. Esta opción aparécese de forma natural
cuando se intentan compatibilizar las técnicas de orientación-a-objetos en aná-
lisis/diseño y GUIs con bases de datos relacionales pre-existentes, o aun con
la imposibilidad práctica de operar con gestores reales de objetos. El peligro
es, naturalmente, que pueda llegar a confundirse “wrapper” con “front-end” o,
aun peor, con un mero “interfaz-de-dibujitos”. En realidad los envoltorios que
aquí se detallan son usualmente no visuales, constituyéndose así en mediado-
res (O/R bridges) entre las vistas y la información tabulada que manejan. Así,
por ejemplo, el evento/mensaje “clicked” aplicado/enviado al botón de una de-
terminada vista resultará en el envío del mensaje “ejecutaQuery” a un objeto en-
voltorio de tipo, verbigracia, “ClienteWrapper”, que a su vez se ocupará, me-
diante la implementación interna (como función miembro en C++) de una fun-
cionalidad contenedora de una frase SQL, de acceder y unir (JOIN) varias ta-
blas para devolver, de forma conceptualmente unificada, una respuesta cohe-
rentemente encapsulada30. El “wrapper” se transforma así en un intermediario
que encierra cuestiones de implementación específicas de el/los gestor/es re-

30
El equivalente gastronómico es claro: el cocinero eficaz es, a efectos del comensal, un “wrap-
per” entre los simples alimentos y las pesadas composiciones culinarias. Evidentemente, cuando
las viandas poseen calidad, sabor y textura per se, el cocinero se troca en mero transportista:
vamos, la antítesis de la nouvelle cousine.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 138
lacional/es a que accede, separando así, de una forma adecuada para los su-
fridos mantenedores de aplicaciones, la oscura parte tabular de los volátiles
dibujos de ventanas. Hay que decir, empero, que subsisten -naturalmente- los
problemas de eficacia relacionados con la gestión de información compleja: un
vale de entrada y el abandono de andrajos facilitan la asistencia a la ópera, pe-
ro en absoluto ayudan a la inmersión musical en las obras.

• Bases de Datos Relacionales Extendidas (RDBMS/Os) o con-orientacion-a-


objetos (“a-algunos-objetos”, que diría más de un malicioso usuario): De las
chisteras de las grandes compañías de RDBMSs aparece, de tanto en tanto,
alguna porción de lo que nos presentan como conejo y que, sin embargo, es
mayormente gato. Ahora una patita (procedimientos almacenados), luego esos
bigotes (disparadores/triggers), más tarde ... ¿qué? La presión del mercado
consigue que lo que debiera presentarse como “RDBMSs mejoradas” -y cier-
tamente es así- aparezca en mercantilista calidad de “Bases de Datos Rela-
cionales Orientadas-a-Objetos”. Naturalmente este tipo de producto se debe al
casi siempre loable afan de supervivencia base de la evolución darwiniana, y
aunque algunos puedan decir que la evolución del diámetro de la cabeza para
albergar más ideas no es sino simple mixomatosis, la verdad es que las
cuestiones de compatibilidad se imponen: sea cual sea el tamaño de la
cabeza lo cierto es que seguirá gobernando el mismo cuerpo, lo cual es bueno
y malo. Bueno porque se aprovecha sin merma toda la estructura de
información pre-existente; malo porque nueve ginecólogos no reducen a un
mes el período de preñez de una gestante: encapsulación, herencia y
polimorfismo son conceptos en buena medida ajenos a estos gestores.

Qué producto usar depende en gran medida del lenguaje, de las herramientas,
del tipo de aplicación. Y aunque lo usual es empezar por el ámbito más relacional
e ir escalando características de objetos según se encuentran problemas o impo-
sibilidades, yo recomendaría comenzar con las bases de objetos puras e ir ba-
jando a esquemas con mayor componente relacional conforme no se cumplan las
características de eficacia, rapidez, etc. Deseadas. Pero vayamos a la parte
humana de los objetos.

LOS DESEOS DEL PROGRAMADOR

Seguro que los programadores, como clase, albergan toda suerte de deseos no-
civos, pero entre ellos uno destaca por su interés morboso: el de liberarse de las
repetitivas codificaciones de acceso y recuperación de datos en bases de datos.
Si, de acuerdo con el esquema involutivo en espiral (de fuente o de piscina) de
las fases de análisis y diseño, la mayor parte del tiempo se pasa intentando gene-
rar un interfaz limpio de clases para su implementación, ¿por qué no dedicar el
tiempo restante a la codificación algorítmica inteligente, en vez de tratar con as-
pectos que podrían ser en gran medida mecanizados o, cuando menos, obviados

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 139
por sistemas de gestión de más alto nivel? La respuesta a la pregunta retórica es
obvia. El lector se cuestionará, empero, el calificativo “morboso”: lo cierto es que
la solución de las cuitas aquí planteadas pasa por una degradación de la profe-
sión misma de programador, cuya funcionalidad básica ha sido, tradicionalmente,
trasladar a código los resultados exactos de la fase de diseño. Si, con el nuevo
esquema del ciclo de vida del software, la codificación queda grandemente susti-
tuida por la lectura y el reuso de clases insertas en bibliotecas y entornos-marcos,
y además prácticamente se elimina la codificación final de almacenamiento, ¿qué
queda para el programador? ¿No resulta más rentable y efectivo, más aún te-
niendo en cuenta el gran acervo actual de herramientas software, que el mismo
diseñador traslade al código los resultados del diseño? Edward Berard afirma
que la figura del programador habrá de ser sustituida por la del “Analista del Do-
minio del Problema Orientado-a-Objetos” (OODA: Object-Oriented Domain Ana-
lyst). Tal presunción es un tanto efectista, pero la idea subyacente es prudente-
mente razonable. De cualquier forma el hecho es simple: el programador tiende a
desear un gestor de almacenamiento transparente (por invisible), o cuando me-
nos translúcido. El presupuesto inicial es sencillo: el manejo de instancias de ob-
jetos en memoria ha de ser igual o muy parecido al manejo de los mismos obje-
tos en un espacio de almacenamiento físico. Así, si consideramos que la memo-
ria es el almacenamiento primario (AP) y, verbigracia, el disco el secundario
(AS), el esquema ideal de cosas pasaría por considerar que los objetos (instan-
cias) pueden, merced a mecanismos muy simples, pasear entre AS y AP consi-
derando éstos como zonas de un hábitat global para nuestras instancias.

PERSISTENCIA, TRANSITORIEDAD Y MATRICES

La persistencia es, en esencia, la cualidad de un objeto o grupo de objetos de


sobrevivir al proceso o CPU que los creó. Esto es, los objetos podrán, tras ser
usados, ser accedidos todavía por otros objetos, pues anidarán en la zona persis-
tente del espacio de almacenamiento unificado. Los objetos transitorios (transient
objects) son, en contraposición a los persistentes, aquéllos cuyo acceso no puede
garantizarse tras terminar el proceso que los creó y/o usó. Y fíjese bien el lector:
no es que no puedan ser accedidos ocasionalmente, sino que no puede garanti-
zarse en general tal acceso. Tenemos, pues, dos características que, junto con las
zonas de almacenamiento (AP y AP), generan una matriz que puede complicar la
vida al programador dependiendo del enfoque que haya adoptado el producto de
base de datos. La cuestión depende, básicamente de la dependencia entre la
cualidad de persistencia y la clase de la instancia que se pretende persistente.

ORTOGONALIDAD DE PERSISTENCIA Y TIPO

La ortogonalidad se refiere, según el Manifiesto de Sistemas de Bases de Datos


Orientadas-a-Objetos, Tokyo, 1.989, a la independencia entre el tipo de un objeto

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 140
y su capacitación para asumir características persistentes. Al decir “ortogonal” se
pretende indicar una relación similar a la de dos ejes perpendiculares en un pla-
no, representativos de distintas dimensiones y, por ende, independientes. La di-
cotomía queda reflejada en los siguientes distintos enfoques:

• La persistencia es una cualidad o capacitación de todas las instancias de


una clase o clases dadas: en esta variante un objeto de una determinada cla-
se (de cualquier clase, en un modelo más amplio pero que aquí no considera-
remos debido a la serie de debilidades prácticas que conlleva) automática-
mente se convierte en persistente al crearse. Digamos que existirían clases (en
un modelo restringido, mucho más flexible) cuya persistencia no merecerá la
pena y otras cuyas instancias contengan información no volátil. En la práctica,
un enfoque muy extendido para insuflar la cualidad de persistencia a una clase
es hacerla derivar de otra que encierre los mecanismos de persistencia (gene-
ralmente denominada “Persist” o algo parecido). El problema que suele plan-
tear este enfoque es el “apuntamiento” de objetos desde el AP al AS y vicever-
sa, que se troca muchas veces casi inmanejable en objetos compuestos. Ima-
ginemos, por ejemplo, que nuestra clase “Cliente” genera instancias persisten-
tes. Pero la clase contiene muchos atributos, pertenecientes, a su vez, a otras
tantas clases, algunas de las cuales serán transitorias y otras no. Si, de acuer-
do con el esquema flexible planteado, una instancia de Cliente contiene un
atributo, verbigracia, “apodo” de una clase “Apodo” de tipo no persistente, la
vida se va a complicar para el programador: no pueden incluirse para este
atributo mecanismos de inicialización equivalentes a los usados en Bases de
Datos Relacionales, pues lo usual es que no podamos controlar si un objeto
persistente apuntado (la instancia de Cliente) está en cada momento en una
zona transitoria o en una zona persistente, de tal manera que habría que añadir
código de comprobación e inicialización en cada función que intentara acceder
al atributo volátil. Pero el problema se agrava cuando tal atributo es accesible
mediante una consulta a la OODB. Naturalmente una opción muy practicada es
evitar la mezcla, siquiera en composición, de clases -y aun de objetos- persis-
tentes y transitorios.

• Cualquier instancia de cualquier clase puede ser persistente o transitoria: de


esta forma no se pretenden incluir mecanismos específicos en la clase, direc-
tamente o por herencia, sino que más bien el sistema gestor se encarga de
traspasar o no la cualidad de persistencia merced a comportamientos influen-
ciables por el usuario. Una práctica muy extendida, adoptada por GemStone y
O2 entre otros sistemas, es que el sistema conste de unos objetos persistentes
predefinidos, de manera que una instancia cualquiera se convertiría también en
persistente si es “apuntada” por alguno de aquéllos objetos. Este esquema,
más complejo que el no-ortogonal, elimina, sin embargo, buena parte de la
complejidad de la matriz “Espacio de Almacenamiento x Persistencia”: si una
instancia transitoria de la clase, verbigracia, “Persona” con identificador “mi-
Tio” se troca en persistente por su inclusión, por ejemplo, en el objeto persis-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 141
tente de tipo agregado con identificador “miFamilia”, las instancias concretas
significadas por variables en nuestro objeto “miTio” se trocarán también en
persistentes. La persistencia se perderá cuando la instancia deje de ser apun-
tada por objetos persistentes (cuando, por ejemplo, “miTio” sea expulsado de
“miFamilia” por crápula). Naturalmente este enfoque es el preferido por los
programadores, porque convierte en invisible la gestión de almacenamiento.
Desgraciadamente, también, sus implementaciones actuales muchas veces no
se ajustan a lo requerido.

CRÍTICA DE LA RAZÓN PRÁCTICA

Bien, bien. Las cuestiones prácticas son: ¿realmente funcionan las Bases de
Objetos? ¿Facilitan las tareas de codificación y, por ende, favorecen la ejecución
de proyectos? ¿La velocidad y eficacia son aceptables? ¿Puedo contar, al
menos, con las mismas características disponibles en el sector relacional?
¿Puedo ... ? Sí, sí, sí, sí y ... err ... no. La tecnología anda en buena medida
renqueando tras los productos comerciales, así que los esquemas de
persistencia, recuperación de errores, versionamiento, concurrencia, integridad
referencial31, etc. dependen del producto en cada caso elegido. Mi experiencia
señala que los conceptos que aquí se han apuntado, siquiera someramente, son
asimilados con cierta eficacia cuando pueden ejemplificarse aplicándolos a un
producto concreto. La comprehensión aumenta cuando los mismos ejemplos se
refieren a porciones conceptuales de una aplicación homogénea. Así que lo mejor
será emplazar al lector para que use (y abuse) de una Base de Objetos
comercial. Bueno: la intención es lo primordial.

INTERNET: ¿CÓMO NO?

Los interesados en las cuestiones aquí esbozadas pueden acercarse por el grupo
de noticias de usenet “comp.databases.object”, excelente punto de partida para
novicios, donde encontrarán interesantes discusiones e información práctica de
uso de productos. Las típicas preguntas, por otro lado, sobre productos comercia-
les relacionados con las Bases de Objetos obtendrán su respuesta en la FAQ
informal del grupo que, con el calificativo de mini-faq, mantiene y actualiza regu-
larmente Tim Harvey. Yo mismo mantengo, de acuerdo con Tim, una copia de
esta mini-faq en “http://www.well.com/user/ritchie/mini-faq.html”.

31
Incidentalmente cabe notar que, como en otros ámbitos, la mejor manera de solucionar los
problemas de integridad referencial es precisamente ignorarlos. GemStone, por ejemplo, impide
la eliminación efectiva de cualquier objeto apuntado por otro, posponiendola hasta que el objeto
quede totalmente desreferenciado y entre en marcha el recolector de basura.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 142
Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 143
CONSIDERACIONES PRÁCTICAS
9
L
a generación de aplicaciones, en su justa acepción de construcción de mo-
delos representativos de sistemas de información con funcionalidad en el
mundo real, ha experimentado cambios sustanciales en los últimos años
debido, sobre todo, a la hiper-influencia de los entornos gráficos de usuario que,
de forma necesaria pero abruptamente demoledora, han redefinido los roles per-
sonales clásicos respecto del uso de los aplicativos software. De esta manera, no
se da en la práctica una distinción clara entre programas finales (v.b.: MS Word
for Windows), herramientas de ayuda al usuario (v.b.: macros o shortcuts), herra-
mientas verticales de desarrollo asistido (v.b.: wordbasic, el "lenguaje de progra-
mación de MS Word"), etc. La patente exigencia del mercado resulta, por otro
lado, meridianamente clara: aplicaciones "amigables" (desafortunada traducción
de "friendly") basadas en sistemas gráficos multi-ventanas, apoyados en siste-
mas operativos o shells con capacidades para compartir, o al menos simular
compartir, tareas y procesos. Esta serie de circunstancias, tan sucintamente so-
brevoladas, ha conducido a claros desajustes en los esquema de desarrollo que,
finalmente, se han asentado sorprendentemente como "estándares implícitos de
facto" en la mayoría de las empresas de programación. Así, por ejemplo, las
herramientas de desarrollo se han transformado (o pretenden hacerlo) en "entor-
nos visuales de desarrollo" (bajo la irónica mirada de los expertos en auténticos
lenguajes visuales, cuyas batallas y gestas discurren en la más alejada ucronia de
aquéllos) donde los modelos de información se unen de forma "flexiblemente in-
disoluble" (según afirma una conocida empresa del sector, rea de crimen linguís-
tico-publicitario) y todo resulta "drag-n-drop", "apropiado para RAD" (rapid appli-
cation development), "idóneo para RPD", etc.

Hasta aquí pudiera parecer que el autor mantiene una absurda batalla contra los
entornos gráficos: ¡nada más alejado de la realidad! En realidad el firmante es un
acérrimo defensor de los entornos gráficos de usuario (y actualmente de los sis-
temas WIMP, tan viejos en el tiempo), pues estos tienden (y desgraciadamente en
muchos casos sólo tienden) a generar aplicativos más intuitivos en los que la ton-
tería, si realmente la hay, es más evidenciable. Lo que al fin se lamenta es la falta
de normalización de estructuraciones básicas que permitan desarrollos gráficos
modulares no basados únicamente en su propia y discutible representación visual

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 144
(sorprende, por ejemplo, constatar que no existen estándares en la confección de
cuadros de diálogo, en la arquitectura de aplicaciones gráficas, en la disposición
de controles visuales, etc.). Se generá, así, lo que Kafka llamaba "El Pozo de Ba-
bel" y que, en esencia, es un acopio de procedimientos propietarios en un es-
quema recolectivo que podríamos llamar de "basurero", del que la fuerza del mer-
cado y mayormente de la publicidad rescatan algunos productos, con toda su
carga de decisiones y soluciones particularizadas.

En definitiva: el énfasis gráfico ha llevado a la construcción de aplicaciones (no a


todas, afortunadamente) a un punto donde lo que se está viniendo en denominar
"ComponentWare" resulta un eufemismo peligrosamente atractivo: los sistemas
de información se segmentan, básicamente, así, en una capa modelable como
repositorio de información (típicamente mediante un gestor de base de datos re-
lacional -RDBMS) y otra asimilable como front-end que posibilite el acceso intuiti-
vo, cómodo, efectivo y "gráfico" a aquélla. Pero, ¡esto no suena mal! ¿Dónde es-
tá, pues, ese "grave error" que se desprende del tono de este análisis? Veámos-
lo.

PANORAMA PARA MATAR

Resulta que la construcción eminentemente gráfica de modelos de información en


la actualidad se basa, de forma desproporcionada, en la ligazón entre el interfaz
gráfico y el interfaz del gestor del repositorio, según lo expresado en el epígrafe
anterior. Así, por ejemplo, un caso típico de esta dicotomía se refleja en la opción
de uno de nuestros clientes, con una facturación anual de más de $30,000,000, al
decidir originariamente soportar con front-ends en "Visual Basic" la información
tabular gestionada por "Oracle". Los "campos" gráficos se convierten, así, en es-
pejo rígido de los "campos" tabulares de la base de datos relacional: campos
para introducir fechas, campos para introducir números, campos para introducir
decimales, etc. La mayoría de herramientas CASE tradicionales (y aun de 4Gs),
con extensiones a objetos o no, recaen en este mismo esquema. Naturalmente
esto facilita la automatización de la producción de código, pero imposibilita, por
otro lado, la reutilización del mismo (tanto del modelo como del interfaz gráfico):
un interfaz devendrá inservible cuando cambie la estructuración interna de la in-
formación que mantiene, a la vez que es posible que un cambio en el interfaz sig-
nifique una modificación de los componentes tabulares a los que accede. Vea-
mos con más detenimiento algunos ejemplos de tal constrictora ligazón:

La propia arquitectura inducida en los aplicativos software por los sistemas gráfi-
cos, usualmente restringida a esquemas monomodales, origina graves desajus-
tes en la concepción de la representación del sistema de información a modelar:
la secuencialidad (o la ilusión de secuencialidad) inducida por el esquema gráfico
soportado origina la necesidad de construir enlaces (links) entre estructuras de
datos, de manera que éstas puedan "comunicarse" (la profusión de vocablos "en-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 145
trecomillados" no es en absoluto casual, como el lector habrá ya adivinado), pro-
duciéndose el curioso efecto de que las deficiencias de la arquitectura gráfica
ocasionan deficiencias en la información que sus componentes manejan, lo que a
la vez provocará un rediseño cada vez más pernicioso de los mismos interfaces
gráficos, que repercutirá de nuevo en la estructuración interna de la información, y
así hasta ... que el "schedule plan" obligue al lanzamiento del producto. En reali-
dad los enlaces que se generan son tan férreos que las aplicaciones resultantes
suelen ser "monolíticas", proporcionando una apariencia de diversidad similar a
la que tiene un recluso cuando, el primer día, recorre su celda en la oscuridad.

REFLEXIONES REBAJADAS

¿Existe una solución global para los problemas y deficiencias expuestos? ¡No,
por supuesto! Aún más: se diría que en absoluto es deseable un bálsamo de fie-
rabrás que restañe y cure todo lo curable, en lo que a software se refiere. Es difícil
pensar que la diversidad natural, difícilmente asimilable en su completitud, de los
sistemas de información pueda ser simplificada en un único esquema mecanicis-
ta. Lo que se pretende, al fin, con este modesto capítulo, es explicitar una serie de
criterios, decisiones y esquemas estructurales que habrían de conformar las ba-
ses estáticas y de comportamiento dinámico de un grupo mayoritario de sistemas
software, teniendo en cuenta que, puesto que tales circunstancias normalmente
serán presupuestos de migración entre tecnologías (usualmente de RT a OOT),
se habrá de ponderar más la inmediata practicidad tangible que la formalidad
huera del efectismo tecnológico, de dudosa utilidad en relación con una aproxi-
mación conscientemente simplificada cual es la presente.

A fin de facilitar la comprensión de las ideas arquitectónicas propuestas, éstas se


expondrán en epígrafes en buena medida autosuficientes, a la vez que los ejem-
plos de aplicación se presentarán en C++, intentando proporcionar un asidero
práctico de aplicación de lo en cada caso expuesto:

Distribución Jerárquica Cósmica de Clases


El esquema jerárquico de clases del sistema a modelar se entenderá directa-
mente cósmico: esto es, todas las clases derivarán de una clase base especial
para la aplicación y también para el propio cliente y sus futuros desarrollos. Se
trata de la clase BaseObjeto, que implementará estados y comportamientos co-
munes, a la vez que forzará la adecuación de todas las clases a ciertas funciona-
lidades (mediante funciones virtuales y funciones virtuales puras). Tal clase permi-
tirá, también, estructurar de forma adecuada el polimorfismo de vistas, en el más
puro estilo Smalltalk. Un esquema de clases no-cósmico, o multibase, podría
haber resultado mucho más elegante, pero hubiera conllevado, también, el enfren-
tamiento con problemas de codificación que requerirían un conocimiento no-trivial
(por no decir extremadamente pulido) del lenguaje de programación (C++) y del

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 146
paradigma aplicado de orientación-a-objetos, no aportando, empero, ninguna
característica adicional de efectividad o seguridad en proyectos típicos de migra-
ción (en realidad el firmante trabaja casi siempre con esquemas no cósmicos y
hace uso intensivo de la herencia múltiple, pero tal no es de pedagógica aplica-
ción aquí).

El Paradigma M2VC
La arquitectura del sistema se basará en una matización del paradigma MVC que
denominaremos M2VC. En primer lugar atendamos a lo que MVC (siglas de Mo-
del View Controller) significa: se trata de la separación de los módulos de un sis-
tema software en tres capas: la del modelo, la de la vista y la del controlador, in-
teractuando de la siguiente forma: un objeto perteneciente a la capa del modelo
sólo puede ser accedido por un objeto perteneciente a la capa de la vista, que, a
su vez, envía y recibe mensajes del objeto controlador. Existe un objeto controla-
dor por cada aplicación activa, y es tal objeto el encargado de transformar las
arquitecturas subyacentes en estructuras orientadas-a-objetos, trocando las lla-
madas a funciones y eventos en mensajes comprensibles para las vistas. Aunque
el paradigma MVC se ha demostrado enormemente eficaz, no existe ninguna ex-
posición teórica del mismo que sobrepase, en esencia, las sucintas notas ante-
riormente expuestas, aunque sí se pueden examinar multitud de sistemas basa-
dos en el mismo. En realidad el enfoque MVC fue el adoptado, de facto, por
Smalltalk-80, y aparece omnipresente en los trabajos de Adele Goldberg y Alan
Kay. Este paradigma facilita sobremanera la independencia de un objeto respec-
to de su representación visual o interfaz con los usuarios, de forma que tales inter-
faces podrían variarse sin afectar apenas a la estructura de la aplicación. La única
desventaja que tal enfoque presenta es, a nuestro juicio, que las vistas son jerár-
quicamente anteriores a los objetos, de forma que no se producen llamadas a
objetos, sino a vistas que a su vez llaman a objetos: esto obliga a que, si se cam-
bia un interfaz, deba modificarse el código de la llamada al mismo en la aplica-
ción. Para solventar esta situación hemos decidido aplicar una matización de es-
te enfoque que nosotros denominamos M2VC. Se trata, en definitiva, de que las
llamadas a las vistas las gestiona el objeto en sí. De esta manera, todos las cla-
ses tienen acceso a una función miembro que genéricamente denominamos "dia-
log()" o "dialoga()", que se hace "virtual" en la clase base cósmica "BaseObjeto" y
que permite acceder al interfaz apropiado de cada objeto con independencia del
conocimiento de su tipo exacto. Así, el cuerpo típico de tal función en una clase
dada responde al siguiente esquema:

miClaseModelo::dialog()
{
new DlgClaseModelo( controlador->getFocus(), this );
}

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 147
Esto es, se crea un objeto de tipo vista (derivado, en nuestro caso, de BaseDia-
logo), no persistente, al que se la pasa como parámetros la vista que actualmente
retiene el focus en el entorno gráfico (para poder "ahijar" la nueva vista), además
de un puntero al mismo objeto del modelo para que la nueva vista lo pueda asimi-
lar como el puntero al modelo que se cita en el epígrafe Gestión de Objetos a tra-
vés de Vistas. La ventaja de esta matización es que si se cambia la vista a través
de la que se accede a un objeto de una clase dada, únicamente habrá que cam-
biar la implementación de tal clase, sin tocar en absoluto el código de la aplica-
ción. Y aun, en un esquema de "Constructores Virtuales" de vistas, ni siquiera
habría que cambiar la implementación de la clase modelo, sino añadir un nuevo
objeto estático de tipo vista a la cadena de vistas asociada a una clase dada,
aunque este último enfoque es excesivo para una primera aproximación a
C++/OOP/OODB.

El enfoque M2VC conlleva, como lógica consecuencia, una mayor facilidad en el


tratamiento del interfaz de los objetos, pues estos pueden ser accedidos a través
de un puntero al objeto con independencia del conocimiento exacto de su tipo. De
esta manera se puede iterar por una colección de objetos de distinto tipo y hacer
aparecer, de forma polimórfica, el interfaz en cada caso apropiado. De hecho,
usualmente los menús conducen a objetos de tipo agregado (derivados de Ba-
seColeccion), que llaman a su propio interfaz de tipo contenedor, a través del que
se accede a los objetos "simples" y, por ende, a sus interfaces pertinentes. Pero
esto será examinado en más detalle en el epígrafe Gestión de Objetos mediante
Colecciones.

Arquitectura Gráfica MultiModal


Existen dos tipos básicos de arquitecturas en sistemas software respecto de la
secuencialidad o no de los procesos (o mensajes o eventos) que conforman y
caracterizan a las vistas respecto del entorno en que se apoyan. Se trata, abu-
sando de los barbarismos, de arquitecturas "modales" y "nomodales" (o mejor,
multimodales). La aproximación modal se basa en la estricta secuencialidad
temporal de la focalización de los interfaces gráficos (denominaremos en adelan-
te interfaces gráficos a las vistas en general, aunque éstas pudieran ser construi-
das en entornos textuales o, en un ámbito más amplio, pudieran ser esencialmen-
te multimedia): esto es, cuando una vista aparece en la pantalla, todos los eventos
y mensajes son necesariamente dirigidos a ella, de forma que los mensajes al
resto de las vistas son ignorados o, a lo sumo, procesados de forma uniforme (un
pitido, una condición de error, etc.). Esto significa que el focus de la aplicación no
puede traspasarse de una vista a otra de forma voluntaria. Examinemos breve-
mente, para esto bien entender, la sustancia común que anima la construcción de
vistas en entornos gráficos: una vista no puede crearse de la nada, sino que for-
zosamente ha de depender de una vista anterior que, como la razón última de las
cosas del de Aquino, finalmente ha de ser el entorno gráfico en sí. Naturalmente
esto no es un modelo teórico, sino más bien una imposición práctica que facilita

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 148
la gestión de vistas. Naturalmente, también, podría haberse obviado tal aproxima-
ción y trabajar con un esquema dinámico (y voluntario) de dependencias, pero la
realidad es que el primer esquema permite montar, como una mera disgresión o
curiosidad, el segundo, de forma que no hay contradicción ni solapamiento: úni-
camente comportamiento práctico por defecto. De esta manera existe una vista
que calificamos de Vista Principal de la Aplicación y que directamente depende
del entorno gráfico elegido en sí. Esta Vista única (de acuerdo con las normas
CUA) contiene menús desplegables y se constituye en "padre" de todas las de-
más vistas de la aplicación, con el siguiente esquema: cualquier vista creada di-
rectamente desde la Vista Principal mantiene a ésta como "padre" y la vista en
cuestión pasa a ser la "hija" de aquél (quizá en un entorno menos masculino de-
beríamos hablar de "madre" e "hijo", o de "progenitor" y "progenie", pero en este
caso dejaremos que el lenguaje, con sus géneros y traducciones, nos arrastre).
Así, repetimos, la Vista Principal produce Vistas Primeras, directamente depen-
dientes de aquélla; pero éstas, a su vez, pueden generar Vistas Secundarias (las
adjetivaciones de las vistas son, por supuesto, únicamente pedagógicas), y éstas
generar otras, y así ad nauseam (o hasta que "se acaben los recursos", según
rezan algunos inopinados mensajes de ciertos entornos gráficos -lisez MS Win-
dows). De esta manera resulta que, puesto que cada "padre" conoce a sus
"hijas", éstas habrán de conocer a su "padre" (noténse las características educa-
cionales y canallescamente sociales de esta aserción). En un entorno gráfico úni-
camente una vista puede retener el focus en un momento dado: esto significa que
siempre hay una única vista localmene activa: o sea, los mensajes que dirijamos
al sistema se "focalizan"" sobre la vista activa y de ésta se dice que "retiene" o
posee el "focus" (foco, en un castizo menos latino). Los mensajes a otras vistas
son generalmente, pues, ignorados o sobreestimados. La aparición de una nueva
vista (siempre dependiente de una anterior, o "padre") origina que el focus se
traspase a ésta, así como la misma podría automáticamente traspasarlo a otra de
la que dependa. En un arquitectura modal el focus es retenido por una vista mien-
tras no se destruya (con lo que el focus pasaría automática al "padre" del que de-
pende) o cree una vista depeniente a la que traspasarlo. A efectos de codifica-
ción esto significa que si en una línea del programa se produce una llamada, dire-
cta o indirecta, a una vista derivada, ésta asumirá el focus y no lo devolverá a la
vista en que se produce la llamada hasta que desaparezca. Naturalmente esto
sugiere e invoca una inevitable secuencialidad en el esquema de mensajes entre
vistas (y aun entre objetos). Significa, también, que la secuencialidad del código
es absolutamente determinista: esto es, tras la línea que llama a la vista (en un
ejemplo típico con C++):

void ClaseCualquiera::llamaAVista()
{
DialogoCualquiera* miDialogo = new DialogoCualquiera();

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 149
el control indefectiblemente, tras la desaparición del diálogo, pasará a las si-
guientes líneas, que podrían perfectamente ser las siguientes:

// extrae información del objeto miDialogo


delete miDialogo;
}

de tal manera que se podría interrogar al diálogo por los objetos o estados de
éstos que nos interesen. Este esquema fuerza un tipo de codificación demasiado
basada en las dependencias casuales entre los distintos tipos de vistas. Obliga,
también, por su propia naturaleza basada en dependencias jerárquicas, a cance-
lar una vista para poder acceder a otras o, en el peor de los casos, a incluir en
una vista controles gráficos que permitan acceder a otra nueva vista sin cancelar
la primera. Por supuesto que esta última estrategia, usada de forma intensiva por
la práctica totalidad de las empresas de desarrollo de software, es particu-
larmente nociva y origina con demasiada facilidad vistas o interfaces en absoluto
reutilizables fuera del contexto para el que fueron creados. ¿Qué ofrece, por otro
lado, el enfoque multimodal? Pues sencillamente la posibilidad de traspasar vo-
luntariamente el focus desde una vista a otra cualquiera, actualmente visible o no
(normalmente mediante el uso del ratón, pero también del teclado u otros me-
dios). Esto causa que los interfaces no habrán de depender de las relaciones que
entre ellos se establezcan dinámicamente, reforzando, al descontextualizar su
uso, la reutilizabilidad práctica de los mismos. Se derivan, a la vez, nuevas e im-
portantes consecuencias con respecto de la secuencialidad del código: una línea
típica de llamada a una vista (en C++) sería la siguiente:

void ClaseCualquiera::llamaAVista()
{
new DialogoCualquiera();
}

y aquí se aprecian modificaciones evidentes: no hace falta identificador de la vis-


ta, ni se procede a su destrucción, como tampoco a su chequeo a efectos de ex-
traer información sobre los objetos que tal vista ha manejado. Hay que pensar que
tras la ejecución de la línea en la que -en este caso- se crea la vista simplemente
no ocurre nada secuencialmente previsible. Esto es, la vista se crea -en nuestro
caso- y literalmente "se abandona" para que interactúe, de la forma más adecua-
da, con otros objetos de tipo "Vista". Pero, según vemos, la memoria necesaria
para la construcción de la vista se asigna dinámicamente: ¿quién se encargará,
pues, de desasignar tal memoria? ¡La propia vista, claro está! El esquema es el
siguiente: una Vista Padre crea una Vista Hija y, como en los peores folletines (y
también en los mejores), la abandona a su suerte en un mundo con vistas de todo
tipo, calaña y condición. El usuario, como metaobjeto de la aplicación, se convier-
te, así, en el único responsable de que tal o tales vistas permanezcan o no en su
terminal, pudiendo en cualquier momento optar por su eliminación o destrucción

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 150
concreta o por una más general desasignación (el cierre de la aplicación entera).
Así, el código de destrucción de una vista estaría -naturalmente- escondido en el
destructor de ésta (y volvemos a la nomenclatura C++), de la forma:

DialogoCualquiera::~DialogoCualquiera()
{
// desasigna la memoria dinámica
// asignada en el constructor
}

Pero, ¿quién llama a este destructor? ¡El usuario! Esto es: el usuario manda un
mensaje a la vista requiriendo su desaparición bien directamente (lanzándole un
mensaje "ciérrate" o "close"), bien indirectamente (lanzando un mensaje "close" a
cualquiera de sus padres o accionando una operación de la misma -típicamente
"aceptar" o "cancelar"-). o aun lanzando una orden de cierre masivo de vistas
(como la procurada por el cierre de la aplicación). Naturalmente el esquema antes
apuntado se mantiene, y, por ende, cualquier Vista conoce a sus hijos (mantiene,
en definitiva, una colección de punteros a los mismos, en lenguaje coloquial o de
programador). Bien: no hay problemas con la memoria, pero se plantean dos nue-
vas cuestiones: ¿qué ocurre con los objetos que una vista ha gestionado, leído y/o
modificado? ¿qué ocurre, por otra parte, cuando en una vista se leen o modifican
objetos o algunos de los atributos de estos que, a su vez, están siendo leídos y/o
modificados por otras vistas, o aun por otras vistas en otros terminales o clientes?
Bien, esta última cuestión es oportunamente tratada en el epígrafe dedicado a la
Gestión de Concurrencias, mientras que la respuesta a la primera perfectamente
corresponde al esquema multimodal como solución aplicativa, pero será tratada
en detalle en el epígrafe dedicado a la Gestión de Objetos a través de Vistas. Y
este es el momento de anunciar que el "multimodo" es, de hecho, una generaliza-
ción del comportamiento "modal", por lo que lo establecido para aquél puede
perfectamente ser aplicado en éste, aunque no a la inversa.

Gestión de Concurrencias
El esquema de control de concurrencias se basa en que cada usuario de un obje-
to o grupo de objetos, identificado por el acceso único al interfaz de éstos, traba-
jará con un conjunto intermedio de punteros a los objetos de la base de objetos en
cada caso manejados. Se trata, en definitiva, de una "shallow copy" (copia super-
ficial) de los objetos en cada caso utilizados por el usuario (naturalmente esto
incluye objetos simples a la vez que colecciones y, en general, objetos de tipo
agregado). Se trata, por tanto, de que cada interfaz, en correspondencia biunívo-
ca con el usuario único que lo accede respecto del terminal en que anida, utiliza
un objeto intermedio del mismo tipo que el objeto al que se intenta acceder, te-
niendo en cuenta que se trabaja en un entorno gráfico multiusuario y multimodal.
Los casos posibles son: un objeto simple (que puede contener enlaces navega-
cionales a otros objetos) y una colección, o agrupación de objetos (simples o co-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 151
lecciones a su vez). En cualquiera de los casos, en orden a la arquitectura gráfica
elegida (el paradigma matizado M2VC), tales objetos intermedios serán maneja-
dos únicamente por las vistas (interfaces gráficos de los objetos correspo-
ndientes), pudiendo ser o no persistentes, aunque una elemental prudencia inicial
aconseja su inclusión en la memoria transient, toda vez que este sistema no ne-
cesita de persistencia de objetos intermedios para recuperar fallos en el sistema.
Veamos los comportamientos por separado:

• Acceso a un objeto simple: El interfaz partirá directamente con el punte-


ro al objeto que forma parte de su representación interna o que, en algu-
nos casos, se pasa como parámetro al objeto (normalmente por medio
de su constructor), pero, como se detalla en el epígrafe dedicado a la
Gestión de Objetos a través de Vistas, se generará una copia temporal.
Como quiera que se trabaja en un entorno multimodal y, por ende, el fo-
cus no se retiene necesariamente por la vista, esto significa que mien-
tras que se trabaja con la lectura estática, traspasada a los controles, de
los atributos del objeto accedido, éste podría estar cambiando en otra
vista, en el mismo terminal o en otro distinto. La política a aplicar será
simplemente que la última modificación es la que vale, con independen-
cia del esquema de refrescos o posibles actualizaciones de la informa-
ción respecto del interfaz en que aparece.

• Acceso a una colección (objeto de tipo agregado): El interfaz trabajará


directamente con un puntero al objeto de tipo Coleccion al que se quiere
acceder, pero los accesos se realizarán mediante una colección inter-
media, cuyo funcionamiento se detalla en el epígrafe Actualización de
Vistas, que conservará la biunivocidad del orden con respecto del con-
trol de la vista (típicamente listboxes o comboboxes) en que se visuali-
zan los objetos contenidos en la misma. En tal colección intermedia se
insertarán los punteros a los objetos que se deseen manejar, y, a la vez
o iterando en una secuencia posterior, se pasará la representación vi-
sual de cada uno de ellos al control apropiado de la vista, de forma que,
por ejemplo, el undécimo ítem en una combobox siempre habrá de co-
rresponder al undécimo objeto contenido en la colección ordenada in-
termedia. Para la modificación de cada uno de los objetos individuales
que la colección contenga se llamará al interfaz particular de éstos y se
seguirá el esquema observado en el párrafo anterior sobre el acceso a
objetos simples. La inserción o borrado de un objeto en la colección ori-
ginal no afectará, pues, a la relación biunívoca entre el control gráfico y
la colección intermedia.

Los dos casos detallados ofrecen, empero, un problema relacionado con el bo-
rrado de objetos: qué ocurre si se intenta acceder mediante el puntero que mane-
ja una vista a un objeto que ha sido físicamente borrado por mediación de otra
vista. Bien, este caso simplemente no se puede producir. Veamos el esquema:

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 152
Eliminación de objetos: en ningún caso se producirá la eliminación física
directa de los objetos persistentes, sino que se utilizarán recolectores de
basura (garbage collectors) a aplicar sobre objetos desreferenciados. Es-
to supone que el acceso desde una vista a un objeto (vía puntero) está
siempre garantizado, pues al ser apuntado precisamente por la vista tal
objeto no puede desaparecer, manteniendo a la vez la posibilidad de que
el mismo objeto pueda ser aparentemente borrado o apartado del interfaz
accesible por un usuario dado o un conjunto de éstos. ¿Cómo se consigue
ésto? Se pueden dar dos casos: si el objeto está en una colección de uso,
se procede a su extracción de la misma desde el interfaz de ésta y se de-
posita bien en el extent (para su posible reutilización ulterior) bien en una
colección genéricamente denominada "basurero" (de la que será física-
mente eliminado cuando quiera que ningún otro objeto lo referencie); si el
objeto está únicamente en el extent, la eliminación consistirá en su inser-
ción (en la inserción de un puntero que lo apunte) en un objeto "basurero".
Naturalmente la eliminación aparente o no de un objeto generará una inci-
dencia interna auditable, según se detalla en el epígrafe Incidencias de
Usuarios.

El principio de "la última modificación es la válida", como ya se ha expresado,


puede ocasionar disfuncionalidades inesperadas en el acceso a distintos objetos
(es posible, por ejemplo, que un usuario esté modificando un objeto y su vista sea
anterior a la última modificación realizada por otro usuario, de forma que al con-
cluir el proceso, se estará sobrescribiendo una modificación que ni siquiera se ha
visualizado). Por eso, opcionalmente, se propone un sistema alternativo al de blo-
queos que pueda manejar adecuadamente la concurrencia de usuarios sobre un
mismo objeto. La idea básica es que, como quiera que un interfaz puede tener
tres niveles de acceso, según lo expuesto en el epígrafe de Seguridad del Siste-
ma, este sistema de concurrencia será aplicado por las mismas vistas (diálogos
derivados de BaseDialogo) únicamente cuando alguna de ellas acceda a un obje-
to en modo "total" (esto es, con posibilidad de escritura o modificación). El siste-
ma de indicadores (o "flags", en un acertado barbarismo onomatopéyico) real-
mente no funciona, pues en caso de que se marque un determinado objeto como
bloqueado, si la aplicación en la consola del usuario bloqueador termina abrup-
tamente, el objeto en cuestión seguirá marcado. Nuestro enfoque es matizada-
mente distinto: se trata de que cuando una vista tenga acceso "total" a un objeto,
tal objeto se insertará en una BaseColeccion que se podría denominar "Bloqueo-
sActivos", pero tal colección no será persistente, sino que anidará en la base de
objetos global "transient" (o transitoria, en desafortunada traducción). De esta
manera cada vez que una vista acceda a un determinado objeto, chequeará pri-
mero si el tal objeto se encuentra contenido en "BloqueosActivos", y si es así im-
pedirá el uso de la función "set(...)" sobre el mismo. Haría falta, de esta manera,
codificar nuevas funciones y datos miembros en la clase base "BaseDialogo" de
la siguiente guisa:

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 153
class BaseDialogo : public Dialog {
public:
boolean estaBloqueado( BaseObjeto*, long = 0 );
private:
// colección de objetos bloqueados en
// la OODB transient (en memoria primaria)
BaseColeccion* bloqueosActivos;
// sigue resto descripción de clase
};

boolean BaseDialogo::estaBloqueado( BaseObjeto* modelo,


long insistir )
{
if (bloqueosActivos->contiene( modelo ) ) {
if (!insistir ) {
VReport::warning( "Objeto Bloqueado", this,
"Este objeto está bloqueado\npor otro usua-
rio.");
return FALSE;
} else {
// activar "timer" para que cada n segundos
// reintente la llamada hasta un número x
// de iteraciones, o bien llamar de nuevo
// recursivamente a la función de chequeo de
// bloqueos hasta un cierto límite.
}
} else
return TRUE;
}

Así en cada vista, además de habilitar o deshabilitar ciertos controles o botones


en razón del acceso del usuario en cada caso pertinente (desactivar los botones
de modificación o eliminación en el caso de acceso "soloLectura" ), la mera lla-
mada a la función set(...) de cada objeto del modelo será sustituida por la siguien-
te condición:

if ( estaBloqueado ( BaseObjeto* ) )
modelo->set(...);

a la vez que en el constructor de la vista se añadirán las líneas:

// "modelo" es el puntero al objeto que gestiona la vista


if ( usuario->nivelAcceso( this ) == total &&
!estaBloqueado( modelo ) )
bloqueosActivos += modelo;

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 154
pudiéndose sustituir la doble condición por una función única en la clase base
"BaseDialogo", de la misma forma que se ha hecho con la función "estaBloquea-
do( BaseObjeto* )".

Este es, a nuestro parecer, el enfoque menos intrusivo, pues no afecta al modelo
de la aplicación, sino únicamente a las vistas que lo manejan.

Gestión de Objetos a través de Vistas


Una Vista contiene una referencia (puntero, en C++) al objeto que habrá de mane-
jar, acceder, leer o modificar, siempre mediante el interfaz público de éste. De
esta manera tenemos un código similar al siguiente:

class DialogoCualquiera {
private:
ObjetoCualquiera* punteroAlObjeto;
// resto representación interna diálogo
};

donde la lectura se realizaría siempre por medio de una función que, por razones
que se evidenciarán en el epígrafe Actualización de Vistas, denominaremos "re-
fresca":

void DialogoCualquiera::refresca()
{
// editLineN son miembros privados de tipo EditLine
// y representan campos gráficos de edición de textos
editLine1->putText( punteroAlObjeto->getAtributo1() );
editLine2->putText( punteroAlObjeto->getAtributo2() );
// etc., etc.
}

mientras que los accesos para modificación (y aquí se obviarán los aspectos re-
lacionados con la concurrencia, como se ha hecho respecto de la lectura) se codi-
ficarán en las funciones (callbacks) a ser llamadas como resultado de la acción
sobre un elemento gráfico (típicamente un botón):

void DialogoCualquiera::callbackOkButton()
{
punteroAlObjeto->set( /* actualización de atributos */ );
// termina el diálogo y pasa el objeto
// al diálogo padre
getParent()->admite( punteroAlObjeto );
}

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 155
Pero este simplificado esquema origina una grave dificultad: como se ha visto la
actualización del modelo (objeto) al que apunta cada vista (aunque también se
pueden dar vistas multimodelo, sin que en absoluto cambie el esquema de actua-
ción) se realiza mediante una función, usualmente denominada set(...), que efecti-
vamente cambia los atributos (o datos miembros) del objeto apuntado. Pero, en el
caso más general, posiblemente una vista dependa de otras vistas "padres" de
ésta, de manera que un cambio en el estado del objeto (u objetos) que la vista
maneja haya de ser validado por cualquiera de los objetos apuntados por una o
más de las vistas "padres", y si en algún caso tal validación resultara negativa, los
cambios habrían devenido irreversibles, por lo que el sistema se encontraría con
un cul de sac de sesgada solución. Imagínese, por ejemplo, que una Vista es el
interfaz de una colección de HombresCuyoNombreEmpiezaPorX, y que de tal
deriva (o se ahija) una vista que modela objetos del tipo HombreCualquiera. Si en
esta última vista se cambian los atributos de un determinado HombreCualquiera,
perteneciente originariamente a la primera colección, y el nuevo atributo de nom-
bre, ya irremediablemente cambiado, no empieza por X, la vista que accede a la
colección rechazará la inserción o actualización de tal objeto en la colección y no
podrá, por otro lado, recuperar el antiguo nombre, produciéndose un deadlock
(bloqueo mortal, en la más pura tradición hollywoodense) cuya posible resolución
será naturalmente oscura para el usuario. ¿Cómo evitar esto? Pues establecien-
do un mecanismo que permita el "backtracking" o vuelta atrás a partir de una mo-
dificación de atributos (esto es, un acceso de tipo escritura, siguiendo la más tra-
dicional nomenclatura de gestores de bases de datos). Tal mecanismo puede
implementarse, básicamente, mediante las tres formas distintas siguientes (o, por
supuesto, por cualquier combinación de ellas):

• versionamiento (versioning)
• transacciones anidadas (o bloqueos anidados con roll-back)
• copias temporales de trabajo

Las transacciones anidadas no son de aplicación, a estos solos efectos, en un


entorno multimodal, pues dado que el focus puede traspasarse de una vista a otra
dejando a aquélla activa ab aeternum, las transacciones (o cualquier otro esque-
ma de bloqueo, conjuntado o no) deberían mantenerse durante más tiempo del
prudentemente deseable, y además hacerse extensivas a los hijos de una vista
dada, incluyéndolas normalmente como inicio en el constructor de la vista y como
final en el destructor de la misma, pues tal sería la única forma de que en un mo-
mento dado se pudiera realizar un "roll-back" y devolver los objetos a su estado
anterior a las modificaciones realizadas. De hecho, el sistema de bloqueos glo-
bales desde vistas no puede funcionar bien en un esquema multimodal, y su uso
industrial se restringe, salvo expresas necesidades, a los entornos modales, de
previsible secuencialidad.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 156
El versioning representa un esquema general que incluye, como concreción, el de
las copias temporales de trabajo y, por tanto, parece el más adecuado para ges-
tionar "versiones temporales" de objetos, que en cualquier momento pueden "ol-
vidarse" (forget) o, más bien, sustituir de forma efectiva a la anterior (aunque sin
perder la información de partida). El versioning, empero, necesita de unos gesto-
res típicamente imbricados con bases de objetos, de manera que no es natural-
mente accesible desde el modelo relacional, y a pesar de resultar la opción más
elegante, clara y rápida, como quiera que obligaría al uso de determinadas herra-
mientas de implementación y restringiría, a efectos prácticos, los resultados del
diseño, hemos optado por considerar como eminentemente práctica la última op-
ción: copias temporales.

¿Cuál es el funcionamiento de las copias temporales, cuándo tal esquema ha de


aplicarse y qué objetos lo gestionarán? Vayamos a ello:

Únicamente hace falta una copia temporal por cada objeto que se esté manejan-
do en un momento dado, lo que equivale a afirmar que se necesita, al menos, una
copia temporal de cada objeto accesible desde cualquier vista activa. Esto inme-
diatamente sugiere que no es responsabilidad en sí del objeto original mantener
un tal sistema temporal, sino más bien de las vistas que actualmente lo acceden.
De esta manera resulta que será la vista la que, en una secuencia más de su
construcción (y nótese que estamos siempre hablando de vistas no-persistentes)
creará un objeto temporal del mismo tipo de cada objeto que maneja y copiará en
él sus mismos atributos (presumiblemente, en C++, mediante un constructor de
copia o por la sobrecarga de un operador de asignación, o por la aplicación de
una función virtual de clonado). El código sería el siguiente:

DialogoCualquiera::DialogoCualquiera()
{
// asigna valores iniciales a atributos de la vista
// seguidamente crea un objeto temporal
punteroObjetoTemporal = punteroAlObjeto->clone();
// sigue resto constructor vista
}

De esta manera se sobreentiende que cada vista poseerá como atributos (como
datos miembros privados, en C++), aparte del puntero a los objetos que maneje,
de acuerdo con el paradigma MVC, sendos punteros a los objetos temporales
que se clonen a partir de éstos, de forma que puedan ser accedidos desde cual-
quier lugar dentro del protocolo de descripción de la clase y con una formalización
similar a la siguiente:

class DialogoCualquiera {
private:
BaseObjeto* pObjetoOriginal;
BaseObjeto* pObjetoTemporal;

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 157
// sigue resto descripción clase
};

Pero esto plantea un nuevo problema de eficacia: una copia por objeto es razo-
nablemente efectiva y no supone penalización en tiempo de ejecución, siempre
que el objeto a copiar no sea de tipo agregado. Esto es, el sistema de copia fun-
ciona mientras que se aplique a objetos simples y no a colecciones, pues en és-
tas se produciría una copia de cada uno de los objetos contenidos en la colec-
ción, y tal es simplemente aberrante. ¿Quiere esto decir que, al no usar de obje-
tos temporales, no hay posibilidad de roll-back en colecciones? ¡Efectivamente!
Y, de hecho, éste es el esquema general normalizado en entornos gráficos ( y
bien se dice general porque podría darse el caso de una aplicación concreta del
esquema de copia en colecciones de número determinado y corto de elementos,
pero tal no sería sino lo mismo que aplicar las copias a una secuencia también
bien determinada y corta de objetos manejados por una vista). En resumen: las
copias temporales sólo se aplicarán a objetos simples de tipo no-agregado. Hay
que tener en cuenta, no obstante, que usualmente una vista manejará un solo ob-
jeto del modelo, de forma que las colecciones las manejarán vistas especiali-
zadas, mientras que el acceso a los objetos concretos contenidos en la colección
se realizará mediante vistas también especializadas para los mismos: no hay,
pues, solapamiento de esquemas. Cada vista conoce, pues, por su propia espe-
cialización, si ha de aplicar o no el esquema de copia.

Situándonos de nuevo en la copia temporal de objetos simples (no-agregados ó


no-colecciones), ¿cuál será el esquema de modificación de un objeto dado? Co-
mo anteriormente vimos, la lectura de los atributos del objeto a manejar (y nótese
que el singular se aplica aquí como mera salvaguardia pedagógica) se realiza
mediante una función virtual refresca() de la vista, que simplemente lee tales atri-
butos a través de funciones miembros del objeto accedido mediante su puntero,
dato miembro privado de la vista, y los copia, usualmente como cadenas de ca-
racteres, en los controles de la vista (típicamente editlines, editboxes, combo-
boxes, etc.). El usuario con perfil suficiente podría, a partir de aquí, modificar las
cadenas de caracteres de los controles, previendo, de forma transparente, una
modificación de los atributos del objeto de los que tales son espejo. Naturalmente
tales modificaciones en los atributos habrán de producirse en el Objeto Temporal,
y nunca directamente en el Objeto Original, pero caben dos distintos esquemas:
bien que tras la edición de cada control se produce un cambio en el atributo afec-
tado, bien las modificaciones se producen de forma global al accionar un deter-
minado control (típicamente el botón "aceptar" u "OK"): ¿cuál aproximación es la
idónea? La primera de ellas necesitaría que el objeto dispusiera de una función
miembro pública específica para la modificación de cada atributo y generaría un
inelegante y complejo rosario de funciones, a la vez que supondría captar, en un
prolijo esquema, las pulsaciones de teclas, los cambios de focus, etc., de manera
que las actualizaciones se realicen de la forma intuitiva esperada; la segunda, en
cambio, necesitaría del objeto a modificar únicamente una función pública, usual-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 158
mente denominada set(...), que sería llamada tras pulsar el botón adecuado, co-
mo por ejemplo:

void DialogoCualquiera::aceptar()
{
pObjetoTemporal->set( atributo1, atributo2, atributo3 );
// ...
}

la cancelación, por otro lado, únicamente causaría la salida del diálogo sin haber
llamado a tal función. Parece evidente, así, que la modificación de atributos resul-
ta más elegante, clara e intuitiva mediante el esquema de modificación global.
Pero si la modificación se produce al final, y piénsese que el comportamiento es-
perado (que no normalizado, desafortunada o afortunadamente) de un típico bo-
tón "aceptar" es que la vista se cierre, ¿por qué no aplicarla directamente sobre
el objeto original? Pues, básicamente, porque el proceso de validación depende-
rá, de acuerdo con los presupuestos arquitectónicos en que se basa la presente
aplicación, del objeto, jerárquicamente anterior, que posibilita la modificación de
éste: se aplica, así, el comportamiento notado anteriormente sobre colecciones y
reflejado en el ejemplo "HombresCuyoNombreEmpiezaPorX". Pero, si no se pro-
duce validación, ¿qué ocurre cuando se cierra el diálogo? Pues que tal vista (diá-
logo) habrá de "enviar" a la vista "padre" desde la que se generó el par compues-
to por el ObjetoOriginal y el ObjetoTemporal, de forma que ésta, tras acceder al
modelo al que a su vez apunta, pueda decidir si se produce o no actualización de
los atributos conforme a lo requerido por el usuario, o más bien se cancela tal
modificación y se notifican al usuario los problemas encontrados. En C++, la codi-
ficación se parecería a la siguiente:

void DialogoCualquierHijo::aceptar()
{
pObjetoTemporal->set( /* lista de atributos */ );
// getParent() devolvería un puntero a la vista padre
// mientras que parDeObjetos(...) sería, típicamente,
// la instanciación de una plantilla del tipo
// par< class T, class T >,
// o también una colección de dos elementos
getParent()->admite( parDeObjetos( pObjetoOriginal,
pObjetoTemporal ) );
}

mientras que en la vista padre encontraríamos:

void DialogoCualquierPadre::admite( ParDeObjetos* par ) {


// compara los objetos contenidos en el par:
// si ambos son iguales (lo que significa
// que no ha habido modificación)
// o la copia está en blanco (se ha producido

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 159
// una cancelación)
// entonces simplemente no hace nada
// y luego destruye el temporal
if ( !( par.sonIguales() ) &&
!( par->getCopia()->esBlanco() ) )
// si los objetos son distintos entonces
// chequea la validez de las
// modificaciones realizadas en el temporal (o copia)
if ( chequea( par->getCopia() ) )
// y si el chequeo es válido, entonces
// copia los atributos del objeto
// temporal en el original (suponemos definido el opera-
tor=)
getOriginal() = getCopia();
// se destruye después el objeto temporal
delete getCopia();
// seguidamente realiza con el objeto original ya modifica-
do
// las acciones pertinentes, como por ejemplo su inserción
// en una colección dada, de la forma:
// miColeccion |= getOriginal();
}

Como fácilmente se aprecia, buena parte del código (y por ende, del comporta-
miento) aquí mostrado sería común para todas las vistas, de manera que se po-
dría, y de hecho así se hará, traspasar a la clase base de todas las vistas: Base-
Dialogo, mientras que cada vista poseería una función virtual chequea(...) que se-
ría llamada desde BaseDialogo::admite(...), aunque esto deberá ser examinado
con más detenimiento en tal clase base.

Incidencias de Usuarios
Toda modificación o alteración de los elementos del sistema deberá ser registra-
da, de manera que quede constancia auditable de las mismas. Esto aquí se con-
sigue merced a un esquema de gestión de lo que en adelante llamaremos inci-
dencias del sistema, o simplemente Incidencias, unido al mecanismo general de
autorizaciones de acceso imbricado en lo que se denominan Perfiles.

La asunción básica es que las eliminaciones (y aquí se trata de extracciones de


objetos de colecciones dadas, pues ya se estableció que no se producen elimi-
naciones físicas directas) se producen normalmente por desuso práctico del item
eliminado, y su aplicación por naturaleza se adscribe a la gestión de Colecciones
Activas, aunque también podrían producirse por error anterior en la creación o
inserción en una determinada colección del ítem. Las modificaciones, por otro
lado, se entenderán siempre realizadas por causa de un error anterior: esto es, en
un Sistema de Información (como pretenden ser la mayoría de aplicaciones en los
entornos comercial e industrial), los atributos de los objetos no pueden ser cam-
biados simplemente debido a una actualización de la información que soportan,

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 160
pues tal política desdibujaría la esencia básica del sistema (por ejemplo, si se
constata que una persona vive en tal domicilio, distinto de aquél que se mantenía
por el sistema, no ha de producirse una mera actualización del objeto Domicilio
referenciado por persona, sino que lo que se espera, y este es realmente el com-
portamiento de la aplicación, es que se genere una nueva relación de domicilio
con fecha (piénsese que la mayoría de la información asumida por el usuario final
como parte de sus necesidades negociales puede poseer momento cronológico
de entrada, pero difícilmente observará momento de finalización: es normalmente
constatar la caducidad de una determinada información). Este esquema de
modificación por error resulta en que cada modificación (y una eliminación se
entiende como tal) generará una Incidencia que recogerá el objeto Original, el
objeto Modificado (ambos encapsulados en un objeto de tipo ParDeObjetos), el
momento de la Modificación y un posible comentario, automático o no, razonando
la incidencia:

class IncidenciaAuditable {
friend class DlgIncidenciaAuditable;
public:
IncidenciaAuditable();
IncidenciaAuditable( ALTiempo*, Usuario*,
ParDeObjetos*, BaseString* = 0 );
~IncidenciaAuditable();
void dialoga();
private:
ALTiempo* crono;
Usuario* usuario;
ParDeObjetos* parDeObjetos;
BaseString* comentario;
set( ALTiempo*, Usuario*,
ParDeObjetos*, BaseString* = 0 );
};

Las llamadas explícitas generadoras de la Incidencia serán implementadas por el


equipo de programación en cada clase de tipo Vista que maneje la validación de
modificaciones (y, por ende, de eliminaciones), físicas o no, de objetos. Así, típ i-
camente, tales llamadas se darán en los objetos de tipo agregado: BaseColec-
cionesActivas, BaseColecciones, etc.

Faltaría, naturalmente, controlar el acceso a la visualización y posible corrección


(?!) de Incidencias, pero por su sencillez (se trata de una colección de Incidencias
desde la que se accede al interfaz propio de cada una de ellas, siguiendo el es-
quema tantas veces repetido en la presente arquitectura, permitiendo el acceso
únicamente a usuarios con cierto perfil) y en aras de una seguridad inicial neutra,
se deja su implementación al criterio del equipo de programadores. He aquí un
ejemplo de tal interfaz simple:

{bmc incidaud.shg}

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 161
Actualización de Vistas
Se trata de que al modificar un objeto, siempre a través del interfaz público de
éste (usualmente mediante la función set( ... )) y mediante mensajes dirigidos a
través de la correspondiente vista que lo referencia, el resto de vistas deberían
ser convenientemente actualizadas para reflejar tales cambios, provocados por el
mismo usuario o por otros en otros terminales. Examinemos estos dos casos por
separado, pues cada vez que una vista expresamente realiza una modificación en
un objeto (agregado o no), se pueden seguir dos políticas de actualización del
resto de las vistas, dependiendo de las capacidades del entorno en que el siste-
ma se asiente:

• local: las modificaciones sólo se comunican a las vistas del terminal que
directamente las generó. El resto de los posibles terminales opera con
vistas desactualizadas en tanto no se genere un refresco de éstas debi-
do a una condición de salto (bien mediante un timer, bien mediante che-
queos funcionales de atributos). Naturalmente el acceso desde uno de
tales terminales a un objeto modificado con posterioridad a su lectura
resultará en que el usuario trabaje con información distinta de la real, de
manera que puede llegar a modificar el objeto por el solo hecho de pul-
sar el botón aceptar con la información antigua. La ventaja, por otro
lado, es que no se producen largas secuencias de inactividad debido a
las modificaciones inter-usuarios en un entorno medio-grande.

• cliente-servidor: se requiere al servidor para que notifique las modifica-


ciones a todos los clientes. Esto origina que todos los terminales son
actualizados tras una modificación. Esto significa, también, que el focus
en cada terminal irá variando en razón de tales refrescos, y causará re-
trasos de difícil explicación intuitiva, a la vez que perjudicará la efectivi-
dad global del sistema. Una solución práctica sería, naturalmente, man-
tener colecciones de asociaciones de objetos con las vistas que debe-
rían ser actualizadas, pero este esquema obligaría a mantener meca-
nismos independientes de validación de vistas.

En las presentes notas nos decidimos por el primer caso, la actualización local,
matizada por un mecanismo que impediría el solapamiento inadvertido de modifi-
caciones en objetos. Pero antes de nada examinemos lo que conlleva la modifi-
cación de objetos.

Existen dos tipos básicos de modificaciones, cualificadas en razón del tipo del
objeto a modificar: las de objetos simples y las de objetos de tipo agregado o
colecciones. En el caso de colecciones, las modificaciones se refieren a la varia-
ción de la estructura interna que las compone: esto es, a la adición y extracción
de elementos, junto con la creación y destrucción de objetos del tipo de la colec-
ción precisamente. Consideramos, pues, que la modificación de la representa-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 162
ción interna de un objeto perteneciente a una colección se adscribiría al primer
caso, en su propia calidad de objeto simple. Examinemos ambos casos:

• Modificación de una Coleccion: como se detalla en el epígrafe Gestión


de Concurrencias, en ningún caso la modificación de una colección pue-
de producir el inmediato borrado físico de un ítem contenido en la
misma, sino únicamente su extracción y, en algunos casos, reposiciona-
miento en otras colecciones. La eliminación visual de un ítem originaría,
pues la colección efectivamente habría cambiado, un mensaje de re-
fresco local que llamaría iterativamente a las funciones refresca(...) de
cada vista que gestione una colección, de manera que se produzca una
nueva lectura de la colección y se traspasen los punteros a los objetos a
la colección intermedia, para desde aquí actualizar la vista (usualmente
el combobox o listbox que contiene las cadenas de caracteres que re-
presentan los objetos). Como quiera que el objeto permanece persisten-
te, las relaciones de referenciación por parte de otros objetos permane-
cerán, tras el borrado lógico, invariantes y estables. En el caso que se
produzca una inserción de un objeto en una colección, el procedimiento
sería el mismo detallado, procediéndose a la relectura de la colección y
a la inserción de lo leído en las correspondientes colecciones interme-
dias. Asimismo la modificación de los atributos de un item originaría un
refresco en la colección, pues la vista de la colección mantiene la repre-
sentación visual de los ítems que contiene, de forma que cuando, como
veremos en el párrafo siguiente, la vista que gestiona el objeto simple
contenido en la colección devuelva, en un par, el objeto modificado, si la
colección acepta tal modificación generará un mensaje de actualización
para que se refresquen las correspondientes cadenas de las vistas.

• Modificación de un Objeto Simple: siguiendo el mecanismo de copias


temporales especificado en el epígrafe Gestión de Objetos a través de
Vistas, un par de objetos (original + copia) sería devuelto al padre de la
vista, que podrá así sólo validar los cambios o también, y en una se-
cuencia inmediatamente posterior, efectuar modificaciones en el objeto
al que tal vista "padre" apunta (verbigracia, mediante la inserción de tal
objeto en una colección, etc.). Esto significa que cada vista deberá de-
cidir si manda directamente mensajes de actualización o si los delega
en vistas "padre". El enfoque aquí adoptado es local-local: esto es, las
vistas que manejen objetos simples no generarán ningun mensaje direc-
to de actualización tras una modificación, sino que simplemente pasa-
rán el control a su ventana padre (piénsese que el comportamiento es-
perado en un entorno gráfico es que, tras aceptar la información a modi-
ficar, la vista afectada se cierre y se traspase el focus a la vista padre
de ésta), y ésta decidirá, tras chequear en su caso si las modificaciones
realizadas son válidas, si envía o no mensajes de actualización al resto
de las vistas del mismo terminal. Se plantean aquí varias cuestiones:

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 163
¿cómo determinar qué vistas manejan objetos simples y cuales no?
¿qué tipo de mensaje de actualización hay que envíar y cómo habrá que
preparar a las vistas para que respondan al mismo? ¿Se dará algún
esquema de optimización de actualizaciones, a fin de no mandar el
mensaje de refresco a vistas que no lo necesiten? Bien, vayamos por
partes.

Existen dos tipos de refrescos posibles: el selectivo y el global. El selectivo obliga


a establecer de antemano asociaciones del tipo uno-a-muchos respecto de los
tipos de un BaseObjeto y varios BaseDialogos. La implementación se realizaría
creando objetos del tipo actualización:

class Actualizacion {
public:
// constructor y destructor
Actualizacion( const TipoBaseObjeto&, BaseColeccion* );
~Actualizacion();
// objeto
TipoBaseObjeto getTipoObjeto();
// llama iterativamente a la función refresco
// de cada una de las vistas del terminal activo
// cuyo tipo coincida con alguno de los contenidos
// en la colección de tiposVistas
refresca();
protected:
private:
TipoBaseObjeto tipoObjeto;
BaseColeccion* tiposVistas;
};

Pero este esquema obligaría a implementar un sistema estático de reconocimien-


to de tipos, a la vez que forzaría una dependencia entre vistas y objetos no explici-
tada en el diseño.

El refresco global simplifica mucho la implementación, actuando de la siguiente


manera: cuando una vista desea mandar un mensaje de actualización simplemen-
te envía un mensaje de refresco a la Vista Principal de la aplicación, y ésta, itera-
tivamente, porque todo padre conoce a sus hijas, lo aplica a cada una de las vis-
tas que de ella dependen. El código resultaría de la siguiente guisa:

class VistaPrincipal {
public:
static void refresca();
// sigue resto descripción clase
};

class BaseDialogo {
protected:

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 164
//llama a VistaPrincipal::refresca();
virtual void actualiza();
// implementa polimorfismo en refrescos
virtual void refresca() {};
// sigue resto descripción clase
};

La clase BaseDialogo es la clase base de todos los diálogos de la aplicación,


por lo que todos ellos podrán usar de la función de actualización. A la vez imple-
menta la funcionalidad básica de refresco a través de la función "refresca()", que
será redefinida en las clases derivadas de BaseDialogo y que siempre equival-
drá a una lectura del objeto u objetos a los que la vista en cuestión apunta. De
esta manera, en una clase derivada de BaseDialogo, como por ejemplo:

class EmpleadosActivosDlg : public BaseDialog {


private:
BaseColeccionActiva* empleados;
BaseColeccionIntermedia* empleadosTemporal;
void refresca() {
// lee los ítems de la colección de analitos
// y los sitúa en una colección intermedia,
// para después (o a la vez) insertar las
// cadenas que los representan en un combobox
}
// sigue resto descripción clase
};

Podría argumentarse que el refresco global resulta demasiado costoso, pues no


incluye optimización ninguna: todas las vistas son actualizadas. No hay que olvi-
dar, empero, que siguiendo un esquema local-local, solamente se actualizan las
vistas en un único terminal y que el proceso será tanto más costoso cuantas más
vistas estén disponibles en un terminal en un momento dado, pero ésta es una
opción que sólo posibilita la arquitectura multimodal, por lo que los beneficios
compensan de forma clara los posibles defectos. Se ha comprobado, por fin, que
la mayoría de usuarios de este tipo de sistemas mantienen un número relativa-
mente bajo de vistas abiertas a la vez. La simplicidad del mismo lo hacen, por fin,
particularmente adecuado para su implementación en un primer proyecto orienta-
do-a-objetos, cual es el presente, sin olvidar que las políticas de optimización
pueden aplicarse en una segunda fase, pues las modificaciones que habría que
realizar serían únicamente las siguientes:

class BaseDialog {
private:
void actualiza( BaseObjeto* );
// sigue resto clase
};

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 165
class VistaPrincipal {
public:
static void refresca( BaseObjeto* );
// sigue resto clase
};

de tal manera que la función VistaPrincipal::refresca(...) actualizaría únicamente


las vistas del tipo asociado al tipo del objeto que se le pasa como argumento,
según la relación establecida en los objetos de tipo Actualización detallados ante-
riormente.

¿Se debe redefinir la función virtual refresca() en todos los descendientes de Ba-
seDialog, esto es, en todas las vistas de la aplicación? ¡No! Únicamente se pro-
ducirá tal redefinición en las vistas que gestionen objetos no simples, para evitar
así actualizaciones secuenciales masivas. La cualificación de tales vistas será
expresamente establecida por la documentación de diseño mediante la inclusión
o no de la redefinición de tal método.

Seguridad del Sistema


¿Cómo se accede a los objetos? A través del interfaz público de la clase a que
pertenecen. ¿Y cuál es tal interfaz en un entorno gráfico? ¡El representado por las
vistas, o diálogos! Esto parece querer decir que el acceso a los objetos se res-
tringe al que proporcionan los diálogos que los manejan o gestionan (ver Gestión
de Objetos a través de Vistas): como si el puro interfaz de clase de los objetos a
acceder se moldeara, restringiera o extendiera merced al interfaz propio de la
vista. Pero en realidad tal esquema no resulta tan simple. El acceso a objetos
únicamente a través de las vistas que los manejan supondría que las clases a que
aquéllos pertenecen no disponen de interfaz público de acceso alguno, pues si
así fuera el acceso se podría realizar directamente por medio de las funciones
que a tal interfaz pertenecieran. Esto es, si tenemos

class TipoDeSuceso {
public:
// establece el dato miembro "nombre"
set( const BaseString& main& );
private:
BaseString nombre;
// resto descripción clase
};

class TipoDeSucesoView {
protected:
void aceptar() {
// ...
set( const BaseString& );
// ...
}

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 166
// sigue resto descripción de clase
};

resulta que efectivamente la modificación se produce a través de la vista (del tipo


TipoDeSucesoView) y en esta pueden codificarse las restricciones de acceso
pertinentes, pero lo cierto es que nada impide que cualquier otro programador
que desee ampliar o modificar la aplicación codifique lo siguiente:

TipoDeSuceso miTipoDeSuceso;
miTipoDeSuceso.set( "Traición" );

y esto ocasionaría la evidente vulneración de la restricción de acceso a la infor-


mación que se pretendía mantener mediante la vista. Hay que pensar que las fun-
ciones típicamente denominadas "set" no suelen establecer chequeo ninguno de
validación y ni siquiera devuelven valor que permita comprobar su correcta ejecu-
ción, pues una de las decisiones tomadas en el diseño de la presente arquitectu-
ra es que "todas las validaciones de modificación de objetos se producirán en las
vistas, en su calidad de interfaces prácticos de estos".

¿Cómo tapar, entonces, este aparente agujero de seguridad? Bien, la solución


más obvia es no hacer accesible las funciones de tipo "set" más que a las vistas
que tengan que manejar los objetos, de forma que habría que eliminar tales fun-
ciones de la sección pública de sus respectivas clases. ¿Donde situarlas, pues?
¡En la sección protegida, posibilitando la modificación desde clases derivadas!
Pero esta decisión arrostra nuevos problemas: para que las vistas pudieran ac-
ceder a tales funciones de modificación se les deberían dar niveles de acceso
que sólo pueden conseguir mediante una declaración de "amistad" del tipo:

class TipoDeSuceso {
friend class TipoDeSucesoView;
// sigue descripción de clase
};

Pero este enfoque plantea dos inconvenientes: por un lado se está ligando de
forma tajante el modelo de la aplicación a una vista determinada, de manera que
si se quiere cambiar de vista (de clase de vista) se deberá modificar la clase del
modelo; por otro lado, la declaración de "amistad" no trasciende a las posibles
clases derivadas de la vista, de manera que no se podría dar un comportamiento
polimórfico inmediato en los diálogos.

Naturalmente esto tiene solución (y muy elegante, a fe mía) mediante el uso de


herencia múltiple: se trataría, así, de establecer una clase base de todos los posi-
bles diálogos (vistas) para una clase dada (del Modelo de la aplicación), de forma
que tal clase accediera, mediante una relación de "amistad" al miembro "set(...)"
protegido del modelo, dotándose de una función protegida, a su vez, de tipo "set(

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 167
tipoModelo, ... )", que podría ser utilizada por sus clases derivadas. Así, toda cla-
se de tipo diálogo debería derivar de "BaseDialogo" y también de nuestra nueva
clase "DlgBaseModelo", única para cada modelo, distinta de BaseDialogo en
tanto que no necesita de las características heredadas de la vista, sino que es un
cruce, a su vez, de funcionalidad con vista. Para intentar aclararlo, seguidamente
se ejemplifica:

class Etiqueta {
friend class DlgBaseEtiqueta;
public:
Etiqueta();
Etiqueta( const BaseString& );
~Etiqueta();
protected:
set( const BaseString& );
private:
BaseString* nombre;
};

class DlgBaseEtiqueta {
protected:
set( Etiqueta*, const BaseString& );
private:
Etiqueta* etiqueta;
};

class DlgEtiqueta : public BaseDialogo, public DlgBaseEtiqueta {


public:
// sigue resto descripción normal de clase
};

Se plantea, empero, un problema: parece que no habría ningún problema en vul-


nerar este esquema protectivo mediante la creación de clases derivadas bien de
DlgBaseEtiqueta o de Etiqueta (en este caso), desde las que se podría acceder
a la función set(...) cuyo acceso se intentaba restringir. Bien, esto sería así si se
permitiera a cualquier programador insertar sin traba alguna un nuevo modulo en
la aplicación y enlazarlo en el ejecutable. La solución es, naturalmente, impedir
esto mediante la creación de una lista jerárquicamente enlazada de objetos está-
ticos de cada una de las clases de la aplicación, de forma que la creación de
nuevos objetos se produzca mediante la llamada a una función polimórfica de
clonación ( virtual CLASE* clone() ). Esta solución está perfectamente detallada
en la obra de James O. Coplien "Advanced C++ Programming Styles and Idioms"
y se denomina de "Constructores Virtuales".

Existe, pues, una opción óptima de seguridad arquitectónica para el sistema, pe-
ro quizá no sea la opción más adecuada para un primer proyecto de OOP/C++.
De esta manera, nuestro consejo es que se utilice un esquema simple inicial,
donde las funciones "set(...)" sean públicas en el modelo, y se acceda a ellas a

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 168
través de las vistas (cualesquiera que éstas sean) que lo manejen. La seguridad
se daría, así, a nivel de usuario y no a nivel de programación, según lo ya expre-
sado, pero este enfoque permite pasar, en futuras versiones (2.0, 3.0 o aun
6.3.7.4.9) al ya expresado de "Constructores Virtuales", sin apenas tener que va-
riar la codificación (únicamente habría que cambiar las llamadas a los constructo-
res por llamadas a la función de clonación usando del RTTI -RunTime Type Identi-
fication de C++. Seguidamente se podría pasar al esquema de herencia múltiple
si así se desea, aunque, como ya se ha expresado en otra sección, en la presente
arquitectura sólo se está usando, en aras de la simplicidad, de la herencia simple.

Según lo expuesto, queda nuestro consejo en concentrar la codificación en la se-


guridad a nivel de usuario. Como quiera que todas las vistas contienen un puntero
al objeto que manejan, tienen evidentemente acceso a la función set(...) (pública
en nuestro caso) de la clase del mismo, de forma que podrán modificarlo a volun-
tad, con las únicas restricciones y condiciones impuestas por la propia vista y las
anteriores que produjeron su llamada (piénsese en ColeccionesActivas, etc.).
Queda, empero, por resolver los filtros de acceso de usuarios: esto es, no todos
los usuarios deben tener acceso a toda la información, ni de la misma forma. Va-
yamos a ello.

Obviando esquemas polimórficos demasiado generalistas y, por ende, endiabla-


damente complejos, atenderemos únicamente a una solución práctica: la creación
de perfiles de usuario, con tres únicos niveles de acceso para cada una de las
vistas de la aplicación:

sin acceso (sinAcceso)


lectura (soloLectura)
escritura (total)

De esta manera, aparece una clase nueva:

class Usuario {
friend class DlgUsuario;
private:
enum Acceso { sinAcceso, soloLectura, Total };
PersonaFisica* persona;
BaseString* nombre;
BaseString* contrasena;
// Colección de pares de objetos donde uno de ellos
// es la clave y el otro el objeto asociado.
// (la class VDictionary existe en C++/Views).
// En nuestro caso la clave sería un objeto de tipo
// vista (derivado de BaseDialogo), sobre el que se
// aplicaría el mecanismo RTTI para identificarlo,
// mientras que el objeto asociado sería el enumerador
// de Acceso.
// El interfaz del VDictionary sería moldeado a través

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 169
// de la vista DlgUsuario (clave también del mismo).
VDictionary* accesos;
};

Para saber qué usuario está activo en cada momento, a la clase ApplicationView
deberá serle añadida una función del tipo

ApplicationView::setUsuario( Usuario* );

a la vez que se añadirá un parámetro de tipo "Usuario*" al constructor de Applica-


tionView:

ApplicationView( Usuario* = 0 );

y se chequeará en su cuerpo que si el puntero a Usuario es nulo habrá de exigirse


la introducción del nombre de un usuario y de su contraseña, que se almacenarán
como un nuevo dato miembro de ApplicationView, con una función de acceso de
la siguiente guisa:

class ApplicationView {
private:
static Usuario* usuario;
// sigue resto descripción sección privada
public:
static Usuario* getUsuario();
// sigue resto descripción sección pública
};

Dado, por otro lado, que todos los diálogos de la aplicación derivan de BaseDia-
logo, habría que hacer una modificación leve en el comportamiento de éstos:

1) añadir al constructor de BaseDialogo, y por tanto al de todos los diálo-


gos, un parámetro adicional de acceso, que por defecto será "sinAcceso":

MiDialogo( MyWindow*, MyFont*, Acceso = sinAcceso );

de forma que cuando se cree un nuevo diálogo sin asignación expresa de


nivel de acceso, no tenga acceso en absoluto. En realidad no haría falta in-
cluir tal parámetro adicional a menos que se desee controlar el nivel de
acceso diferencialmente desde las clases derivadas de BaseDialogo
(bastaría únicamente, en otro caso, con establecer tal nivel de acceso en el
cuerpo del constructor de BaseDialogo).

2) añadir código en el constructor de BaseDialogo (que siempre se


ejecutará para cada diálogo, como clase base en la inevitable secuencia-
ción de constructores) para que se modifique el nivel de "sinAcceso" al

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 170
constructores) para que se modifique el nivel de "sinAcceso" al correspon-
diente para tal diálogo al usuario a obtener desde "Application-
View::getUsuario()".

En cuanto al interfaz de la clase Usuario, bien, se seguirá el proceso típico: ini-


cialmente habrá un usuario "Supervisor" con contraseña nula a través del cual se
podrán crear nuevos usuarios y asignarles niveles de acceso. ¿Cómo acceder,
sin embargo, a la lista actualizada de vistas disponibles en la aplicación? La so-
lución óptima sería el uso del esquema de "Constructores Virtuales" notado ante-
riormente, pues un recorrido por la cadena de objetos estáticos devolvería, usan-
do de RTTI, el tipo de cada vista disponible. En caso que, como se aconseja, no
se implante tal sistema inicialmente, la lista habrá de ser forzosamente estática y
compuesta expresamente. La garantía, a este respecto, es que una vista que el
programador no incluya en la lista no podrá ser accedida por ningún usuario; la
desventaja es que habrá que asignarle al usuario Supervisor expresamente el
acceso total a cada nueva vista introducida. De cualquier forma es el enfoque re-
comendado para esta primera aproximación. El interfaz gráfico, en sí, vendrá da-
do por una lista de usuarios activos (una BaseColeccionActiva) a través de la que
se accederá a un diálogo DlgUsuario, compuesto por un campo de edición para
nombre, un campo de edición secreto para contraseña y un listbox con los nom-
bres de las vistas de la aplicación, donde cada una se asociará con un radiobut-
ton de un grupoExclusivo (sinAcceso, soloLectura, Total). Tanto este sistema de
interfaz como el de IncidenciasAuditables pertenece a la zona que podríamos de-
nominar "de Sistema" de la aplicación, y que no se debe modelar en el prototipo.

El sistema de seguridad propuesto no interfiere en el sistema de menús, pues


siempre aparecen todas las opciones, modelando únicamente el acceso a las
vistas. Una clara consecuencia de este enfoque es que un determinado nivel de
acceso en un diálogo originará que los diálogos hijos de éste mantengan el mis-
mo nivel de acceso.

Gestión de Objetos mediante Colecciones


Así como en las RDB (Bases de Datos Relacionales) los registros (como equiva-
lente a objetos), que contienen campos (como equivalentes a atributos), están
dispuestos en tablas, en las OODBs (Bases de Datos Orientadas a Objetos) no
existe unanimidad en cuanto al trato de equivalencia a dar a las tablas, aunque en
ODMG-93 (el estándar de facto en OODBs) y en la mayoría de productos comer-
ciales se mantienen conjuntos de objetos de un mismo tipo que se denominan
"extents". Cada extent englobaría a todos los objetos de una misma clase (persis-
tentes o no). En realidad, así como en las RDBs las únicas formas de acceso son
EPs (Entry Points: puntos de entrada) cualificados como tablas, en las OODBs
los objetos pueden ser accediros via EPs arbitrarios, via queries (consultas glo-
bales) y mediante búsqueda navegacional (de hecho las OODBs son también
llamadas bases de datos navegacionales).

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 171
Nuestro enfoque se basa en crear, si es que la OODBs comercial concreta no lo
provee implícitamente, tantos EPs como clases de modelo aniden en el presente
sistema, de tal manera que cada objeto creado será automáticamente insertado
en una Colección que representará el extent de tipo del mismo. Usualmente tales
extents serán accedidos por una clave alfanumérica, representativa del nombre
de la clase de objetos que manejan. Cuando un objeto se destruya será extraído
de su extent. Se perfila así el extent como un dato miembro estático de la clase
correspondiente, conocido por todos los objetos instanciados de la misma. No se
ha modelado expresamente esta funcionalidad porque algunas OODBs incluyen
tal comportamiento sin intervención del operador. Pero aunque los extents resul-
tan eficaces, no se puede trabajar con todos los objetos todo el tiempo: piénsese
que el extent agrupa a TODOS los objetos de un tipo dado: los ya no-usados, los
temporales, etc. De esta manera, la mayoría de los objetos son modelados a tra-
vés de colecciones que denominamos ColeccionesActivas, donde se insertan los
objetos (además de en el extent, siempre) con un significado concreto para tales
colecciones. Así, por ejemplo, una colección de agentes de policía en plantilla
contendrá únicamente objetos de tipo "AgenteDePolicia" que mantengan una re-
lación actual laboral con la Policía Local, conteniendo el extent de "AgenteDePoli-
cia" la colección total de "agentes", en plantilla o no. La extracción de un objeto de
una colección activa nunca significa la extracción del mismo del extent al que, por
su tipo, pertenece. Las ColeccionesActivas pueden ser consideradas, también,
como EPs, aunque como también estarán contenidas en un extent podrían ser
seleccionadas en tiempo de ejecución en función de un parámetro adicional o de
una actuación polimórfica. Nuestro enfoque, siempre en pos de la sencillez, será
calificarlas como EPs.

{bmc alcolact.shg}

La mayoría de llamadas desde los ítems de los menús de la aplicación serán,


pues, del tipo:

void PoliciaView::itemMenuAgentesPlantilla()
{
miColeccionActiva->dialog();
}

donde el identificador "miColeccionActiva" habrá sido debidamente inicializado


bien en el constructor de la vista, bien como variable estática de la aplicación an-
tes del comienzo de ésta.

Esquema de Consultas
El esquema habitual de consultas se basa en menus ad-hoc que conducen a in-
terfaces específicos que, en base a una selección un tanto arbitraria de atributos,
procuran un resultado conceptualmente pre-determinado aunque de extensión

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 172
variable. Naturalmente cada vez que se necesita información en nueva disposi-
ción es necesario acometer la generación de nuevos interfaces y modificar los
puntos de acceso (menús, controles, etc.). El enfoque a adoptar, sin embargo, en
la presente esquematización es esencialmente genericista y de planteamiento
basado en la reutilización del software ya generado, usando de los mismos inter-
faces utilizados para la lectura y modificación de los atributos de los objetos.

Esquema Teórico

La idea básica es que las consultas de los usuarios se circunscribirán a un de-


terminado tipo de objeto (o clase), toda vez que todos los entes del dominio del
problema que cubre la aplicación, e incluso las relaciones que podrían unirlos,
están modelados como clases. Así se partirá del siguiente interfaz genérico, que
será consecuencia de la aplicación de la función virtual "dialog()" a un objeto de
tipo "Consulta":

class Consulta : public BaseColeccion {


public:
void dialog() {
new DlgConsultaSeleccion( notifier->getFocus(),
this );
}
private:
SQLString SQL;
};

{bmc querycol.shg}

sobre el que la ampliación de la selección originará la creación de un nuevo obje-


to del tipo seleccionado (de la capa del modelo), pero con un comportamiento
adicional que hasta ahora no ha sido tratado. De alguna manera se debe encap-
sular la capacidad de selección de consulta en cada clase, de forma que puedan
generarse caminos complejos de búsqueda con una delimitación clara de res-
ponsabilidades, en vez de crearlos cada vez para cubrir una necesidad específi-
ca. Para cumplir con este requerimiento se insertarán nuevas funciones en la cla-
se base BaseObjeto:

class BaseObjeto {
public:
virtual SQLString SQL( const SQLString& ) = 0;
boolean estaVacio( BaseObjeto* objeto ) {
if ( objeto && !objeto->estaVacio() )
return TRUE;
return FALSE
}
virtual boolean estaVacio() = 0;
// sigue resto descripción de clase

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 173
};

Tal nueva función, de obligada redefinición en las clases derivadas de BaseObje-


to (todas las de la aplicación, por su estructuración cósmica), será la encargada
de recorrer todos los datos miembro de la clase afectada para generar así una
porción de la condición WHERE de una cláusula SQL estándar:

class Etiqueta {
public:
SQLString SQL( const SQLString& );
private:
char* nombre;
// sigue resto descripción de clase
};

SQLString Etiqueta::SQL( const SQLString& identifier )


{
if ( !estaVacio( nombre ) )
// el operador + está sobrecargado en la clase
// SQLString (derivada de BaseString) para añadir
// si fuera necesario nexos conjuntivos (AND)
return ( identifier + "nombre = " + nombre );
}

o, en un ejemplo ligeramente más complejo y bastante autoexplicativo:

class Clase {
public:
SQLString SQL( const SQLString& );
private:
OtraClase* otraClase;
};

class OtraClase {
public:
SQLString SQL( const SQLString& );
private:
Etiqueta etiqueta;
Etiqueta* pEtiqueta;
};

SQLString Clase::SQL( const SQLString& identifier )


{
if ( !estaVacio( otraClase ) )
return ( otraClase->SQL( identifier + "otraClase->"
));
}

SQLString OtraClase::SQL( const SQLString& identifier )


{

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 174
SQLString tmp;
if ( !etiqueta.estaVacio() )
tmp += etiqueta.SQL( identifier + "etiqueta." ) );
if ( !estaVacio( pEtiqueta ) )
tmp += pEtiqueta->SQL( identifier + "pEtiqueta->" ));
return tmp;
}

que devolvería la cadena:

this->otraClase->etiqueta.nombre = NOMBRE1 .AND. /


this->otraClase->pEtiqueta->nombre = NOMBRE2

De esta manera, al codificar adecuadamente el rastreo SQL en cada clase se


posibilita la relación genérica de búsqueda entre clases. En caso, naturalmente,
que aun siendo el puntero a un objeto no nulo sus atributos no fueran sino objetos
en blanco, la función SQL() devolverá la cadena nula, y no se producirá modifica-
ción de la frase SQL, aunque lo más práctico sería usar de la función estaVacio()
que chequea cada miembro del objeto de la clase dada. Evidentemente, también,
por otro lado, la relación entre objetos susceptible de búsqueda podrá ser tan
compleja e intrincada como se quiera y, lo que es más importante, usando de los
mismos interfaces ya codificados para el uso normal de la aplicación.

Bien, el esquema secuencial es el siguiente: al seleccionar un tipo o clase de ob-


jeto en el interfaz anterior, se instancia un objeto "vacío" o "en blanco" de tal clase
y se inicializa el SQLString con la frase "SELECT clase FROM objectDatabase
WHERE" (donde "clase" es el identificador de la clase seleccionada y "objectDa-
taBase" es la base de objetos accesible como variable estática de la aplicación).
Seguidamente se produce la llamada al interfaz del objeto en forma de la función
virtual "objetoQuery->dialog()", de forma que al operar con tal interfaz se pueden
crear nuevos objetos relacionados con el primero, directa o indirectamente, que
generarán la aparición de interfaces que serán validados o cancelados, hasta que
se valide el interfaz del objetoQuery primero, y entonces, con el objeto ya montado
con los atributos de búsqueda deseados (en la relación interobjetos que se des-
ee), se llamará la función constructora de la sentencia SQL en tal objeto, pasando
como parámetro usualmente la cadena "this->" (la forma más extendida en OSQL
y sus derivados), pasándose seguidamente a ejecutar tal sentencia (presumible-
mente con un preprocesador SQL de la base comercial de objetos). El resultado
será almacenado en una BaseColeccion de Consulta y mostrada en el interfaz
anteriormente explicitado. Tal colección podrá ser ampliada (con una nueva se-
lección de búsqueda en disyunción), restringida (con una selección en conjunción,
que en la práctica sustituye la base de objetos por la BaseColeccion de resulta-
dos) o limpiada (vacíado de la BaseColeccion).

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 175
Como fácilmente se comprenderá, el objeto de tipo Consulta no necesita ser per-
sistente, aunque sí podría ser interesante (quizás para controlar la frecuencia de
ciertas consultas) hacer persistente al objeto que representa los atributos de se-
lección, de forma que esto se deja al criterio del equipo de programación, aunque
se recomienda un tal control únicamente en las fases de prueba y validación de
estabilidad de la aplicación final.

Quedan, sin embargo, algunas cuestiones por resolver. En primer lugar, ¿cómo
se accede a la colección de tipos disponibles? Aquí aparece la misma opción
discutida en otros epígrafes: bien la lista dinámica enlazada de objetos estáticos
(uno por cada tipo disponible) bien una lista estática directamente codificada pa-
ra la aplicación. Nuestro consejo es, como en otras ocasiones, empezar por la
segunda opción, pero, aun así, enhebrándola en una solución más elegante que la
de escribir expresamente en el código la lista de clases como un mero "switch":
habría que mantener la estructura jerárquica de las clases haciéndola persistente,
para lo cual es necesario crear una jerarquía de colecciones de colecciones. O
sea, habría que mantener objetos de la clase TipoEnJerarquia, relacionados por
su inclusión o no en la bolsa de tipos derivados de cada tipo.

class TipoEnJerarquia : public BaseColeccion {


public:
TipoEnJerarquia();
TipoEnJerarquia( BaseObjeto*, TipoEnJerarquia* = 0 );
~TipoEnJerarquia();
BaseObjeto* getTipo() const;
TipoEnJerarquia* getTipoBase() const;
private:
BaseObjeto* tipo;
TipoEnJerarquia* tipoBase;
// la colección heredada por derivación
// contiene objetos de clase TipoEnJerarquia
// derivados del presente:
// BaseColeccion* tiposDerivados;
};

De esta forma, una vez instanciado un objeto de cada tipo, mediante un interfaz
accesible sólo para el equipo de programación, para formar la lista necesitada de
tipos sólo habría que iterar por el grafo de relaciones de inclusión formado. Esta
solución evita tener que codificar el acceso a tipos expresamente en cada por-
ción de la aplicación que se necesite, pero tiene la desventaja, frente a la lista
dinámica de objetos estáticos con capacidad de clonación, de que no se garanti-
za automáticamente la inclusión de tipos. Se mantendría, así, una colección de
tipos empezando siempre por el propio de BaseObjeto.

En cuanto a las consultas, por otro lado, hemos visto que hay que dotar a cada
clase de un "constructor SQL" (algo susceptible, en este enfoque, de ser perfec-
tamente mecanizado a través de un preprocesador, en una aproximación similar

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 176
a la de los preprocesadores de persistencia comerciales). En un esquema similar
al de persistencia utilizado por algunas bibliotecas de clases, el encardinado SQL
se lleva hasta tratar con tipos predefinidos (char, long, etc.). Pero en realidad, ¿se
está asimilando la selección únicamente con la igualdad de atributos? ¿Qué ocu-
rre con los rangos (de fechas, de cadenas alfanuméricas, etc.)? Bien: dado que la
codificación es nuestra, podemos dotar a tales funciones del comportamiento que
en cada caso resulte más apropiado. Así, por ejemplo, en el caso de búsqueda
en objetos de tipo BaseColeccionActiva, la inserción en un tal objetoQuery de un
nuevo objeto significará, y así deberá ser codificado, que lo que se busca es una
BaseColeccionActiva (del tipo adecuado, si acaso) que contenga al menos un
ítem con los atributos especificados (este sería el caso, por ejemplo, para encon-
trar los agentes en plantilla cuya brigada sea una dada). Este enfoque amplía los
límites del problema de búsqueda, pero no soluciona la cuestión de los rangos:
¿qué hacer si deseamos a todos los empleados en activo nacidos entre el 01-01-
60 y el 01-09-64? De entre todas las posibles implementaciones, y en fidelidad a
la idea de reutilización de interfaces, examinaremos únicamente una: rangos por
interfaces.

La delimitación de rango mediante el interfaz se basa en que los controles de los


diálogos admitan la inserción de operadores de comparación y de cadenas
regulares. Así, por ejemplo, para buscar sujetos cuyo nombre esté comprendido
entre "Antonio" y "Casimiro" habría que introducir la expresión ">=Antonio AND
<= Casimiro" en la línea de edición del interfaz de sujeto correspondiente al
nombre. Igual pasaría con las fechas, que admitirían así expresiones del tipo
"<=02-09-94". Dado que la práctica totalidad de los campos de edición disponen
de scroll horizontal automático, la longitud de la cadena de comparación no
representa ningún problema. Sería, pues, el interfaz el que leería los resultados de
todos los controles y los pasaría por un analizador léxico (que se puede encontrar
ya montado en multitud de bibliotecas de clases), controlando los operadores e
interpretando las expresiones regulares encontradas (del tipo "A*"). Pero tal
comportamiento no debe darse más que cuando se esté en disposición de
búsqueda. De esta manera tenemos que el interfaz, como clase derivada de
BaseDialogo, tendrá dos posibles estados (independientemente de los niveles
de acceso explicitados en el epígrafe Seguridad del Sistema): de edición y de
consulta. En estado de consulta la validación del mismo interfaz admitirá
operadores y expresiones regulares, generando las subexpresiones SQL
correspondientes.
Esquema Recomendado

El anterior esquema teórico resulta enormemente elegante, pero supone algunas


modificaciones importantes sobre la arquitectura de la capa del modelo, y de en-
tre ellas la que supone el mantenimiento para ciertos objetos de estados internos
(datos miembros) de carácter esencialmente distinto al que su comportamiento
presupondría. Así habría que cambiar, verbigracia, la clase "Fecha" para que, en
un aparte, sostuviera una mera cadena SQL (no asimilable como fecha sin cam-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 177
bios sustanciales). De hecho tal enfoque supone un reestructuración arquitec-
tónica difícil de asumir en un primer proyecto orientado-a-objetos. ¿Cuál es, pues,
nuestra recomendación? Pues que se use la conceptualización detallada anterior-
mente, pero transladándola al interfaz. O sea: se usará el mismo esquema nave-
gacional detallado, pero tales funciones miembros constructoras de la frase SQL
estarán implementadas en la clase que defina el interfaz de acceso a los auténti-
cos objetos (esto es, en los diálogos que los manejan). De hecho bastará con
implementar un tal mecanismo navegacional genérico en la clase BaseDialogo
con las características anteriormente señaladas para la clase BaseObjeto. La
diferencia sustancial respecto del anterior planteamiento es que en el presente no
subyace ninguna representación real de la capa del modelo. Se trata, sin más, de
aprovechar el escalonamiento jerárquico de los diálogos (padre-hija) para cons-
truir las frases SQL correspondientes (naturalmente el esquema de tipos explica-
do sigue siendo válido). Esto significa, también, que no será posible una recons-
trucción navegacional automática de las frases SQL generadas (bueno, se puede
hacer, pero esto supondría que los diálogos dispondrían de analizadores léxicos
de implementación no elemental). El esquema recomendado se basa, pues, en la
siguiente secuenciación: del diálogo de Consulta básico se irán llamando a diá-
logos afectos a la modelización SQL que se pretende, con la anexión del siguien-
te diálogo auxiliar:

{bmc sql.shg}

donde aparecerán las sentencias SQL determinadas por los diálogos y que ac-
tuará como gestor de la construcción de la frase. Dado que una secuencia de ta-
les diálogos podría ser muy larga y, en principio, la cadena SQL se construiría con
la aceptación o validación de lo que en cada diálogo aparece, tal gestor dispone
de un pushbutton que automatiza la aceptación en cadena de los diálogos de
selección. Tras la construcción de la frase, el gestor será el responsable de
ejecutarla, apareciendo los resultados en el diálogo primero de "Consulta". De
esta forma la aceptación en los diálogos de expresiones regulares no presente
ningún problema, pues no habría que mapearlos a objetos reales del sistema. Lo
que se podría archivar, así, es la propia sentencia SQL dotada de un título. Este
esquema puede ser naturalmente extendido para soportar consultas genéricas
introduciendo marcas en expresiones que, en su ejecución (por compilación)
requieran la cooperación del usuario.

Gestión de Dependencias entre Vistas


Ya se ha dicho que cada vista (diálogo, ventana, ...) mantiene apuntadores res-
pecto de las vistas que ahija, de forma que un diálogo conoce qué diálogos ahija
(dependen de él) y de qué diálogo depende (cuál es su padre). Usualmente la
implementación en C++ se sostiene con una Colección Ordenada de Vistas en
cada vista más un puntero a la vista padre en cada caso correspondiente.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 178
Este esquema de dependencias supone que cuando una vista se destruye, mini-
miza o esconde, la acción pertinente es traspasada a sus vistas hijas. Natural-
mente el caso crítico es el de destrucción. Pero, ¿es siempre posible mandar un
tal mensaje a una vista actualmente con hijas? Examinemos esta cuestión desde
el punto de vista polar de la arquitectura elegida: modal o no-modal.

En el caso del comportamiento modal resulta que el focus permanece en la última


vista creada y no puede ser modificado o traspasado a otra vista por el usuario
mediante el uso del teclado o el ratón. De esta manera resulta que cuando el
usuario ocasione el cierre o destrucción de una vista, forzosamente ésta no ten-
drá hijas activas, pues en otro caso el focus estaría en la última de tales hijas, y
desde ésta no se podría cerrar una de las vistas padre. Naturalmente es posible
codificar un acceso desde una vista a su vista padre, pero tal conllevará, forzo-
samente, un chequeo expreso de las condiciones de cierre y, en general, de la
dependencia entre vistas, pues es condición en una arquitectura modal que el
flujo secuencial del programa retorne a la línea de código siguiente a aquella des-
de la que se llamó a un dialogo ahijado.

Vemos, pues, que el comportamiento modal automáticamente mantiene un con-


trol de dependencias a nivel de usuario. Vayamos ahora a lo interesante.

En aplicaciones basadas en una arquitectura no-modal completa, cual es el pre-


sente caso, cualquier vista puede ser accedida en cualquier momento, pues no
existe retención automática del focus. ¿Qué ocurre, pues, cuando se cierra un
diálogo bien para validar la información en el introducida bien para cancelar la
visión o edición del mismo, y éste posee diálogos hijos en activo (y esto se pro-
duce bien al cerrar el diálogo -a través del menú de sistema- bien al pulsar el bo-
tón "aceptar" o "cerrar" del mismo)? Pues que, por defecto, y en base únicamente
a los presupuestos que conforma la capa de la vista, se cerraran (por destruirán)
los diálogos hijos. Pero, claro, es posible que algunos diálogos ahijados se
encuentren en un estado inestable y no puedan o deban ser cerrados en ese mo-
mento. Lo que necesitamos es un esquema de reconocimiento que gestione la
acción comandada en el diálogo padre.

La cuestión es, pues, implementar en la clase base BaseDialogo un método que


podríamos denominar "compruebaHijos" y que, en definitiva, seguirá el siguiente
esquema: se recorrerá la colección ordenada de diálogos hijos y se preguntará a
cada uno (con una función virtual llamada "boolean puedoCerrar()", definida tam-
bién en BaseDialogo para proporcionar el adecuado polimorfismo) si se puede
cerrar. Si un diálogo se puede cerrar se seguirá examinando el siguiente en la
cadena de iteración. Si no se puede cerrar directamente (esto es, no está en es-
tado estable), entonces aparecerá un diálogo de usuario que preguntará algo así
como "En este diálogo se ha producido una modificación. ¿Desea salvar tal mo-
dificación? SI, NO, CANCELAR". Si el usuario pulsa "Sí" entonces se llamará a la
función de validación de tal diálogo (una típica boolean aceptar(), también defini-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 179
da en BaseDialogo), lo que ocasionará una nueva cascada de validaciones con
el mismo método, tras la cual continuará la iteración de comprobación de diálo-
gos. Si el usuario pulsa "No" entonces se llamará a la función virtual "boolean
cancelar()" que simplemente eliminará las modificaciones realizadas en el interfaz
y ocasionará, de nuevo, una cascada de reconocimientos tras la cual seguirá la
iteración principal. Si el usuario, finalmente, responde "Cancelar", entonces sim-
plemente se cancelará la iteración principal en tal punto. Esta es la secuenciación
más elemental y permite una rápida y segura implementación. Pero examinemos
la función de validación individual de diálogos.

La función "boolean puedoCerrar()", también conocida en muchas bibliotecas


como "boolean canClose()", admite, en esencia, dos posibles implementaciones:
la estática (de identificadores -flags-) y la dinámica. El uso de flags supone que la
clase de la vista mantiene un control estricto sobre las modificaciones realizadas
en los controles visuales que la componen. De esta manera, por ejemplo, un pro-
cesador de textos sabe, captando las pulsaciones de teclas, si un texto ha sido
modificado (aunque una posible modificación deje el texto en un estado idéntico
al inicial). Así habría que captar todos los eventos (perdón: mensajes) que el usua-
rio dirigiera al diálogo y, en base a la repercusión de aquéllos en éste, establecer
el valor lógico de un identificador de modificación (originariamente FALSE). Natu-
ralmente la función "puedoCerrar" devuelve el valor lógico de tal identificador. El
enfoque ideal, si se adopta el esquema estático, es crear nuevas clases deriva-
das para todos los controles que contengan tal identificador (las clases de edición
de texto suelen ya contar con él en la mayoría de las bibliotecas), de tal manera
que la función "puedoCerrar" chequee, en vez de un único identificador por diálo-
go, que ninguno de los identificadores de los controles (accedidos como ventanas
hijas del diálogo por iteración) ha cambiado. La implementación dinámica, por
otro lado, se basaría en la construcción, con la información del interfaz, de un ob-
jeto del tipo del que se accede (en realidad se trataría de rellena, con la informa-
ción del diálogo, el objeto "copia" del par de objetos que maneja la vista, como se
detalla en el epígrafe Gestión de Objetos a través de Vistas), para luego compa-
rar (presumiblemente con el operador de comparación) el objeto "original" con tal
copia.

¿Cuál es el enfoque que recomendamos? Pues, como siempre, el más sencillo y


eficiente: una mixtura de ambos. Se trata de que la función virtual "puedoCerrar"
se implemente en cada diálogo de la forma más efectiva. Si un diálogo es visual y
formalmente simple, entonces quizá el sistema dinámico sea más efectivo, mien-
tras que si el diálogo contiene controles con gran cantidad de información (como
editores de texto, etc.), el sistema estático será más adecuado.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 180
GESTIÓN DE PROYECTOS
10
EL SÍNDROME DE ESTOCOLMO

Es opinión antigua, firme y consensuada que “no existe una bala-de-plata” en re-
lación con la OT32 (o sea, que la panacea software universal sigue estando en el
terreno de la alquimia), pero también se acepta de forma unánime que la OT pro-
porciona ventajas evidentes en el desarrollo genérico de software. Naturalmente
lo más fácil es pensar que los “nuevos” métodos que la OT propugna no van a
facilitar el trabajo diario “real” en empresas y departamentos de desarrollo de
software, pero ocurre que los que accidental o incidentalmente caen en ellos pron-
to incuban lo que los no-convertidos denominan un OOSS (Object-Oriented Stoc-
kholm Syndrome) y que resulta en una persistente y malsana atracción por las
nuevas técnicas. Los atractivos de los “objetos” aparecen innegables y, de acuer-
do con Lorenz, las siguientes etapas personales se cubren de forma inexorable:

• Novicio (3-6 meses): se mixtifica el código eminentemente funcional


modificándolo y añadiéndole porciones significadas en objetos.

• Aprendiz (3-6 meses): aquí se produce el “despertar” del que hablan los
textos de Zen, se ve el verdadero significado de la orientación-a-objetos
y se empiezan a bosquejar diseños pertinentes.

• Postulante (6-18 meses): los conceptos ya se aplican con soltura en el


desarrollo, pero todavía se dan algunos problemas con el modelado.

• Experto (guru): todo son objetos, y el sujeto se pregunta: ¿cómo pude


pensar antes de otra manera? Este estadio no siempre se alcanza, na-
turalmente (afortunadamente, según algunos).

32
OT es la abreviatura inglesa de Object Technology: Tecnología de Objetos. TO suena dema-
siado castizo.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 181
Bien: todo esto, unido a la gran profusión de noticias sobre objetos en la prensa
especializada, resulta muy emocionante y atractivo, pero en las empresas necesi-
tan de métodos fiables, estables, bien documentados y a los que dé soporte un
buen conjunto de soluciones software desarrolladas con ellos. Hay, pues, que
examinar el estado de la OT para decidir. Pero aquí empiezan -o siguen- los pro-
blemas.

ANÁLISIS Y DISEÑO ORIENTADOS-A-OBJETOS

Si echamos una ojeada al actual panorama de análisis y diseño orientados-a-


objetos nos encontraremos con pocas metodologías y muchos métodos, buena
parte de los cuales observan un carácter básicamente “propietario”, siendo muy
pocos los que cubren el ciclo completo de vida del software. Aparecerá claro, a la
vez, que existe un escaso interés, ajeno a las cuitas comerciales, en estandarizar
los métodos: es más: cuando el OMG (Object Management Group), en un intento
normalizador, requirió la colaboración de los autores de métodos de OOA/OOD,
un nutrido e importante grupo de éstos replicó con una carta pública en la que
afirmaban que tal estandarización no era en absoluto deseable. Parece que es
ridículo pensar que existe un método orientado-a-objetos bueno-para-todo, como
es risible afirmar que no existe ningún hombre absolutamente estúpido. Se apre-
cia, también, la difuminación del hasta ahora habitual gap semántico entre análi-
sis y diseño: pero aun esto resulta turbador, pues la planificación práctica usual-
mente necesita de fases claras que permitan la evaluación de resultados y los
pertinentes controles de integridad y estabilidad. Las grafías, por último, resultan
sospechosamente parecidas entre sí, y oscilan entre la simplicidad sospechosa y
la prolijidad culpable.

Tras todo lo anterior no dejen que el tono les intimide: la orientación-a-objetos -de
la que el autor es firme defensor- realmente “funciona”, y ya existe un importante
acervo práctico de soluciones software basadas en análisis y diseños orientados-
a-objetos. Pero también existe un nada despreciable anecdotario de fracasos y
despropósitos. La realidad es que el camino de la productividad, como el del in-
fierno, está fatalmente empedrado de ilusiones e intentos aventurados. Necesi-
tamos alguna luz. Intentemos una revisión de los métodos comerciales.

MÉTODOS COMERCIALES DE OOA/OOD

Es opinión extendida que en la arena de los métodos OOA/OOD existen dos co-
rrientes principales, dividiendo a estos en:

• estáticos (enfocados-a-datos), en los que lo importante es la estructura


de datos anexa a los objetos y las operaciones que sobre ella operan.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 182
• dinámicos (enfocados-a-comportamientos o enfocados-a-
responsabilidades): un objeto tiene sentido en estos métodos cuando
exhibe un comportamiento diferencial respecto del resto de los objetos.
Tal comportamiento puede referirse bien al objeto en sí (los cambios
que pueden operarse en su representación interna) bien a sus relacio-
nes con otros objetos.

Pero la verdad es que ésta es una diferencia puramente académica. Lo cierto es


que la mayoría de los métodos viene desplazándose, afianzados en el borboteo
comercial de las Bases de Objetos (OODBMSs), hacia esquemas puramente
dinámicos. Y es que divisiones como la anterior pueden encontrarse con dema-
siada frecuencia en la literatura, cada vez más extensa, sobre el tema, fundamen-
talmente debido a la gran profusión de métodos con bases teóricas un tanto difu-
sas. Y si no échenle un vistazo a algunos (imposible disponer de una lista actuali-
zada de todos ellos) de los actuales métodos de OOA/OOD (intencionadamente
desordenados, para no generar falsas esperanzas):

Análisis y Diseño Orientados-a-Objetos

Métodos Siglas Autores


Object Oriented Design OOD Grady Booch
Object Behaviour Analysis OBA Rubin & Goldberg
Methodology for Object Oriented MOSES Henderson-Sellers &
Software Engineering of Systems Edwards
General Object Oriented Design GOOD Seidewitz & Stark
Object Oriented Software Engineering OOSE Ivar Jacobson
Visual Modeling Technique VMT IBM
Texel Texel
Object Modeling Technique OMT Rumbaugh y otros
Better Object Notation BOM Nerson
Object Oriented System Analysis OOSA Shlaer & Mellor
Object Oriented Structured Design OOSD Wasserman et al.
Systems Engineering OO SEOO LBMS
Syntropy Cook y otros
Object Oriented Jackson OOJSD Jackson
Structured Design
Hierarchical Object Oriented Design HOOD ESA
Object Oriented Analysis OOA Coad & Yourdon
Object Oriented Design OOD Coad & Yourdon
Object Oriented System Analysis OSA Embley y otros
Colbert E. Colbert
Frame Object Analysis FOA Andleigh/Gretzingr
Semantic Object Modelling Approach SOMA Ian Graham

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 183
Berard Berard
ADM3 Donald Firesmith
Ptech OOA&D Martin & Odell
Object Oriented Rôle Analysis, OORASS Reenskaug et al.
Synthesis and Structuring
Fusion Coleman y otros
Desfray Softeam
Responsability Driven Design CRC Wirfs-Brock et al.

Como el lector avisado fácilmente comprobará, en la anterior tabla se mezclan


métodos de análisis y de diseño porque, pese a lo que anuncien sus autores o
aun su mismo nombre, la distinción entre análisis y diseño se difumina, como an-
tes comentábamos, de forma turbadora (aunque claramente deseable respecto
de la modelización de la realidad).

SOBRE LISTAS Y COMPARACIONES

Naturalmente sería deseable disponer de una suerte de catálogo bien documen-


tado en el que se explicitaran, compararan y evaluaran las claves exactas, bonda-
des y flaquezas de cada método de OOA/OOD. Así los interesados simplemente
sólo tendrían que dilucidar, tras una somera revisión del documento, el método
que mejor se ajustase a las características del proyecto a desarrollar. Podría pen-
sarse, también, que tales comparaciones ya existen (y de hecho así es: libros,
artículos, juegos y productos software presumen de haber completado tal tarea).
La realidad es, sin embargo, que mal pueden evaluarse y compararse métodos
cuando no existen métricas orientadas-a-objetos, ni tan siquiera criterios claros e
indiscutibles, para tal fin. Los mismos autores no se atreven a afirmar que sus
métodos son “universales”, aunque desafortunadamente tampoco establecen sus
límites de aplicación. De esta manera resulta que la mayoría de papeles sobre el
tema se suelen limitar a un resumen textual de ideas, donde se comparan las sub-
jetivas quintaesencias de cada método. Resulta así, por fin, que tales revisiones
son válidas sobre todo para aquellos lectores que conocen suficientemente los
métodos estudiados. Pero ejemplifiquemos estas flaquezas:

Wirfs-Brock favorece las responsabilidades frente a los atributos, mien-


tras que Rumbaugh (OMT) enfatiza la importancia de las asociaciones
entre clases; Jacobson (Objectory) es el autor de los casos-de-uso (use-
cases) y afirma que su metodología soporta el ciclo total de vida del soft-
ware orientado-a-objetos; Booch, por otro lado, introduce la noción prácti-
ca de visibilidad en su método de OOD. Shlaer/Mellor (OOSA) modifican
los DFDs en Action-DFDs y soportan una gradual descomposición de las
acciones en el modelo-de-estados en procesos en el modelo-de-
procesos. Embley, Kurtz y Woodfield (OSA) amplían y matizan OMT

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 184
añadiendo el modelo-de-interacción-de-objetos (OIM). Coad/Yourdon
aportan en su método unario (OOA/OOD) las plantillas de objetos y de
descripción de métodos. Etc., etc., ... ad nauseam.

Como el lector fácilmente podrá apreciar, la legibilidad no es lo que aquí más im-
porta: las anteriores frases tienen sentido sobre todo para el que ya tiene una opi-
nión formada sobre los distintos métodos. La comprensibilidad sí se echa en
falta: es fatalmente difícil asimilar opiniones como las anteriores sin conocer en
suficiente detalle cada uno de los métodos tratados, y tanto más en cuanto que
tales no representan unidades de comprensibilidad autónoma, pues cada uno
refleja una particular visión, usualmente basada en la experiencia de sus autores
en proyectos reales, del ciclo de desarrollo de software. ¿Y entonces? Pues que,
aun a riesgo de parecer estúpidamente trivial, debo sostener que para aplicar
un método de OOA/OOD (o un refrito de algunos de ellos) hay que conocerlos
casi todos. Pero examinemos con más detenimiento esta -aparentemente- sor-
prendente conclusión.

CÉSAR O NADA

A poco que el lector bucee en los textos y artículos de análisis y diseño orienta-
dos-a-objetos se encontrará con afirmaciones tales como que los métodos de
OOA/OOD se dividen en unarios y ternarios, o que los métodos de segunda ge-
neración pueden usar de uniones de métodos de primera generación, o simple-
mente que si un análisis contiene un solo DFD es que hay que rehacerlo (Cole-
man?!). Y es que no hay final donde no existen claros principios: si aun se discute
la esencia ortodoxa de la orientación-a-objetos (si la persistencia es una caracte-
rística, si la herencia es simplemente un mecanismo, bla-bla-bla), ¿cómo abundar
en tantas clasificaciones y divisiones? Epore il muove, como diría Galileo, pues
lo cierto es que los sistemas orientados-a-objetos comercial y realmente funcio-
nan. Debe ser posible, pues, extraer técnicas prácticas de los mismos, pues tales
normalmente se basan directamente en las experiencias prácticas (que sí suelen
funcionar) de sus autores. Pero ¿existe algo bueno en cada método que se pue-
da aislar y usar en un entorno separado? Absolutamente no, aunque quizás sí con
cierta matización. Resulta que los objetos no están ahí para cogerlos (object-
picking), sino que más bien el analista (OODA: Object-Oriented Domain Analyst)
los crea en cada análisis, y de aquí que la pertinencia de que cada método o téc-
nica deban ser específicamente evaluados en cada proyecto. Y precisamente
para esto hay que conocer muchos de ellos, pues si no limitaríamos de forma pe-
ligrosa la arquitectura, y aun el resultado final, de nuestros proyectos. Pero tal no
es exclamar, en un remedo barojiano, o “César o nada”: se trata más bien de
ponderar si la base metodológica del equipo de análisis y diseño es suficiente
para abordar proyectos no-triviales, y dado que no existen todavía criterios in-
amovibles de evaluación metodológica en OOA/OOD, parece que habrá que de-
jar, en buena medida, la concrección de elementos básicos arquitectónicos en la

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 185
construcción de software al buen criterio de tal equipo. Pero aquí surgen lógicas
inquietudes: ¿es posible dedicarse al estudio de las corrientes OOA/OOD sin
perder el rumbo del trabajo diario? ¿cómo se ajusta el anunciado eclecticismo,
de connotaciones subjetivas, con la deseable normalización de la gestión de pro-
yectos en las empresas de desarrollo? ¿cómo determinar la bondad práctica de
los métodos basándose solamente en consideraciones teóricas? ¿no existe, al
fin, un cierto rango de soluciones suficientemente probadas en análisis y diseño?
Bien: vayamos a las respuestas.

LA SOLUCIÓN ECLÉCTICA

La experiencia y la revisión real de la gestión de proyectos orientados-a-objetos


en distintas empresas muestra que el exito siempre ha ido acompañado de una
visión particularizada de OOA/OOD/OOP acorde con la política de la empresa,
las pecularidades del equipo de desarrollo, la experiencia en determinados len-
guajes de programación y la naturaleza concreta de cada proyecto, dentro de
unos márgenes más o menos estables de actuación. Resulta así que, mayormen-
te, es una cuestión de “afinidades electivas”, como diría Goethe. La gran cuestión
es, por supuesto, ¿quién elige? Y aquí sólo una respuesta: un experto, natural-
mente.

Es moneda común que la resolución de proyectos exitosos, en tanto la empresa


no adquiere una sólida cultura de objetos, se debe al consejo y mentorización -
externa o interna- de expertos o “gurúes” (la barbarización de OGs: Object Gurus)
que, merced a su experiencia en distintas empresas, pueden elegir con funda-
mento el enfoque idóneo para cada cometido software. Es labor de tales expertos
asesorar sobre el equipo de desarrollo, arquitecturas, metodologías, generación
de documentación, lenguajes de programación, gestión del proyecto mismo,
herramientas de desarrollo, técnicas y herramientas de validación, etc.

Con todo, y para que el lector no se quede con una amarga sensación, sí puede
explicitarse un cierto núcleo metódico: Las fichas CRC y el enfoque a responsabi-
lidades facilitan mucho la labor en las primeras etapas del OOA/OOD (que bási-
camente trabajan sobre unas especificaciones obtenidas en la etapa del OORA,
Object-Oriented Requeriment Analysis, pero esto es otra batalla); después una
modelización tipo OMT ú OSA ayuda bastante, junto con aspectos de la metodo-
logía de Booch y la aplicación de casos-de-uso (use-cases) de Jacobson, e inclu-
so diagramas de transición de estados y redes de Petri. Al final se obtienen fi-
chas tanto de objetos como de sus relaciones, pero tales son, como casi todo,
susceptibles de ser mecanizadas. Naturalmente si todo lo anterior se envuelve en
un entorno visual (lisez VMT, de IBM), las cosas mejoran bastante. Si a este en-
torno añadimos capacidades de prototipación rápida y de documentación del
diseño, entonces parece que tendríamos la herramienta adecuada, con lo que
entramos en el resbaladizo terreno del CASE (OOCASE ahora).

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 186
HERRAMIENTAS OOCASE

Como una requeteconocida multinacional oficiosamente afirma: “Prescinde de


herramientas OOCASE, pero si has de operar con una procura que sea la más
barata”. Lo cierto es que el mercado de tales herramientas está un tanto desqui-
ciado: cuando parecía que estaban en franco decaimiento, la orientación-a-
objetos les proporciona un soporte nuevo sobre el que extender su funcionalidad.
La realidad, empero, es que tales herramientas son aun bastante primitivas: el
soporte de los distintos métodos es cuestionable, la reingeniería no funciona (los
problemas son incontables si se toca el código), y muchas de ellas se limitan a la
parte gráfica (son al menos sinceras y resultan simpáticas las que comercialmen-
te afirman que sólo sirven para eso: para los gráficos). El otrora espinoso asunto
de los repositorios parece solucionado mediante Bases de Objetos, pero existe
un gap insondable entre los resultados de tales herramientas y el código de pro-
ducción que se supone (como el valor a los soldados) generan. Los mejores re-
sultados aparecen, no obstante, en herramientas íntimamente ligadas a entornos
de programación, como smalltalk, pero lo que aquí se consigue es, en esencia,
extender tales entornos de una forma que pronto asumen los propietarios de los
mismos (Parts de Digitalk, VisualAge de IBM, etc.). ¿Hasta qué punto un entorno
orientado-a-objetos con capacidades visuales/textuales de documentación, mo-
delización y prototipación necesita de una IOOCASE? Naturalmente la cuestión
pasa primero por la revisión de tales entornos, y más tarde por la adecuación de
los posibles métodos de OOA/OOD elegidos a las herramientas OOCASE dis-
ponibles para su uso. Cuestión de expertos, sin duda. Pero la experiencia mues-
tra que la herramienta que mejor satisface los requerimientos de un proyecto
orientado-a-objetos es la que no existe: esto es, usualmente se utilizan esquemas
textuales “caseros”, mientras que el uso de herramientas comerciales se basa en
razones “corporativas” de normalización documentaria (así es frecuente encontrar
severos desajustes entre la documentación generada por la herramienta y el pro-
yecto real, tan lejos uno de del otro como lo está un político de Utopia). Lo más
prudente es determinar, con la ayuda de un experto, el direccionamiento hacia
estas herramientas.

GESTIÓN DE PROYECTOS ORIENTADOS-A-OBJETOS

Si los anteriores capítulos parecen borrosos, echémosle una ojeada ahora al


OOPM (Object-Oriented Project Management): ¿Qué documentos deben gene-
rarse y utilizarse en el desarrollo de un proyecto software orientado-a-objetos? ¿Y
en qué momentos? ¿Cuáles son los puntos críticos y cuáles los de control del pro-
yecto? ¿Cómo pueden, por otro lado, evaluarse los rendimientos del equipo de
desarrollo? ¿Existen métricas formales? Bueno, son muchas preguntas. Y las
respuestas aparecen en los manuales. Sí, en los escasos manuales de OOPM
existentes y que se deben, en su práctica totalidad, a grandes firmas consultoras.
Otra cosa es su practicidad: hay que soportar grupos de “kernels” de cuatro díg i-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 187
tos y “mapeos” de componentes a documentaciones y responsabilidades. Lo que
es incuestionable es que algunos roles cambian, otros aparecen como nuevos,
mientras que otros desparecen por completo (como, verbigracia, el del mero pro-
gramador). Con la documentación (o los “deliverables”) ocurre otro tanto: dado
que el ciclo de vida del software es diferente (“Analiza un poco, diseña un poco,
implementa un poco y evalúa un poco”, según Berard), no existen fases estancas
y, por tanto, una fase no puede basarse en resultados completos de una supuesta
fase anterior, de lo que resulta que la documentación ha de generarse, también,
de forma involutiva. Parece claro, no obstante, que han de documentarse clases,
métodos y relaciones. Pero lo que definirá la estrategia de documentación nor-
malmente se define en la etapa de OORA (la gran desconocida, en manida fra-
se): especificaciones estáticas y dinámicas, de interfaces y comportamientos, de
modelización y control, etc. Trabajo de expertos, de nuevo. El lector podrá encon-
trar, empero, descripciones de particulares gestiones de proyectos, con diferen-
tes niveles de abstracción, en Lorenz, Berard, Firesmith, etc., pero difícilmente
podrán ser aplicadas a un proyecto concreto con limitaciones humanas, tempora-
les y dinerarias, cual suele ser el más común caso. ¿Desesperanza, entonces?
No: simplemente una cuestión de monitorización: suele acertar quien se ha equi-
vocado antes, directa o indirectamente; y usualmente la gestión de proyectos ne-
cesita de una experiencia interdisciplinar en campos dispersos.

SÍSIFO REDIVIVO

Quedáronse muchas cosas en el tintero: Bases de Objetos, Lenguajes de Pro-


gramación, OORA, etc., etc. Pero se trataba aquí de generar en el lector un cierto
grado de insatisfacción, complemento del caramelo comercial “orientado-a-
objetos” que nos invade de forma inmisericorde. Y recuerden a Chesterton: sólo
se permite dudar el que sinceramente cree, alejado de todo fanatismo. Cada uno
tiene un método de OOA/OOD, como cada matemático tiene un álgebra. Quizá la
mejor descripción del proceso de desarrollo de software orientado-a-objetos sea
esa que explica que se trata con plastilina en lugar de con bloques de metal, y que
la cuestión no es generar componentes inamovibles, sino más bien refinarlos gra-
dualmente desde borradores más o menos acertados. Se trata, al fin, de un
proceso reiterativo que asimila al analista a Sísifo, subiendo incesante una piedra
que vuelve a caer a la ladera para volver a ser subida. Y aquí, como en el ensayo
de Camus, podemos sentir la sonrisa de Sísifo cuando baja la pendiente para
volver a empujar la piedra.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 188
MODELOS DE ROLES
11
¿Qué es lo que, por encima de otras características, identifica a todos los lengua-
jes de programación orientados-a-objetos? ¡Los objetos, naturalmente!, como
quiera que en cada caso puedan llamarse. Pero tras éstos sigue una legión de
lenguajes que incorporan lo que se denominan “clases”, sean éstas propiamente
objetos o no, intentando proporcionar estructuras que encapsulen las comunali-
dades de conjuntos de objetos. Teniendo en cuenta, por otro lado, que los es-
quemas de diseño, y más tarde de análisis de sistemas, debieran provenir del
estudio de las estructuras linguísticas aplicadas en la implementación real de so-
luciones software, resultará evidente al lector por qué la mayoría de los métodos
de OOA/OOD se basan en el pre-establecimiento de una dicotomía inamovible:
clases y objetos. Pero, atención, si se examina brevemente esta aseveración pa-
rece que algo falla: ¿todos los sistemas reales se modelarán sólo en base a cla-
ses y objetos? ¿Es ésta la solución definitiva? Humm, atendamos a una pequeña
disgresión: como el lector sabe, los distintos métodos, técnicas, trucos y conjuros
que conforman el grueso de lo que pomposamente se ha dado en llamar Análisis
y Diseño en Ingeniería de Software no son sino resoluciones más o menos afortu-
nadas del problema genérico de la traslación de la realidad al dominio software.
Tradicionalmente tal correspondencia se ha basado, como antes se expuso, en
una inferencia posibilista basada en modelos prácticos de funcionamiento efecti-
vo: esto es, primero se han modelado, más o menos desconexamente, multitud
de sistemas, para más tarde extraer de éstos conclusiones genéricas teórica-
mente aplicables a problemas similares a los modelados. Actualmente, sin em-
bargo, la creciente explosión informativa ha dado en generar un fenómeno harto
curioso que yo denominaría “recursión desplazada”. Tomemos, por ejemplo, el
“Análisis y Diseño Orientado-a-Objetos”: originariamente bienintencionado y ba-
sado en simulaciones reales (lisez aquéllas basadas en Simula, Smalltalk-80,
etc.), pronto desembocó en un precipicio involutivo con ribetes atractivamente
comerciales (lisez Coad&Yourdon, Booch, etc.), mención aparte de las cualida-
des reales de cada método (bien escasas, con todo, en su relación con la gestión
real de proyectos). Así, como cuando se llevaron conejos a Australia, estas prime-
ras incursiones se asentaron en un terreno sorprendentemente fértil, e, igual que
ocurrió con aquellos animales, la profusión de métodos devino alarmantemente
rápida, ocasionando a la vez similares destrozos (y la mixomatosis no parece ser

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 189
aquí, tampoco, buena solución). Si a todo esto añadimos la avidez informativa
que caracteriza a estos últimos años obtendremos una situación en que las expe-
riencias quedan aplastadas por las inferencias: esto es, como no se da tiempo a
que se generen experiencias sólidas fiables para cada uno de los métodos publi-
cados, de forma que sobre ellas pudiera basarse una revisión de los mismos, los
nuevos métodos han de basarse, por fuerza, en las versiones más recientes de
métodos anteriores: o sea, nos elevamos tirándonos de las orejas. De esta mane-
ra, finalmente se convierte en incontestable esencialidad lo que fue contingencia
primeramente: los nuevos métodos33 orientados-a-objetos dan, así, por supuesto
que la base de su estructura y notación son, a la vez, objetos y clases, usualmente
aderezados después con sofisticadas, prolijas, tendenciosas o ridículas conside-
raciones contextuales y de establecimiento de capas comprehensivas (ya sabe el
lector, esas que consiguen hacer mínimamente abordables composiciones de
3.000 clases, 15.000 conexiones, 20.000 flechas, etc.): aparecen, así, subsiste-
mas, entornos de visibilidad, relaciones tipificadas, abstracciones de variación de
estados ... y multitud de artefactos intelectuales que intentan suplir las carencias
de una elección inicial cuando menos sesgada. Pues si sólo se dan inicialmente
clases y objetos, como sólo se dieron Adán y Eva 34, forzosamente ha de cons-
truirse inicialmente un modelo estático de relaciones entre entidades (agrupemos
la dualidad), para después estimar comportamientos, interacciones y reacciones
dinámicas: todo un pastiche gastronómico que podría haberse evitado eligiendo
mejor las viandas principales. “Y, claro, lo que faltan son los roles” -apuntará el
impaciente lector. ¡Vaya, así es! Y es que he dado muchas pistas. “Pero esto es
una afirmación tan gratuita como la que asegura la sola necesidad de clases-
objetos” -seguirá el lector crítico- “¿Por qué ésta sí y aquéllos no?” Humm, ob-
viando la importante cuestión que el enfoque de roles que aquí se expondrá inclu-
ye al de clases-objetos, y aplazando temporalmente la inmersión en definiciones y
procedimientos, intentaré dar satisfacción al lector atacando, antes de entrar en el
modelado genérico de roles, distintos aspectos esenciales a la construcción de
sistemas software, singularmente basados en la interpretación de la realidad y
extraídos, en buena parte, de mi experiencia personal.

ESCENARIOS Y APREHENSIONES

Imaginen a una gran empresa rediseñando su sistema de facturación y gestión


comercial: como se trata de un proyecto crítico para la compañía, no basta un
simple redibujado de lo hasta entonces utilizado: hay que replantearse los su-

33
Observe el lector que conscientemente evito el vocablo “metodología” en un vano propósito, sin
duda, de resaltar con la repetición en la omisión cierta intención irónica, cuando no despectiva.
34
Oh, Adán y Eva se dieron entre sí de manera que vocablos como “conocimiento” y “congreso”
adquirieron su sentido exacto. Y es que seguro que a estas alturas el lector ya habrá asimilado la
subrepticia comparación entre la dicotomía “clase-objeto” y el Concilio de Trento. Claro que, ¿se
equiparará OOram al Concilio Vaticano II? ¡Demonios, espero que no!

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 190
puestos básicos en que se apoya el sistema; de hecho hay que volver a “analizar”
el problema. Pero ... ¡se trata de un sistema complejo, con demasiadas interac-
ciones para ser asimilado conceptualmente en una sola vista! ¿Solución? “Oh,
usemos una herramienta CASE -podría exclamar el impaciente lector”. “Mejor si
es OOCASE” -asumirá aquel otro lector, quizás un tanto receloso. “Alegradme el
día” -podría anunciar entonces yo, empuñando con soltura una Magnum. Porque,
¿para qué demonios quiero yo traspasar la complejidad del mundo real al plano
de los dibujitos inanimados35? Lo que aquí hace falta es algún enfoque compre-
hensiblemente simple que permita reducir la complejidad en el ataque para do-
mar el problema. Naturalmente no nos sirven, y más bien nos perjudican, las com-
posiciones de “tablas”: incontables entidades, con atributos de todos los tipos y
colores, que se relacionan mediante una inaprensible maraña de líneas con car-
dinalidades explícitas (0,n) por doquier, usualmente construidas mediante una
herramienta visual que genera los scripts de creación de tablas relacionales para
una amplia gama de gestores. Y es que en estos esquemas una entidad (por
ejemplo, una Persona) tiene los mismos atributos (columnas) respecto de muchí-
simas otras entidades, lo cual parece razonable en el contexto del diseño de ta-
blas, pero resulta descorazonador cuando respecto de una entidad concreta, co-
mo por ejemplo un Asistente Técnico, únicamente necesita de su dirección. ¿Por
qué repetir, pues, todos los atributos en todas las situaciones? Pues porque, sim-
plemente, no se consideran las situaciones en sí, sino sólo las relaciones estáti-
cas entre entes. Oh, las relaciones dinámicas también se estiman en los posterio-
res diagramas de transición de estados (y técnicas adláteres), pero sólo de ma-
nera que se rellenan y cambian algunos atributos del total que reúne la entidad:
esto es, cada situación dinámica mantiene un conjunto invariante de atributos,
que únicamente se deduce por su omisión del modelo dinámico, de forma que los
defectos del presupuesto inicial de diseño de tablas contaminan el proceso ente-
ro de análisis/diseño. Además este planteamiento supone una visión conciliadora
que pasa por muchas consideraciones, decisiones de diseño y soluciones ses-
gadas que en absoluto quedan documentadas con el mero diseño de las tablas.
Me explico: yo puedo decidir, por ejemplo, que el “Cliente” de este escenario es
el mismo “Abonado” de aquel otro, y que ambos son, en el fondo, instancias, qui-
zás con diferentes estados internos, de la entidad/clase “Persona Física”, que
fácilmente es una clase derivada de “Persona”, con la que operaré más genéri-
camente (también las “Personas Jurídicas” son “Clientes” y “Abonados”). Todo
esto está muy bien, pero son decisiones que, a lo sumo, se documentarán de
forma externa al diseño de marras, pues en éste lo que finalmente aparecerá será
una clase/entidad con demasiados atributos/columnas y alguna breve calificación
textual. Y es que no podemos abordarlo todo a la vez: según G.I. Miller, la memo-

35
Las neurosis de Walt Disney, a pesar de la animación, construyeron una realidad comercial
asexuada y muy cercana a los sueños infantiles del Estrangulador de Boston: muñecos que no
hablan y que siempre enseñan ambas manos, y ... ¡Pero bueno! ¿Qué clase de bicho es Goofy?
¡El sueño de un psicólogo rogeriano!

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 191
ria a corto plazo sólo puede asimilar simultáneamente tres bloques conceptuales
con un máximo de tres elementos cada uno (una variación de la regla, también
suya, del 7 más/menos 2), así que para comprender hay que segmentar: Divide et
Impera. Pero, ¿cómo encontrar los subsistemas, escenarios o porciones autó-
nomas sobre las que aplicar nuestra cuestionable inteligencia analítica? Exami-
nemos algunas posibilidades:

1. Use Cases (Casos de Uso): Fiamos la delimitación del escenario a


nuestra intuición en la adecuada apreciación de los procesos esencia-
les al sistema a modelar, concretados en razón de los estímulos exter-
nos que los generan. Se trata, en breve, de reflejar el flujo secuencial
temporal de mensajes/respuestas entre clases/entidades

2. Responsibility-Driven Design Walkthroughs (Revisión de Escenarios


en Diseño Orientado-a-Responsabilidades): Tras identificar las clases
del sistema y las responsabilidades que asumen, basadas en la política
contractual de colaboraciones cliente-servidor, el analista se plantea es-
cenarios posibilistas e intenta adaptar, refinar o inferir nuevas interac-
ciones entre entidades.

3. Escenarios: Un atractivo término resonante (buzzword) que encierra,


como el infierno de don Ramón, más intenciones que realidades. Los
escenarios son ubicuos, como se quisiera que fuese dios, pero igual-
mente difíciles de encontrar e identificar.

Oh, estas opciones suenan distintas pero adolecen de la misma indeterminación


primera: ¿Cómo establecer los escenarios de un sistema a modelar sin usar heu-
rísticos cosmogónicos o técnicas dolosamente arbitrarias? Esto es, ¿cómo iden-
tificar escenarios que no resulten tan volátiles como para que su cambio no afecte
a la arquitectura del sistema? Bueno, la experiencia sirve, sin duda, pero sólo a
los expertos36. Necesitamos algo más: una suerte de tabula rasa en la que basar
nuestra segmentación de la realidad.

TRAS DIVIDIR HAY QUE MULTIPLICAR

Volvamos a nuestro sistema de gestión comercial: ¿no hay ninguna forma de es-
tablecer módulos de trabajo a modo de componentes con los que construir, me-
diante su integración, el sistema final? ¡Naturalmente que sí! Un primer acerca-
miento incruento (al menos respecto del Jefe de Departamento, usualmente bien
pertrechado de arrojadizos laborales) sería intentar modelar procesos (o mejor

36
El lector deberá perdonarme el refrito de la infatigable verdad “El dinero llama al dinero”. Yo
mismo habré también de perdonarme, pues ciertamente aborrezco la incontinente vulgaridad de
la franqueza social de la que los refranes son odioso reflejo.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 192
actividades) a la manera de los casos de uso, controlando los estímulos externos
que afecten a la identidad del sistema (no las colaboraciones ni las cadenas de
mensaje), también como las repercusiones externas, pero recabando que los ro-
les a que se pueden adscribir las entidades del mismo conforman un modelo
conceptual claro y presumiblemente inamovible en ese contexto. O sea, en la
práctica se trataría de modelar por separado situaciones en las que en primer
lugar no se dé variación del rol asumido por cada entidad, pudiendo integrar des-
pués los intercambios de roles en el mismo modelo. Así una bienintencionada
primera fase práctica pudiera ser la de reconocimiento de los roles que se juegan
en razón de los procesos actuales que se dan en el dominio del problema: existe,
por ejemplo, un proceso de “Servicio de Reparación de Averías” en el que se dan
los roles de “Abonado con Problemas”, “Mecánico”, “Punto de Servicio”, “Servicio
Afectado” y “Material Estropeado”, entre otros. Nuestros punto de control respecto
de los límites de esta porción serían los de transición en una entidad de un rol a
otro. Así, por ejemplo, si como consecuencia de una avería irreparable el “Abona-
do” decide rescindir el servicio, o contratar más garantías para el mismo, el rol
que juegue habrá dejado de ser el de “Afectado”, pasando a desempeñar el de
activo “Contratante”, pero tales nuevas actividades deberían permanecer fuera de
nuestro modelo de Servicio Asistencial. Hay que recabar, también, que en esta
etapa en absoluto se prejuzga qué tipo de entidad o clase es la que “implementa-
rá” (y este calificativo es importante) cada uno de los roles encontrados: de esta
manera en cada escenario (o mejor, modelo de roles) se detallan únicamente los
atributos que interesan al modelo en estudio. Naturalmente en algún momento
habrá que integrar o sintetizar tales atributos para componer la entidad de imple-
mentación (“clase” o usualmente “tabla”): para conseguir tal habrá que considerar,
más o menos informalmente, cómo la unión de modelos de roles afecta al modelo
de roles resultante (pues eso es lo que obtenemos, en buena lógica). Lo que así
estamos consiguiendo es estratificar con límites semánticos bien definidos cada
una de las porciones de que se compondrá nuestro sistema, y la verdad es que
las perspectivas son halagüeñas: si el enfoque funcional se está abandonando en
favor del de objetos/entidades, para evitar la volatilidad de los sistemas, ¿por qué
no ir un paso más allá y afianzar el diseño en modelos fácilmente comprensibles,
reutilizables y componibles en distintos contextos? Ah, estos son los modelos de
roles. Pero ¿no suena todo lo expresado un tanto etéreo, como muy dejado a la
imaginación del analista? Bien, quizás suene así, como puede sonar a tarareo
una ópera de Verdi desde muy lejos, así que habrá que acercarse un tanto: en lo
que sigue examinaremos muy muy brevemente los aspectos más importantes de
un método, Ooram, analíticamente completo, intelectualmente satisfactorio y con
muchos años de experiencia a sus espaldas. Así que lo propio será recaer en el
libro en que se explicitan, mejor que aquí, tales ideas.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 193
EL LIBRO

Working with Objects: The OOram Software Engineering Method, por Trygve
Reenskaug, Per Wold y Odd Arild Lehne, 1996, Manning Publications, 1-884777-
10-4 (Prentice Hall 0-13-452930-8).

Atención, lector: a mi entender este es uno de los textos más importantes publi-
cados en los últimos años sobre construcción de sistemas software, de forma que
arriesgarse a ignorarlo es comprometerse con la oscuridad. Sepa el lector, de
cualquier forma, que no soy fanático: sólo vehemente. Esto es, en el texto se ex-
plicitan muchas ideas que a mí me parecen no sólo naturales y adecuadas, sino
también inteligentes, efectivas y, finalmente, humanas. Es curioso que sean los
trabajos europeos, y sobre todo los del norte, los que enfaticen la primordial im-
portancia del factor humano en la construcción de sistemas software. El libro
contiene, además, un importante componente pedagógico, pues evidencia los
importantes logros conseguidos mediante el uso prudente de Smalltalk y de la
más general Tecnología de Objetos en una empresa (Taskon) a lo largo de más
de 25 años. El autor principal, Trygve Reenskaug, es, además de un reputado
experto en el campo de la Orientación-a-Objetos, el creador del concepto
Modelo-Vista-Controlador (MVC) que seguro todos conocen. En fin, para abordar
el contenido del libro intentaré resumir los capítulos más significativos.

IDEAS PRINCIPALES

El método OOram se define como “un marco de referencia para una familia de
metodologías orientadas-a-objetos”, tras lo que se muestran las mejoras que el
método introduce en las tres dimensiones típicas que componen el grueso de me-
todologías:

• Tecnología (conceptos, notación, técnicas y herramientas): donde se


muestran el poderoso modelado de roles en análisis y su integración
posterior -síntesis en Ooram-, junto con los conceptos básicos (roles,
puertos -que anuncian la visibilidad de un rol respecto de otro-, etc.) y
algunas herramientas. Es interesante resaltar la referencia de distintos
conceptos a abstracciones: Objeto es “es”, como rol es “por qué”, mien-
tras que tipo es “qué” y clase es “cómo”.

• Proceso con Entregables (la secuencia de pasos y la documentación


que los acompaña): OOram distingue tres distintos procesos: creación
del modelo, desarrollo del sistema y construcción de piezas reutiliza-
bles.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 194
• Organización (las maneras en que la empresa acoge lo anterior): aquí
se explicitan las dificultades de diseñar para reutilizar y las ventajas que
supone el uso de cadenas de valor.

MODELADO DE ROLES

“El modelo de roles es una abstracción del modelo de objetos donde reconoce-
mos un patrón de objetos y lo describimos como un corespondiente patrón de
roles”. Y es que la clase se apoya en las capacidades (funcionalidad absoluta) de
los objetos, mientras que el rol se apoya en la posición y responsabilidades de un
objeto respecto de una estructura de otros tantos, que es precisamente lo que
percibimos cuando examinamos un conjunto de entidades/objetos en colabora-
ción. Pero el modelo de roles no representa la descripción de la estructura entera
de objetos observada, sino más bien de porciones de la misma, segmentadas
temporalmente, denominadas “áreas de interés” (areas of concern), y que mues-
tran fenómenos colaborativos de interés. Resulta, así, que un modelo de roles es
un modelo orientado-a-objetos de una estructura de objetos.

En OOram las técnicas de identificación de roles no resultan muy lejanas a aqué-


llas expuestas en el libro de Wirfs-Brock con la ayuda de fichas CRC, de forma
que tras haber encontrado el modelo de interacción, mediante la separación en
Áreas de Interés, para su visualización comprehensiva OOram plantea tres distin-
tas perspectivas: contextual, externa e interna, y provee diferentes “vistas” apli-
cables según mejor convenga, siguiendo la acertada idea de que no hay que usar
todos los recursos para describir un modelo, y que determinadas vistas, reunidas
en un subconjunto concreto, tornan el modelo más comprensible que otras. Así
OOram provee las siguientes vistas: Vista del Area de Interés, Vista del Estímulo-
Respuesta, Vista de la Lista de Roles, Vista Semántica, Vista de Colaboracio-
nes, Vista de Escenario, Vista de Interfaces, Vista de Procesos, Vista de Dia-
gramas de Estado y Vista de Especificación de Métodos, cada una de ellas con
su propia notación, enlazada empero respecto de un objetivo global de modela-
do.

SÍNTESIS DE MODELOS DE ROLES

Se trata aquí de integrar en un modelo más general (o modelo derivado de roles)


los diferentes modelos de áreas de interés, de manera que un sistema se confi-
gura finalmente como síntesis, que no mera agregación las más de las veces, de
las partes que lo componen. La síntesis puede darse, así, bien por superposición
(los mensajes de estímulo en el modelo básico siguen siendo mensajes de estí-
mulo en el modelo derivado), o por agregación (un modelo de base se integra
como una caja negra o subrutina en el modelo derivado), que se califican de téc-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 195
nicas seguras de síntesis, bien por lo que se conoce como síntesis insegura, cu-
ya aplicación necesita de una personal y cuidadosa atención.

En la síntesis de roles aparece nuevas vistas: Vista de Síntesis, Vista de Colabo-


ración por Herencia, Tabla de Herencia y el Lenguaje OOram de Especificación
de Herencia. Se muestran, a la vez, las perspectivas de aplicación del proceso de
síntesis respecto de cada una de las vistas correspondientes al modelo de roles.

PUENTE A LA IMPLEMENTACIÓN

En esta sección aparecen las especificidades añadidas a las vistas de colabora-


ción inter-roles en relación a los aspectos de implementación de los roles, ade-
más de hacer corresponder los conceptos en OOram con los equivalentes de im-
plementación en C++ y Smalltalk, aplicables por tanto a otros lenguajes con
facilidades de orientación-a-objetos. Hay que tener en cuenta que el entorno de
trabajo desarrollado por Taskon se ha basado grandemente en Smalltalk, de
forma que los aspectos de uso de herencia múltiple y mixins en C++ se tratan con
alguna ligereza. Lo que aquí se abordan son los aspectos iterativos de desarrollo
en el proceso de implementación y las cuitas de elección del lenguaje de
programación

CREACIÓN DE COMPONENTES REUTILIZABLES

Aquí se muestra la idea básica de que para reutilizar es necesario utilizar antes,
de manera que la llave del éxito es la comunicación efectiva entre el proveedor y
los consumidores. OOram distingue entre reutilización por herencia (mediante
patrones y frameworks) y reutilización por encapsulación.

El reúso por herencia se basa en patrones (que respecto de OOram son paque-
tes de formato fijo contenedores de un modelo de roles junto con documentación
que describe cómo y cuándo debe éste ser usado) y en frameworks (entornos-
marco), que representan un modelo de roles junto con un conjunto de clases base
que definen direcciones y restricciones de extensibilidad.

El reúso por encapsulación se refiere en OOram a tres técnicas denominadas


OOCS (Sistema de Composición de OOram), Configuración en Tiempo de Eje-
cución y Duplicación de la Estructura de Objetos. El Sistema de Composición
OOCS se basa en que dado un objeto-semilla, el esquema OOCS especifica los
tipos de objetos que pueden ser asimilados al mismo. El OOCS es, en esencia,
una cadena de valor con cuatro capas: Usuario final, Creador del Esquema
OOCS, Implementador de Tipo OOCS y Creador de Infraestructura.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 196
LEA, LECTOR: LEA

Como el lector puede apreciar en base a lo hasta ahora expuesto, no se trata aquí
de un simple método que postula elegantes y frecuentemente hueras descripcio-
nes étereas de sistemas, sino más bien un verdadero marco referencial muy den-
so en conceptos, notaciones y esquemas formales, pero donde el factor organiza-
tivo y humano juega un papel primordial. La extrema densidad de lo que en el tex-
to se expone troca casi en imposible su sola enumeración comprehensiva aquí,
así que emplazo de nuevo al lector a que lea.

OOram es, según sus creadores, un método adecuado de tratar con lo que se
denomina programming-in-the-large. Pues bien, aparte de lo expuesto, en el
mismo texto podrán encontrar cuatro casos bien desarrollados como ejemplos: el
desarrollo de un sistema de información negocial, el análisis y diseño de un sis-
tema de tiempo-real, la creación de un entorno-marco (framework) y los servicios
inteligentes de red organizados como cadena de valor.

REFERENCIAS DIRECTAS

Ciertamente no hay demasiado escrito, así que rompiendo mi propia norma de


referenciar exclusivamente lo por mí leído, a continuación detallo las referencias
que a este respecto me pasó Carl Petter Swensson, a la postre uno de los reviso-
res del texto sobre Ooram, y que incluyen libros, artículos, tesis doctorales y es-
carceos con lenguajes de programación, conceptos e ideas similares en otros
ámbitos:
• Ivar Jacobson, Object Oriented Software Engineering (Libro).
• Richardson and P. Schwarz, Aspects: Extending objects to support multiple,
independent roles, Proceedings of the 1991 ACM SIGMOD International Con-
ference on the Management of Data, pp. 298-307, 1991.
• P. Gilbert and L. Bic, An object-oriented data model for Semantic Relativism,
ECOOP 1989.
• Lynn Andrea Stein and Stanley B. Zdonik, Clovers: the dynamic behavior of
types ans instances, Brown University, Technical Report CS-89-42, noviembre,
1989.
• Trygve Reenskaug et al., OORASS: seamless support for the creation and
maintenance of object oriented systems, Journal of Object-Oriented Program-
ming, 5 (6), octubre, 1992, pp. 27-41.
• Los conceptos del protocolo de meta-objetos dentro de la comunidad LISP.
• Egil P. Anderson and Trygve Reenskaug, System Design by Composing Struc-
tures of Interacting Objects, Proc. of ECOOP 92, Springer-Verlag LNCS 615,
junio/julio 1992, pp. 133-152.
• Barbara Pernici, Object with Roles, Proc. of the Conference on Office Informa-
tion Systems (COIS), Cambridge, Massachusetts, 1990.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 197
• Constantin Arapis, Specifying Object Lifecycles, D. Tsichritzis (ed.), Object
Management, Universite de Geneve, 1990.
• Constantin Arapis, Specifying Object Interaction, D. Tsichritzis (ed.), Object
Composition, Universite de Geneve, 1991.
• Constantin Arapis, Object Behaviour Composition, D. Tsichritzis (ed.), Object
Frameworks, Universite de Geneve, 1992.
• P. Papazoglou, Roles: A Methodology for Representing MMultifaceted Objects,
Proc. of Conf. Database and Expert Systems Applications - DEXA 91, 1991,
pp. 7-12.
• Lynn Andrea Stein, Compound Type Expressions: Flexible Typing in Object
Oriented Programming, Proc. of Conf. on Object-Oriented Systems, Languages
and Applications (OOPSLA), 1988, pp. 360-361, Position Paper.
• Constantin Arapis, Type Conversion and Enhancement in Object-Oriented Sys-
tems, D. Tsichritzis (ed.), Object Oriented Development, Universite de Geneve,
1989, pp. 191-205.
• David McAllester and Ramin Zabih, Boolean Classes, Proc. of Conf. on Object-
Oriented Systems, Languages and Applications (OOPSLA), 1986, pp. 417-
423.
• Hendler, Enhancement for Multiple-Inheritance, SIGPLAN, V. 21, N. 10, Octo-
ber, 1986.
• Edward Sciore, Object Specialization, ACM Transaction on Information Sys-
tems, V. 7, N. 2, April, 103-122, 1989.
• Mohamed E. El-Sharkawi and Yahiko Kambayashi, Object Migration Mecha-
nisms to Support Updates in Object Oriented Databases, N. Rishe, S. Navathe
and D. Tal (eds.), Databases: Theory, Design and Application, A postconfer-
ence proceedings based upon the proceedings of PARBASE-90 Florida
March 6-9, 1990, IEEE Computer Society Press, 1991, pp. 73-109.
• Jianwen Su, Dynamic Constraints and Object Migration, Proceedings of the
Sixteenth International Conference on Very Large Databases, Barcelona,
Spain, VLDB 91, 1991, pp. 233-242.
• El lenguaje denominado LOOM, desarrollado en ISI, California. Para más in-
formación, contactar a David Brill (brill@isi.edu) o copiar el Manual de Usuario
mediante ftp de isi.edu:/pub/loom.
• Craig Chambers, Predicate Classes, ECOOP 93 proceedings.
• Robert Strom et al., Hermes A Language for Distributed Processing, Prentice
Hall, 1991.
• La característica CHANGE-CLASS del lenguaje CLOS, que permite convertir
un objeto de una clase a otra.
• Papel sobre roles implementados en Smalltalk: roles.ps via ftp anónimo de
140.78.58.1
• La tesis doctoral sobre Redes Semánticas escrita por Nicholas Roussopoulos
en 1977 en alguna universidad canadiense.
• El modelo de datos funcional permite que los tipos sean asociados dinámica-
mente con objetos surrogados, y esto significa que la identidad de un objeto

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 198
permanece invariante durante sus transformaciones de tipo. IRIS ú OPEN-DB,
de HP.
• Berard Software Enggineering, Inc., A Complete Object-Oriented Design Ex-
ample.
• J. Wieringa, Algebraic Foundations for Dynamic Conceptual Models, Depart-
ment of Mathematics, Vrije Universiteit, Amsterdam, May, 1990, Ph.D. Thesis.
• Wirfs-Brock, Johnson, Surveying Current Research in Object-Oriented Design,
Communications of the ACM, 33 (9), September, 1990, pp. 113-116. Este
artículo incluye una sección sobre modelado de roles.
• T. Hawryszkiewycz, Database Analysis and Design (Libro).

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 199
Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 200
BIBLIOGRAFÍA COMENTADA
12
Lo que sigue es un conjunto de cortas reflexiones y personales consejos sobre
textos imprescindibles en C++ y Orientación-a-Objetos, sin dejar pasar los dedi-
cados a Java, Smalltalk, Bases de Objetos y metodologías en general. El lector
enterado constatará que algunos de los textos que se citan aquí (abrumadora-
mente en inglés) han sido traducidos ya al castellano, pero sea dicho que yo, en la
mayoría de los casos ya lo sabía, y he preferido un idioma extranjero a una tra-
ducción peligrosa, malvada o, simplemente, ridícula. El lector tiene, empero, una
garantía: todos estos libros forman parte de mi biblioteca personal y han sido se-
leccionados para su inclusión en esta relación por poseer una característica bási-
ca: merecen ser releídos37.

LIBROS BÁSICOS SOBRE C++

Seguidamente se detallan las obras de introducción al lenguaje, así como aqué-


llas de referencia básica, sin las cuales no se debiera seguir avanzando en el
mismo. Naturalmente he debido, pues éste es el segmento editorial más poblado,
tomar alguna decisión de exclusión que a alguno puede extrañar. No he incluido,
por ejemplo, la otrora famosa obra introductoria de Dewhurst & Stark, pues no
ofrece características diferenciales con respecto a la de Lippman, mientras que
ésta me parece de mayor claridad pedagógica. Tampoco he incluido ningún libro
del “respetable” Mr. Schildt, que obtiene su nicho de mercado de un grupo psico-
lógico bien determinado de lectores. He procurado, en fin, presentar estos textos
en orden de dificultad creciente, pero sin esforzarme demasiado en prevalencias.
Helos aquí:

The C++ Workbook, por Richard S. Wiener & Lewis J. Pinson, 1990, Addi-
son-Wesley, 0-201-50930-X, 349 pág.
He de confesar que le profeso cierto cariño a este texto, sin duda el más elemen-
tal de todos los aquí comentados. Se trata de una introducción a AT&T C++ 2.0,

37
“El libro que no merece ser releído no merece ser leído”, confirmo yo.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 201
con multitud de ejemplos, letra grande y legible, y abundantes resultados de
programas. El libro se caracteriza por frecuentes inserciones de apartados "¿Qué
pasaría si ...?", que intentan resolverle al lector las dudas más habituales en el
primer acercamiento al nuevo lenguaje. Teniendo en cuenta la proliferación actual
de desenfocadas introducciones al lenguaje C++, repletas de dudosas intencio-
nes y de tontería, esta obra es un respiro que se puede completar en escasas
horas. Ni de lejos sustituye, por supuesto, a textos de introducción como el de
Lippman o el de Dewhurst & Stark, pero muy bien podría constituirse en el perfec-
to puente hacia éstos, pese a que la evolución del lenguaje convierte cualquier
libro de menos de 400 páginas de letra pequeña en un mero divertimento.

A C++ Toolkit, por Jonathan S. Shapiro, 1990, Prentice Hall


Uno de las primeras necesidades de un desarrollador de C++ es la lectura y el
estudio de código "práctico": tras una etapa de aprendizaje generalmente no todo
lo corta que uno hubiera esperado, el principiante llega a estar ciertamente has-
tiado de estudiar código general, pedagógicamente aceptable pero de nula o
ligerísima aplicación en el mundo real. Este libro trata, precisamente, de los com-
ponentes reutilizables que con la frase "no reinventen la rueda" se suelen obviar
en los textos introductorios de C++. La obra, resultando agradablemente breve,
ofrece, tras una corta aproximación a los conceptos muy básicos de Orienta-
ción-a-Objetos y OOD, código de herramientas tan utilizadas como listas enlaza-
das, árboles binarios, arrays dinámicos, etc. El texto se acerca, desde la óptica
del OOD, a la codificación de cada uno de estos componentes, para ofrecer los
listados completos en la última parte del libro. Se proporcionan, también, algunas
líneas para mejorar distintos aspectos de los programas C++, aunque, por estar
basado en AT&T C++ 2.0, no incorpora, como sería sobremanera deseable, plan-
tillas (templates), supliéndolas mediante macros genéricas del preprocesador. El
autor expresamente autoriza el uso comercial de los elementos software que apa-
recen en el libro, respetando el aviso de copyright, y de su calidad comercial dan
fe distintas aplicaciones bien conocidas en las que aparece tal aviso. Con todo, y
en contra de lo que el lector pudiera esperar, el texto no acompaña diskette, por lo
que al posible usuario le esperan largas veladas de tedioso teclear. Naturalmente
éste no es un libro introductorio al lenguaje -pues se supone que el lector ya cono-
ce C++- sino, quizás, uno de los mejores candidatos para despegar de la etapa
inicial de aproximación a C++. El texto adolece, con todo, de cierta lineal terque-
dad, producto hoy de un regusto demasiado monocorde. Vamos, que se trata de
un texto ya antiguo (pero recapacite el lector que hasta de la Biblia se desprende
algún mensaje, por lo visto). Ciertamente las actuales bibliotecas de clases, con
todo su código accesible y miles de páginas de documentación, dejan bien chi-
quita a la intencionalidad de este texto: su única descarga es que ... resulta más
barato. Vamos, que es como ir al cine a ver Viernes 13th parte 1 (quizás la mejor
de la serie).

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 202
On to C++, por Patrick Henry Winston, 1994, Addison-Wesley, 0-201-58043-8.
Reconozco que que este libro lo adquirí coercionado por un tanto de morbo hacia
su autor: Winston, del MIT, padre de la Inteligencia Artificial moderna y guru tecno-
lógico que ... “bueno, bueno, veamos qué cuenta éste” me dije. El libro, al fin, es
ciertamente curioso, pues expone en secciones breves enseñanzas de muy po-
cas líneas calificadas por epígrafes numerados (hasta 795), insertas en capítulos
que empiezan, sin variación, por “Cómo hacer ... (How to)”. Así tenemos: “Cómo
escribir programas que hallan funciones-miembro en tiempo de ejecución”, “Có-
mo construir constructores que llaman a otros constructores”, “Cómo diseñar cla-
ses y jerarquías de clases”, etc., etc. En fin, la brevedad no da cancha a la tonte-
ría, aunque en este caso tampoco a la profundidad (pero infiero que no era tal su
intención). Los amantes de aforismos encontrarán aquí su sustento, pero no los
buscadores de epigramas. Tan árido como la IA, pero más práctico.

C++ Primer, 2nd Edition, por Stanley B. Lippman, 1991, Addison-Wesley, 0-


201-54848-8, 614 pág.
Nos encontramos ante lo que podría ser calificado como el texto estándar (por
oficial) de introducción a C++. En él se detalla la especificación AT&T C++ 3.0 (y,
por tanto, plantillas y clases anidadas) de una forma extremadamente rigurosa y
pulida: los escasísimos errores o carencias del texto se han debido, como es po-
sible apreciar en comp.lang.c++, a circunstancias ajenas al autor. No se le supone
al lector conocimiento previo alguno de C++ (ni siquiera una media experiencia
en C), y la exposición gradual de las características del lenguaje está perfecta-
mente modulada. O sea, se trata del libro ideal para iniciarse, sin la ayuda externa
de un profesor (aunque ésta es, en general, de valiosísima consideración), en los
tópicos y recovecos del lenguaje. Lippman empieza muy pronto en la obra con la
codificación de la clase "Array", y el lector puede asistir al proceso de su refina-
miento, mediante la aparición en el texto y acertado comentario de nuevas carac-
terísticas que se le van añadiendo, involucrándose y comprometiéndose poco a
poco en el proceso. El libro está repleto de instructivos ejercicios, de factible re-
solución, a la vez que de ejemplos "vivos": el lector ve, sorprendido, cómo éstos
van incorporando con extrema facilidad nuevos detalles conforme avanza el rela-
to. Todas las secciones del libro muestran en la práctica, pues, los conceptos teó-
ricos que se desprenden del lenguaje. Se exponen, por otro lado, una buena can-
tidad de interesantes y bien probados trucos de programación, que el lector po-
drá apreciar "en su salsa". Lippman dedica también un capítulo a OOD (Diseño
Orientado-a-Objetos), substanciado, como es habitual en el resto del libro, en un
ejemplo apropiado. La técnica de OOD empleada es simplísima: se trata de iden-
tificar las clases necesarias a nuestra aplicación para dotarlas seguidamente de
los interfaces apropiados, y establecer las relaciones entre ellas. Se trata, pues,
más que de una exposición de OOD, una indicación de la importancia que se le
debe conceder al uso, expreso o no, de los conceptos de OOD a la codificación
en C++. En definitiva: este libro es fundamental para el principiante, mientras que

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 203
para el experto se convierte en paradigma de cómo debe escribirse un buen texto
de introducción.

Mastering Object-Oriented Design in C++, por Cay S. Horstmann, 1995, John


Wiley & Sons, 0-471-59484-9.
El título es ciertamente engañoso, porque el “Master” significa aquí “Iniciación”.
“¡Oh, dios mío! ¡Otro libro de introducción al lenguaje!” exclamará el cansado lec-
tor. Pues sí. Pero el autor, columnista habitual de C++ Report e imparcial revisor
de ideas y herramientas C++, merece, al menos, el beneficio de la duda. La in-
tención de Horstmann queda, con todo, meridianamente clara en el prefacio de la
obra, en el que expone que hubo de montar este texto para satisfacer las necesi-
dades de un curso de C++ en la San Jose State University, porque otros libros no
servían: así el de Lippmann “cubre cada aspecto de C++ en relación a su comple-
jidad, no a su importancia”, mientras que las comparaciones de lenguajes del de
Budd distraerían a los estudiantes de su aprendizaje de C++, a la vez que los tex-
tos de análisis y diseño (como los de Booch y Rumbaugh), pese a su bondad con-
ceptual, no resultan prácticos para estudiantes sin experiencia en el desarrollo de
grandes proyectos. Con estos supuestos el autor plantea un libro que no intenta
ser completo respecto de C++, sino mostrar el subconjunto de mayor uso práctico
del lenguaje junto con guías para su adecuado uso. A la vez, y otra vez pese al
título, el libro no trata de análisis o diseño orientados-a-objetos, pues en las pocas
ocasiones que el autor aborda tales temas lo hace desde la perspectiva de las
fichas CRC o, en todo caso, usando de la notación Booch-Lite. El resultado es un
libro enormemente práctico que presenta una eclecsis razonable de tópicos so-
bre el lenguaje y que, además, lo hace de forma prudente y agradable. No sé si
una universidad española recomendaría este libro tan práctico, pero los lectores
que todavía estén pisando la tierra agradecerán tan sincero enfoque, alejado de
alquimias intergalácticas sobre estructuras de datos y, sobre todo, situado en la
misma dimensión de los problemas que el lenguaje intenta solucionar.

Object-Oriented Design for C++, por Tsvi Bar-David, 1993, Prentice-Hall, 0-13-
630260-2.
El autor pretende aplicar los conceptos de la orientación-a-objetos al lenguaje
C++ a la vez que intenta construir un puente entre tales conceptos y “el mundo de
los métodos formales y las especificaciones formales”. Claro que el lector, yo
mismo y los periodistas científicos saben que tal no se consiguió, pues todavía se
suscitan debates sobre si métodos formales sí o no, y acaso cómo, en ingeniería
de software38. El texto tiene, con todo, un agradable aunque liviano regusto formal
y abunda sobremanera en la importancia evidente de “precondiciones” y “post-
condiciones” e intenta imponer éstas en un estilo de codificación en C++ loable

38
¿Oigo exclamaciones airadas? Reclamaciones a IEEE y ACM.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 204
pero en buena medida poco práctico, y es que el lenguaje C++ no aporta ninguna
facilidad embebida para el tratamiento de estas cuitas (como sí lo hace, por
ejemplo, Eiffel). Se pontifica también en este texto sobre la extrema importancia
de las jerarquías de subtipado como extensión e interrelación de las abstraccio-
nes de datos, ideas que parten del artículo “Data Abstraction and Hierarchy” de
Barbara Liskov (que aparece reproducido en su integridad en este libro) y que
influyó de forma decisiva en el autor. En cualquier caso el libro es una sorprenden-
te introducción “formalista” a C++ y está tan empedrado de buenas intenciones
como el infierno (que diría don Ramón, así que no vendrá mal a más de un cabe-
za-hueca que piense que manuales, referencias y normas de estilo sólo han de
ser leídos por programadores incompetentes. Una cosa hay que decir: en el am-
biente universitario el adjetivo “formal” posee poderosas connotaciones atractivas
que suelen crear adicción y cierta psicosis denominada “de método científico”, de
forma que a nadie le habrá de extrañar que esta obra se use como texto en un
primer curso de C++.

The C++ Programming Language, 2nd Edition, por Bjarne Stroustrup, 1991,
Addison-Wesley, 0-201-53992-6, 669 pág.
Esta es la segunda edición del texto que el Dr. Stroustrup publicó en 1.986 deta-
llando el lenguaje C++, en su calidad de creador del mismo. Se trata de un tutorial
del lenguaje en el que, a diferencia del texto de Lippman, se enfatizan los aspec-
tos claves de uso del mismo. Se asume que el lector tiene experiencia previa en
programación en C, y se detalla la especificación AT&T C++ 3.0 partiendo "de
cero". El estilo del texto es enormemente sintético, de manera que la cantidad de
tópicos revisados es netamente superior a la del texto de Lippman. Comparada
con la primera edición, la obra ha crecido también considerablemente en número
de páginas. Precisamente ahora, cuando se empieza a generalizar el uso de
plantillas (templates) y se empieza a asentar la posibilidad práctica de uso del
tratamiento de excepciones, las páginas de este libro sobre tales materias se
constituyen en una insustituible guía referencial a la vez que conceptual, pues el
conocimiento de la mera sintaxis es claramente insuficiente. Si en la primera edi-
ción se revisaba la biblioteca que entonces se denominaba "stream", implemen-
tada por el propio Stroustrup, aquí se repasa la biblioteca "iostream", sobre las
bases desarrolladas por Jerry Schwarz. Se muestran y discuten algunos intere-
santes trucos y técnicas del lenguaje y el texto termina con un manual de referen-
cia. Una sección importante del libro la constituye (120 páginas) la dedicada a
OOD (Diseño Orientado-a-Objetos), en la que, usando de una extensión de la
técnica de las fichas CRC (de la que el lector encontrará una sucinta descripción
más adelante, en el texto de Wirfs-Brock), se introduce con acierto al lector en
esta disciplina. Bien, se trata de un libro muy difícil de resumir por su buscada
tersedad conceptual y de estilo, pero es indiscutible que, quizá con el soporte
previo de una obra introductoria como la de Lippman, su posesión es indispen-
sable para cualquier programador de C++. Estúdienlo, sin más condiciones.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 205
The Annotated C++ Reference Manual, por Margaret A. Ellis & Bjarne Strous-
trup, 1990, Addison-Wesley, 0-201-51459-1, 447 pág.
Esta es la biblia del C++: el texto de referencia indispensable para cualquier es-
tudioso, interesado o programador del lenguaje. Y biblia es aquí un término usado
con intencionada literalidad: acaloradas discusiones entre miembros de la comu-
nidad C++ suelen zanjarse con citas del tipo "la sección 13.2 de ARM (el apelati-
vo cariñoso del texto) dice: ...", que surten el efecto del mágico ensalmo que sólo
puede emanar de la autoridad por todos reconocida. El texto de este libro fue to-
mado, en su momento, como documento base por el comité ANSI C++ para la
estandarización del lenguaje, y la carencia, en estos momentos, de tal estándar
ha redundado en confirmar como inamovible tabla de salvación, en el complicado
mar sintáctico y conceptual de C++, a esta obra. Nos encontramos ante un texto
eminentemente referencial, que no pretende enseñar al lector a usar los meca-
nismos del lenguaje, sino más bien a describirlos en una forma tersa y rigurosa,
pero que, fundamentalmente en las notas, explicita cuestiones sobre la naturaleza
de C++ que pueden llevar al lector a comprender mejor la esencia del lenguaje:
por qué tal o cual característica no ha sido contemplada, o por qué tal otra ha sido
implementada de esta determinada manera, o cómo podrían suplirse comporta-
mientos no previstos en el nudo lenguaje, etc. Nótese que la primera línea del pre-
facio dice así: "Este libro provee una completa referencia del lenguaje para el
usuario experto de C++". No se pretende que el lector estudie de golpe, del prin-
cipio al final, el texto, pues tal tarea sería como la de intentar abarcar por orden
alfabético una vasta enciclopedia (y esto trae reminiscencias, no obstante, de
aquel "autodidacto" de la nausea sartriana): queda, pues, como una insustituible
obra de consulta. De cualquier manera, y dado que desde mayo de 1.995 el bo-
rrador del estándar está disponible (en Internet) para el público en general, esta
obra, eminentemente referencial, ha perdido parte de su valor práctico, conser-
vando empero todo su valor histórico.

The Design and Evolution of C++, por Bjarne Stroustrup, 1994, Addison-
Wesley, 0-201-54330-3.
Este libro no versa sobre C++ sino sobre las decisiones de diseño que han con-
ducido al lenguaje, en lo esencial, a su actual configuración. Stroustrup posee un
tono exacto y pragmático que convierte su texto en un alegato contra la tontería,
más allá de consideraciones beligerantes entre lenguajes. Yo diría que su ante-
cedente más cercano es el inteligente libro de Bertrand Meyer “Object Oriented
Software Construction”, 1988, donde se explicitaban también las decisiones de
diseño del lenguaje Eiffel. Así que ... ¡alto! ¿Oigo las quejas de algún lector?
Humm, las comparaciones no son odiosas: la estupidez y la banalidad, en contra-
partida, sí son odiosas. Considero que aunque C++ se pervirtiera y sólo se usara
en el BOE, el libro seguiría siendo absolutamente recomendable. Si uno compara
las decisiones de diseño que se notan en este libro con las de otros lenguajes
como Java, el resultado es escandaloso. Al fin: racionalidad seria para un texto

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 206
inteligente y sentido. No se lo pierdan.

Algorithms in C++, por Robert Sedgewick, 1992, Addison-Wesley, 0-201-


51059-6.
¿Un libro de algoritmos? Humm, la verdad es que me veo un tanto forzado a in-
cluir algo así en esta sucinta reseña bibliográfica. Lo que me hizo recapacitar so-
bre esta necesidad fue una anécdota curiosa acontecida en la Universidad de
Alicante: portaba yo, para preparar una ponencia, varios libros sobre C++, a cual
más interesante, y mi sorpresa fue constatar el repentino interés de todo el perso-
nal del departamento por lo que yo pensaba un anodino libro sobre C++, cual es
éste del Sr. Sedgewick, a la postre conocido por su trabajo desde la Universidad
de Princeton sobre algoritmos en todos los lenguajes conocidos. Ciertamente la
presente obra no es más que el transporte, a veces prudente y usualmente atro-
pellado, del anterior texto del autor “Algorithms in C” al dominio (si se puede men-
tar este vocablo) del C++. Claro que el texto en C parecía resultar tecnológica-
mente inadecuado para las avanzadas mentes universitantes (por su estructurada
antigüedad), así que C++ parecía una adecuada buzzword para unir a Algoritmos.
Oh, el texto no está mal. De hecho yo aconsejaría que nadie se comprara ninguno
de los otros lesivos textos sobre algoritmos en C++ que pueblan el mercado (Alan
Ladd y compañía, mayormente), pero el hecho es que la implementación adolece
de muchas cosas, y principalmente es anterior al “descubrimiento” de la STL. Así
que ya saben: si quieren algoritmos, vayan a la fuente de Donald Knuth; si quieren
C++ vayan a los textos clásicos; y si lo que quieren es exprimir el tiempo, acudan
a este texto, sin duda el mejor de los que pudieran encontrar de este estilo.

LIBROS DE ESTILO EN C++

Tras superar la etapa introductoria y asimilar los recursos del lenguaje, lo siguien-
te es aprender a codificar en C++ con efectividad y limpieza. Los textos que se
detallan a continuación pretenden cubrir la etapa intermedia de formación del
programador de C++, recalando más que en la sintaxis en los esquemas concep-
tuales que subyacen bajo ésta.

Effective C++: 50 Specific Ways to Improve Your Programs and Designs, por
Scott Meyers, 1992, Addison-Wesley, 0-201-56364-9.
Este libro es, en esencia, una colección de 50 consejos sobre programación en
C++, debidamente comentados y justificados. Pero no se trata de un recetario al
uso, sino más bien de un compendio de líneas maestras, interrelacionadas entre
sí (como el lector pronto descubre) y tendentes a procurar al lector este tipo de
conocimiento que subyace tras la seca sintaxis del lenguaje. Así, aunque el len-
guaje permite usar con libertad la derivación pública, la aproximación de éste al
espíritu de OOD exige que tal derivación se use solamente cuando se da una re-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 207
lación de subtipo (o sea, de "ES-UN") entre las clases a relacionar. El lenguaje
permite, también, por ejemplo, la redefinición en clases derivadas de funciones
miembros no-virtuales de las clases base: esta práctica vulnera, sin embargo,
como bien indica Meyers, la cualidad de coherencia de los programas: el com-
portamiento de un objeto variará dependiendo del puntero desde el que sea ac-
cedido. Como vemos, los casos estudiados son de enorme utilidad práctica. Jun-
to a éstos se exponen técnicas que suelen pasar desapercibidas a los principian-
tes en C++, como, por ejemplo, el chequeo de auto-asignación en la implementa-
ción del operador de asignación de una clase. Así, nos encontramos en el índice
con secciones como las siguientes: "Nunca redefinas el valor heredado de un pa-
rámetro por defecto", "Cualifica los destructores como virtuales en las clases ba-
se", "Chequea el valor de retorno de new", "Evita los datos miembros en el inter-
faz público", para terminar con "Lee el ARM". El libro, totalmente recomendable,
proporciona al lector la inteligencia de cómo usar los recursos sintácticos de C++
asimilados de los textos introductorios clásicos. Además, su lectura es tan amena
como la de una novela policial. Y para los que gusten de segundas partes he de
decir que Meyers amenazó con más consejos en su nuevo libro.

C++ Programming Style, por Tom Cargill, 1992, Prentice Hall, 0-201-56365-7
Junto con el libro de Meyers, configura una de las mejores inversiones en papel
impreso que puedan realizar los ya iniciados en el lenguaje C++. El enfoque
adoptado por Cargill es, no obstante, distinto al de aquél: en vez que acopiar re-
glas parciales y supuestamente independientes (como una shopping list), el libro
reúne porciones de código extraídas de otros textos de C++ y de productos co-
merciales, para luego examinarlos con el ojo crítico del estilo. Es sorprendente la
cantidad de elementos perturbadores, y aun de graves errores, que Cargill hace
aparecer ante nuestros ojos, demasiado acostumbrados a las revisiones rutina-
rias y a los esquemas del mínimo esfuerzo. Se tratan, así, temas como "Herencia
innecesaria", "Consistencia de clases", etc. analizando codificaciones de clases
como "String", "Stack", "Máquinas de Estados Finitos", etc. Cargill pretende im-
buir al lector de determinadas reglas de buen estilo en C++, tales como las si-
guientes: "Identifica el delete para cada new", "No uses un constructor para inicia-
lizar datos miembros estáticos", "Considera los argumentos por defecto como
una alternativa a la sobrecarga de funciones", etc. Como se puede fácilmente
apreciar, el texto no tiene desperdicio. El tipo de conocimiento que procura es,
por otro lado, de una madurez matizadamente más clara que en el anterior texto,
pues el lector adquiere una visión global de la cohesión del estilo a imprimir al
código. El mismo Scott Meyers reconoce su proximidad a este texto, indispensa-
ble a todo programador de C++.

C++ Programming Guidelines, por Thomas Plum & Dan Saks, 1991, Plum
Hall, 0-911537-10-4, 274 pág.
Los que experimenten cierta aversión por las colecciones de reglas y los manua-

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 208
les de estilo, encontrarán en este libro el arquetipo de sus pesadillas. Al menos en
el formato. Se trata, en efecto, de secciones codificadas que observan el siguien-
te esquema: TÓPICO (por ejemplo, 6.06 virtual_fct - funciones virtuales), REGLA
(descripción de las líneas maestras de estilo correspondientes al tema del título),
EJEMPLO (demostración práctica de la problemática contextual en que se desa-
rrolla la regla), JUSTIFICACIÓN (argumentación de apoyo, tanto mediante razo-
namiento directo como a través de informes y textos externos, de la regla expues-
ta), ALTERNATIVAS (opciones de la regla consideradas menores, o aun imprac-
ticables), NOTAS LOCALES (media página al menos, y normalmente página y
media, en blanco para el supuesto apunte de notas por el lector). Bien, decíamos
que una tal rígida esquematización pudiera resultar demasiado restrictiva. El pre-
sente texto es, sin embargo, enormemente provechoso. Muy en la línea de un an-
terior libro de los mismos autores ("C Programming Guidelines"), sus distintas
secciones tienden a reforzar una idea que los principiantes en C++ rápidamente
relegan al olvido: C++ no es C. C++ no es, tampoco, Smalltalk o Eiffel. Se trata de
sistematizar las soluciones a los cuellos de botella en la gestión de proyectos en
C++, fundamentalmente cuando intervienen equipos de más de dos personas: a
este fin se explicitan convenciones para nominar identificadores, facilidades para
comentar código, gestión de sobrecargas, etc. El libro es, en definitiva, un com-
pañero a tener muy en cuenta ante el diseño efectivo de clases realmente cohesi-
vas y portables.

Taligent’s Guide To Designing Programs: Well-Mannered Object-Oriented


Design in C++, por Taligent Inc., 1994, Addison-Wesley, 0-201-40888-0.
Conocido el resultado fallido del ambicioso proyecto Taligent (al menos como
joint-venture de multinacionales de marca), pudieran parecer fuera de lugar los
productos derivados de tal heroica (en más de un sentido) gesta, libros incluidos.
Pero no, claro. Cualquiera que haya de desenvolverse con C++ en un equipo de
más de tres personas debería leer este libro, pues en él se detalla un extracto de
una suerte de “manual corporativo de estilo en C++” al uso en el entente Taligent.
Naturalmente la naturaleza del producto a conseguir (básicamente el conjunto de
frameworks “CommonPoint”) matiza y personaliza las decisiones de uso y diseño
del lenguaje, pero yo diría que casi todo lo aquí expuesto es, más que razonable,
deseable. C++ es un lenguaje difícil y, enfrentados a él, las corporaciones necesi-
tan determinar primero qué subconjunto del mismo utilizar, para después estable-
cer las normas de uso de tal subconjunto. Este libro es una suerte de manual en el
que se detalla una interesante normativa de codificación de identificadores, a la
vez que detallan consejos del tipo: “Implemente protocolos completos en clases
derivadas”, “Exprese todos los interfaces mediante objetos”, “Use objetos estáti-
cos en lugar de temporales”, etc. Naturalmente todo esto sobra para codificar una
pila o un árbol rojo-negro, pero resulta imprescindible para sacar a flote cualquier
proyecto no trivial en C++.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 209
LIBROS DE PROGRAMACIÓN MEDIA Y AVANZADA EN C++

Este es un apartado singularmente despoblado en el actual panorama editorial


sobre C++. No hay que dejarse engañar, por supuesto, por títulos como "Inteligen-
cia Artificial con Borland C++ 4.5" o "Fabrique su propia red neuronal con Visual
C++ 2.0". Los tópicos avanzados del lenguaje constituyen, en la mayoría de los
casos, un universo impensable por el lector de introducciones. En este caso,
pues, la selección ha resultado evidentemente fácil. He preferido, con todo, reunir
los textos bajo el epígrafe común "medios y avanzados" para evitar tener que cali-
ficar como avanzada únicamente a dos obras (que dejo como adivinanza). En fin:
lo mejor será que el lector juzgue y establezca su propia ordenación.

C++ IOStreams Handbook, por Steve Teale, 1993, Addison-Wesley, 0-201-


59641-5.
¿No se le atragantó siempre al lector ese inexorablemente corto capítulo dedica-
do a los “streams”? Oh, streams por aquí, iostreams por allá. ¿No se le dio dema-
siado bombo a algo que no parece tener demasiadas posibilidades? Bueno, yo
les diría que dos de las preguntas que planteo en mis evaluaciones de conoci-
miento de C++ a equipos de desarrollo son: “¿Ha personalizado la biblioteca de
Streams?” y “¿Cuántos manipuladores ha creado?”. La evaluación no es cierta-
mente inequívoca, pero las respuestas han resultado siempre escandalosamente
sintomáticas. Hasta ahora un aprovechamiento efectivo de la bien diseñada bi-
blioteca de Streams únicamente podía provenir de la lectura trabajada del código
fuente y, si acaso, de los papeles de ANSI X3J16 C++. El presente libro viene a
cubrir tal hueco, aprovechado sin piedad por articulistas malsanos que han ma-
lenseñado con saña a abusar de esta elegante biblioteca (y no citaré revistas
como PC World, PC Magazine, Sólo Programadores, etc., reas de garrote vil
“streamico”). Oh, la satisfacción de leer un libro técnico bien fundado en C++ es
inexpresable, pues proporciona indicios de vida inteligente en algún ámbito edito-
rial. Bueno, yo no diría que este es un texto indispensable para todos los progra-
madores de C++: sólo lo es para aquellos programadores de C++ que restañan
código cuando teclean. Cuestión de calificación, claro (y aquí debo notar que, tras
la euforia, el nivel técnico en este libro alcanzado no es estratosférico: más bien
se trata de un pequeño puerto sobre el nivel del mar: cuestión de cultura C++).

Advanced C++ Programming Styles and Idioms, por James O. Coplien, 1992,
Addison-Wesley, 0-201-54855-0.
Este es uno de esos escasos libros que redimen, con sobriedad textual y derro-
che de inteligencia, el aluvión de tonterías y libros triviales sobre C++ y OOP que
nos están inundando de forma inmisericorde. Al Stevens ha llegado a decir que
este texto representa para C++ lo mismo que el de Kernighan & Ritchie represen-
tó para C: bien, quizá este comentario se limite a exteriorizar una pasión (fácil de
comprender, por otra parte), porque sería más lógico aplicar tal comparación al

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 210
texto de Stroustrup, pero ciertamente indica un cierto estado de ánimo que per-
manece al terminar el texto. Siendo este libro uno de mis preferidos, mi única re-
comendación es: ¡cómprenlo!. Pero, ¿de que trata esta obra? ¿De técnicas más
o menos elaboradas de codificación en C++? Bien, no exactamente. Tras una
breve, rigurosa y modélica introducción a los más interesantes tópicos del lengua-
je, el autor nos anuncia de la existencia, dentro del lenguaje C++, de "idiomas"
autónomos: esto es, de abstracciones por encima del nivel sintáctico elemental
del lenguaje y que configuran, a partir de distintos supuestos, unas formas de co-
dificar y un sustrato conceptual esencialmente distinto entre cada una de ellas.
Recordemos, por ejemplo, que Stroustrup nos indica que los constructores en
C++ no pueden ser virtuales, pero, a la vez, expresa que con facilidad se puede
suplir tal característica. Coplien (Cope para sus amigos) habla, sin embargo, de
un "idioma de constructores virtuales". Tomemos como ejemplo este idioma para
que el lector pueda entender mejor qué se esconde tras tal denominación. Un
mensaje virtual permite posponer al tiempo de ejecución la ligadura entre prototi-
po e implementación de la función asociada; o sea, el mensaje se enviará a un
objeto, desconocido en tiempo de compilación, y éste responderá de la forma
más apropiada. Un constructor virtual supone lo siguiente: a un objeto, cuyo tipo
exacto desconocemos, se le envía el mensaje "constrúyete", y éste eligirá el mé-
todo de construcción apropiado pues, ¿quién habría de saber más de construirse
que el objeto en sí?. Mediante la estructuración de una codificación que permita
simular este comportamiento nos podríamos encontrar, por ejemplo, con que nin-
guno de los tipos de los objetos de nuestra aplicación sería chequeado en tiempo
de compilación, pues el tipo exacto de un determinado objeto no nos haría falta ni
siquiera para construirlo. Piense el lector que, de un plumazo, nos hemos "carga-
do" una de las cacareadas características de C++: el fuerte chequeo de tipos.
Piense ahora el lector cómo sería codificar en C++ sin el chequeo de tipos: ¡mu-
chas de nuestras ideas predeterminadas habrían de cambiar! ¡muchísimo de
nuestro código ya no tendría sentido! Es como si, de repente, estuviéramos traba-
jando con un dialecto de C++: con un "idioma". Coplien describe algunos idiomas
adicionales en su libro: "idioma de ejemplares", "idioma de estructuración en blo-
ques", "idioma de lenguajes simbólicos", "idioma de herencia dinámica múltiple",
etc. Ningún programador de C++ que aspire a algo más que a la codificación de
una lista enlazada debería dejar de leer este texto. Hay que recabar, no obstante,
que al lector se le supone un conocimiento adecuado de AT&T C++ 3.0, versión
en que la obra está basada. Una última nota: los apéndices son realmente intere-
santes, y convienen en procurar al lector un agradable sensación de efectividad.

The Power of Frameworks for Windows and OS/2 Developers, por Taligent
Inc., 1995, Addison-Wesley, 0-201-48348-3.
Teniendo en cuenta la disolución del trió Appel-HP-IBM, la inclusión de este texto
pudiera parecer vana. Lo cierto es que aun habiendo desaparecido el entente
conjunto, IBM finalmente se quedó con la tecnología, y ésta se repartirá en sus
productos de forma inexorable (como ya está ocurriendo). El texto es francamente

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 211
comercial, como un buen catálogo técnico, e intenta susurrarnos a cada página
“¿Viste que buena idea? ¿Observaste que inteligente implementación? ¿Notaste
que elegante jerarquía?”. Lo mejor de todo es que las ideas, implementaciones y
jerarquías son endiabladamente buenas, así que un repaso amable de estos
“frameworks” seguro que aclara a más de uno las ideas sobre qué pedirle a una
biblioteca-entorno de clases. De hecho este libro suple una carencia habitual en
la documentación de las bibliotecas comerciales de clases: índica algunas
decisiones de diseño. Por lo demás es una buena lectura de verano y un
agradable repaso de un ambicioso proyecto que quizás debiera haberse
planteado en otro lenguaje de programación (y ésta es una opinión de Andy
Novobilsky).
Data Abstraction and Object-Oriented Programming in C++, por Keith E. Gor-
len, Sanford M. Orlow & Perry S. Plexico, 1990, John Wiley & Sons, 0-471-
92346-X, 403 pág.
Este texto está basado en el estudio realizado por los autores para proveer de
optimizaciones software a los sistemas UNIX de los Institutos Nacionales de Sa-
lud (NIH) en USA. Se trata, en esencia, de una biblioteca de clases envolvente
(conocida como biblioteca NIH) modelada a imagen y semejanza del entorno
Smalltalk-80 y prescindiendo de las capacidades gráficas de éste. El libro exige
un solvente conocimiento previo de C++ y se apoya en AT&T C++ 2.0. Usando la
terminología de Coplien, podríamos decir que aquí se genera un "idioma tipo
Smalltalk", y lo cierto es que lo que en el texto se expone ha tenido una extraordi-
naria y reconocida influencia en el desarrollo de un gran conjunto de entornos de
aplicación y bibliotecass de clases genéricas. El provecho que los programado-
res de C++ pueden extraer de la obra es evidente, pues ésta está montada como
un tutorial, explicando detalles y decisiones de diseño de las clases (algunas de
ellas inapreciables para la adecuada construcción de componentes reutilizables,
una de las expectativas más cacareadas y, a la vez, más difíciles de C++) y, tras
esto, directamente utiliza el entorno creado como herramienta de desarrollo, ex-
plicando la posible extensión del sistema. La biblioteca NIH incluye clases como
Iterador, Bolsa, Diccionario, Tiempo, Vector, Fecha, Colección, Pila, Objeto (la
raíz de la biblioteca "cósmica", pues tal es el nombre por el que se conocen las
bibliotecas con una única clase base inicial). Como el lector puede fácilmente
intuir, este tipo de bibliotecas propende al usuario a practicar la derivación múlti-
ple, y dadas las dificultades no siempre evidentes que esto entraña, el libro dedi-
ca un largo capítulo a este respecto. Se detalla, incluso, un ejemplo de aplicación
de base de datos usando la biblioteca. ¿Mi sugerencia? Bien, el fuerte entronca-
miento de los conceptos de OOD con el acertado uso de C++ procuran un exce-
lente texto de uso referencial, de valiosísima lectura para cualquier programador
de C++ y aun de los estudiosos de OOP; las ventajas y defectos del enfoque tipo
Smalltalk adoptado, por otro lado, disgustarán o deleitarán al lector, dependiendo
en buena medida de su estilo y costumbres, pero esto aguzará, alternativamente,
el ojo crítico o el gozo del lector. ¡Léanlo!

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 212
C++ Strategies and Tactics, por Robert B. Murray, 1993, Addison-Wesley, 0-
201-56382-7, 273 pág.
Como el mismo título indica, Bob Murray, editor durante muchos años de C++
Report, ha querido mostrar, como en ajedrez, las estrategias y componendas que
trascienden los meros movimientos de las piezas del juego. No es exactamente,
en general, un libro "avanzado" sobre el lenguaje, sino que más bien ocupa ese
estadio intermedio entre los libros de estilo y la practicidad del código realmente
efectivo. En este sentido los dos capítulos dedicados a las plantillas ("templates")
justificarían, por sí solos, la lectura del libro, pues dada la novedad comercial de
esta adición al lenguaje, pocos textos se han ocupado hasta la fecha de revisar,
de forma seria, algunos aspectos no triviales de la misma. Junto con las plantillas,
el buen diseño de jerarquías de herencia en C++ y la reusabilidad del código ocu-
pan el grueso de la obra, que, además, procura un capítulo sobre el manejo de
excepciones. Nos encontramos, pues, ante una obra moderna y de potente clari-
dad, pensada para el lector no-experto y que contiene ejercicios, resúmenes y
una buena cantidad de ideas y sugerencias de buen diseño en C++. En definitiva,
una adición indispensable para la biblioteca de C++.

Designing and Coding Reusable C++, por Martin D. Carroll y Margaret A. Ellis,
1995, Addison-Wesley, 0-201-51284-X.
¿Reúso? ¿No es la característica esencial y automática de toda codificación en
C++? ¿No es una de las bases de la Orientación a Objetos? ¿No está ciertamen-
te de más un libro sobre algo que resulta evidente a todo programador en C++
imbuido en los fabulosos esquemas de la Programación Orientada-a-Objetos?
Bueno: sí, no, sí y no, respectivamente. A la Dra. Ellis la reconocerá el lector como
co-autora del ARM y co-columnista junto con Carroll (de AT&T Bell) en la revista
“C++ Report”. El libro es francamente bueno y muestra lo que da de sí un lenguaje
dúctil como C++: si en la mayoría de los textos al alcance del hirsuto lector se pri-
ma la eficiencia como alma mater del lenguaje, aquí se ensalza la capacidad de
reutilización por encima de aquélla. La buena noticia es que los autores analizan,
para cada posible tópico, los inconvenientes de la fijación en el reúso, y propor-
cionan estrategias, técnicas y trucos que habrán de incorporar el acerbo de expe-
riencia del programador prudente de C++. Las plantillas se utilizan profusamente
en el texto (que expresamente se desdice de los contenedores de tipo Smalltalk)
y el tono escéptico respecto del reúso de código es realmente de agradecer. Con
los pies bien plantados en tierra, la obra habla de diseño de clases, extensibili-
dad, eficiencia, errores, conflictos, compatibilidad, jerarquías de herencia, porta-
bilidad, uso de bibliotecas de clases ajenas, documentación y otros interesantes
tópicos (el lector avisado notará que he descrito el índice). Las ideas que aquí se
dan sobre excepciones, herencia invasiva, el uso de “const”, clases endógenas,
instanciación de plantillas, etc. son de una practicidad tal que el lector no podrá
por menos que exclamar: ¿Cómo nadie me recomendó este libro antes? En fin,
una obra indispensable.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 213
Taming C++: Pattern Classes and Persistence for Large Projects, por Jiri
Soukup, 1994, Addison-Wesley, 0-201-52826-6.
Teniendo el cuenta en panorama de obras sobre C++, el título anterior puede ha-
cer segregar jugos gástricos a más de un jefe de departamento o responsable de
proyectos no-triviales en C++. Bien: el texto, como aparece en su presentación,
versa sobre “las cosas obvias y simples que llevan a buen puerto o hacen
fracasar a los grandes proyectos”. De hecho su elección suele ir ligada al texto de
Taligent sobre estilo de codificación en C++, y se fundamenta en el sentimiento
de inseguridad que se genera al unir un equipo de personas mediante el pega-
mento C++. Aparte de los discutibles porciones dedicadas a la persistencia de
datos (un tanto forzadas por la actividad empresarial del autor) el libro está pla-
gado de revisiones, técnicas y consejos que intentan reflejar el éxito del autor en
su propia aventura C++ post AT&T. Así, por ejemplo, en el capítulo “Bibliotecas de
Patrones” se examinan estructuras, problemas de distintas bibliotecas actuales
(Tools.h++, NIH, Componentes C++ de Booch, etc.) para concluir con una biblio-
teca ideal: la implementada por Code Farms (la empresa del Sr. Soukup). Pero
en realidad esto es totalmente legítimo, pues nadie puede hablar de una expe-
riencia que no ha obtenido, y no hablar de experiencia es mentar el aire, claro.
Pues bien, el autor expone sus personales ideas sobre dependencias entre cla-
ses, tipos de datos abstractos, generación de patrones y ... persistencia. Es cier-
to, también, que la palabra resonante “patrones” en el título no encontró eco en la
comunidad establecida de patrones, así como las ideas del Sr. Soukup, siendo
radicales, son discutidas. Pero la verdad es, al fin, que este es uno de los pocos
libros que hablan de C++ en su relación con grandes desarrollos y, en este senti-
do, es imprescindible.

LIBROS SOBRE SMALLTALK

¿Smalltalk? ¿Por qué Smalltalk? Bueno, querido lector, Smalltalk es uno de los
lenguajes de más rancio abolengo dentro del mundillo de la Tecnología de Obje-
tos, y su influencia ha resultado decisiva en los aspectos prácticos de desarrollo
de las bibliotecas de C++. Además la literatura Smalltalk posee un tanto a su fa-
vor: al ser éste un lenguaje que aparentemente no está “en la cresta de la ola” y
que conserva un cierto rigor academicista (rigor mortis le llaman algunos) la tonte-
ría no ha penetrado de forma irremediable en su acervo bibliográfico. No se dan
así, al menos todavía, libros del tipo “Programe en 15 horas con Smalltalk, asom-
bre a sus amigos y dé un nuevo rumbo a su vida sexual”. Esto aparentemente
quiere decir que casi todos los libros actuales sobre Smalltalk son intelectualmen-
te legibles y no causan daños neuronales irreparables. Y la verdad es que, aunque
sin competencia respecto de C++, existe una buena cantidad de libros sobre
Smalltalk, pero, puestos a elegir, he aquí mi selección, aproximada y pretendida,
pero no necesariamente, en orden creciente de dificultad:

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 214
Programación Orientada a Objetos: Aplicaciones con Smalltalk, Angel Mo-
rales & Francisco J. Segovia, 1993, Paraninfo, 84-283-2019-5.
Es éste el único libro original en castellano que, de forma pedagógicamente re-
glada, procura una razonable introducción al lenguaje, bien aderezada, además,
de ejemplos prácticos completos cuya utilidad, sin embargo, no sobrepasa el
ámbito de la didáctica académica. Resulta curiosamente actual el tendencioso
ejemplo en el que Empresarios, Funcionarios, Intermediarios, Matones y Perio-
distas son, por fuerza, Políticos (derivan de esta clase), y un objeto de tipo Em-
presario soborna a un objeto de tipo Funcionario conocido como John War (nada
que ver con Monsieur Alphonse, a lo que parece). Los ejemplos son, en general y
según lo visto, muy amenos (no faltan las ubicuas “Torres de Hanoi”), a la vez que
la primera parte teórica es suficientemente exacta: se describe el dialecto Small-
talk-80 de forma que se consigue adentrar en los conceptos básicos del lenguaje
a lectores con conocimientos al menos básicos de algún otro lenguaje de pro-
gramación, quizás Pascal o similar. Se echa quizás en falta un disquete con el
código de los ejemplos, siempre deseable en Smalltalk por las facilidades de
experimentación con el mismo.

Smalltalk Programming for Windows, Dan Shafer con Scott Herndon y Laur-
ence Rozier, 1993, Prisma Publishing, 1-55959-237-5.
Este libro es una revampirización del texto “Practical Smalltalk: Using Smalltalk/V”
de Dan Shafer y Dean Ritz, publicado en 1990 por Springer-Verlag. La obra des-
cribe ejemplos prácticos sobre Smalltalk/V versión 2.0 para Windows, y asume un
conocimiento cuando menos elemental de Smalltalk y de la programación Orien-
tada-a-Objetos, abordando la enseñanza mediante el desarrollo completo y gra-
dual de siete proyectos bien engarzados y cuyo código completo se acompaña
en un disquete: un “prioritizador” (un secuenciador de ítems en base a priorida-
des), un contador simple (con botones de incremento y decremento), un calenda-
rio con interfaz gráfico, un agenda de citas (que se añadirá al calendario), un pa-
quete gráfico elemental, un diseñador de formularios y un reloj multiproceso. El
texto está muy pegado a la tierra, y el nivel es bajo-medio. Resulta aconsejable su
lectura tras el estudio de los manuales de Smalltalk/V, quizá demasiado etéreos.

Object Oriented Programming, Peter Coad & Jill Nicola, 1993, Prentice-Hall, 0-
13-032616-X, con disquete del código C++ y Smalltalk.
El libro resulta sorprendentemente fresco, detallado y específico comparado con
los anteriores de Coad y Yourdon sobre OOA/OOD, y, la verdad, resulta muy di-
vertido de leer: se puede acabar de un tirón. Mediante el análisis, diseño e im-
plementación de distintos pequeños proyectos, en creciente grado de dificultad,
pero siempre muy pedagógicos (un contador elemental, una maquina vendedora,
un sistema de soporte de ventas y un sistema de control de tráfico), se introduce
al lector en un discurrir donde se alternan C++ y Smalltalk, convirtiéndose así en

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 215
una perfecta obra ambivalente de introducción a ambos lenguajes. Los autores
intentan, a la vez -y parece que consiguen-, imbuir al lector en lo que denominan
“object-thinking”, una suerte de estructuración mental en la que se ven objetos por
doquier en cualquier área39. En fin: un texto ameno y recomendable, de nivel me-
dio.

Discovering Smalltalk, Wilf LaLonde, 1994, Benjamin Cummings, 0-8053-


2720-7.
Si sólo ha de leer un libro sobre Smalltalk, lea éste. Independientemente de si
conoce o no ya el lenguaje, de si sabe o no qué es un ordenador, de si proviene
de COBOL o C++, éste es su libro. Y si bien puede resultar chocante que al prin-
cipio por ejemplo se explique, aun de pasada, qué es un “disco flexible”, el lector
pronto descubrirá que casi inadvertidamente, en una forma que recuerda el énfa-
sis conceptual adscrito a Alan Kay, se va introduciendo en un lenguaje que no ne-
cesita de ningún bagaje informático anterior. La profusión de gráficos es impre-
sionante (a diferencia del resto de los libros, éste se puede leer perfectamente sin
tener Smalltalk delante) y el estilo francamente atractivo: los conceptos se introdu-
cen con fuerza, amenidad y detalle. No tienen excusa: lean y disfruten de un len-
guaje agradecido como pocos.

Smalltalk-80: The Language, Adele Goldberg & David Robson, 1989, Addison-
Wesley, 0-201-13688-0.
Este libro es un clásico y representa la segunda edición, convenientemente modi-
ficada y remozada, del anterior texto “Smalltalk-80: The Language and its Imple-
mentation”, familiarmente conocido como “libro azul”, de los mismos autores, pu-
blicado por Addison-Wesley en 1983, que contenía una interesante especifica-
ción, desaparecida en la presente edición, para la construcción de una máquina
virtual Smalltalk-80 (una buena razón para disponer de las dos obras). Este texto
se apoda “libro púrpura” y está estructurado como un manual de acceso y des-
cripción del lenguaje: la introducción sintáctica es seguida por un repaso de los
recursos del lenguaje-entorno para acabar con la exposición de distintas aplica-
ciones de simulación discreta orientada-a-eventos. El tono de la obra es exacto y
su lectura resulta cómoda y estimulante. En definitiva, como dicen los americanos,
un “must-have”.

39
Con esto pasa como con el tabaco y los vicios mayores: los más convencidos de la “objetivi-
zación mental” son precisamente los “estructuralistas arrepentidos”. Y la pregunta es ¿cuándo se
fundará “Estructuralistas Anónimos”? Hay ciertos pecados que necesitan pública confesión.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 216
Smalltalk-80: The Interactive Programming Environment, Adele Goldberg,
1984, Addison-Wesley, 0-201-11372-4.
Originalmente concebido como documentación de usuario del sistema Smalltalk-
80 de Xerox, debido a la extraordinaria integración del entorno con el lenguaje en
sí, este texto (denominado, según la costumbre, con el color de su portada: “libro
naranja”40) describe, en forma de manual tutorado, la interfaz de usuario del entor-
no-lenguaje, las técnicas de extracción de información de los objetos del sistema,
las operaciones CRUD sobre clases, la depuración del código y las relaciones
del entorno respecto del exterior. El libro presenta suficientes ilustraciones gráfi-
cas y el estilo es adecuadamente paternal. En fin, un manual “clásico”41.

Inside Smalltalk I, Wilf LaLonde & John Pugh, 1990, Prentice-Hall, 0-13-
468414-1.
Esta obra, junto con el segundo tomo, representa para los años 90 lo que para los
80 los textos de Goldberg y Robson. El papel impreso quédase obsoleto con mu-
cha rapidez, pero así y todo está obra es indispensable para todo aquél que quie-
ra trabajar de forma seria en Smalltalk. En este tomo se describen los fundamen-
tos del dialecto Smalltalk-80 versión 2 y de su entorno de programación, junto con
el detalle de las clases básicas y la gestión de imágenes gráficas.

Inside Smalltalk II, Wilf LaLonde & John Pugh, 1990, Prentice-Hall, 0-13-
465964-3.
Este tomo, inseparable compañero del anterior, se centra en la construcción de
aplicaciones con IGUs VIMP 42. Centrándose en el paradigma MVC (Modelo-
Vista-Controlador) con una profundidad difícil -si no imposible- de hallar en otros
textos, los autores introducen al lector con detalle, rigor y precisión en las opera-
ciones con ventanas, menús, ventanas emergentes, etc., para acabar con casi
200 páginas en las que se discute y expone el diseño e implementación de un
“constructor de ventanas” (una suerte de asistente que proporciona facilidades
gráficas para la creación de interfaces de usuario), una aplicación de tamaño
medio de gran interés. Se trata de una obra imprescindible.

40
El inquieto lector podría preguntarse si existen más “libros de colores”. Pues, bien, sí: al texto
“Smalltalk-80: Bits of History, Words of Advice”, de Glenn Krasner, editor, Addison-Wesley, 1983,
ISBN 0-201-11669-3, se le denomina “libro verde”. Nada malicioso, empero.
41
Mark Twain socarronamente afirmaba que “un clásico es un libro que todo el mundo elogia y
nadie lee”. Ya sabe, lector: sea original y lea.
42
VIMP (Ventanas, Iconos, Menú y Puntero) es la aventurada grafía castellana de WIMP.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 217
Smalltalk/V: Practice and Experience, Wilf LaLonde & John Pugh, 1994, Pren-
tice-Hall, 0-13-814039-1, con disquete.
LaLonde y Pugh son, entre otras cosas y desde hace tiempo, columnistas perma-
nentes de la sección Smalltalk del JOOP (Journal of Object-Oriented Program-
ming), de SIGS Publications. Fruto del ingente material publicado en sus colum-
nas es el presente texto, en el que se presentan convenientemente remozados
algunos casos prácticos de programación orientada-a-objetos en Smalltalk/V pa-
ra Windows. El libro está pensado, como los mismos artículos, para lectores con
conocimientos medios de Smalltalk, aunque como los casos son realmente inte-
resantes (conjuntos borrosos, intercambio dinámico de datos, combinación de
componentes modales y no-modales para construir un visor gráfico, etc.), la lectu-
ra de las 185 páginas del texto resultarán tan valiosas para los expertos como
para los recién llegados al lenguaje.

IBM Smalltalk: The Language, por David N. Smith, 1994, Addison-Wesley, 0-


8053-0908-X.
IBM ha entrado de forma sorprendentemente rápida en la arena Smalltalk y ha
cambiado ciertos criterios que se estimaban inamovibles. De cualquier manera la
tradición Smalltalk puede mucho, así que todo es, mayormente, como cualquier
Smalltalkista podría esperar. Este libro es bueno, con todo, en el sentido referen-
cial más práctico del lenguaje: nomenclatura, esquemas, modificaciones arquitec-
tónicas leves, muchas tablas gráficas, ejemplos, relaciones y código, suficiente
código (aunque de ese que se califica como toy-code). La inclusión de este libro
aquí se debe a que la integración de manual referencial y manual de usuario que
procura tiene una calidad superior a la media y, por tanto, ayudará de forma prác-
tica a los que necesitan inmersiones guiadas en el lenguaje. Debo decir que yo
mismo he utilizado este texto como soporte para grupos de programadores traba-
jando con herramientas sofisticadas en Smalltalk.

Smalltalk Best Practice Patterns - Volume 1: Coding, por Kent Beck.


Errr ... no es un error de tipografía. No he notado los detalles editoriales de este
libro porque yo lo he leído como borrador pre-publicación. Pero lo incluyo aquí
porque no quiero dejar de recomendar al lector con conocimientos de Smalltalk
un texto que reúne, en forma de patrones software de codificación, un conjunto de
técnicas bien asentadas utilizadas desde los albores de lenguajes por los mejo-
res programadores Smalltalk. Los patrones de este libro se segmentan en los
correspondientes de Comportamiento, Estado, Colecciones, Clases, Estilo de
Codificación y se aderezan por un pequeño ejemplo de conversión de monedas
que usa de muchos de tales patrones. Si alguien quiere saber por qué la codifi-
cación en Smalltalk no se semeja a ninguna otra y por qué genera tanta adicción,
lea, lea este libro. Kent Beck, que el lector reconocerá como creador de las fichas
CRC y activo Smalltalkista y Patronista, lo hace muy bien, de forma que el choque

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 218
inicial respecto de la rara nomenclatura de patrones utilizada se torna pronto en
un reconocimiento respetuoso a la experiencia de quien sabe de qué habla (y
sólo por esto el lector ya debería ir corriendo a adquirir este indispensable libro).

LIBROS SOBRE JAVA

¿Java? ¿Java? ¿Java? Pues sí, parece que la unión de palabras resonantes (In-
ternet, C++, Smalltalk, herencia, etc.) ha dado en generar un síndrome ansioso
respecto de este lenguaje. A buen seguro que cuando el lector revise esta sec-
ción habrán ya aparecido decenas de libros sobre Java, la mayoría de ellos cri-
minales o simplemente estúpidos. La cuestión es, en lo que a este texto respecta,
su comparación respecto de C++. El lector podrá encontrar en internet papeles,
índices, métodos, referencias e incluso manuales completos, libros en pre-edición
y comentarios diversos a los distintos aspectos del lenguaje/entorno Java. Lo que
ocurre es que la naturaleza volátil de internet sigue aconsejando fiar el conoci-
miento de la seguridad de las hojas impresas. He aquí algunas de ellas:

Hooked on Java: Creating Hot Web Sites with Java Applets, por Arthur van
Hoff, Sami Shaio y Orca Starbuck, 1995, Addison-Wesley, 0-201-48837-X, con
CD-ROM.
Los autores, miembros del equipo de desarrollo de Java en Sun Microsistems
Inc., exponen en este texto la euforia de un proyecto con una liviandad que causa
escalofríos. Mi pronta reacción a la primera lectura del libro fue la de decepción
clara: mucho web, muchos applets, mucha euforia, pero poco lenguaje y dema-
siadas decisiones de diseño tomadas muy a la ligera, como porque sí. Van Hoff
es el autor del compilador Java e impulsor de Hot Java, mientras que Shaio dise-
ñó e implementó el conjunto de herramientas de interfaz de usuario (a la vez que
el mecanismo de seguridad de los applets) y Starbuck básicamente se ocupó de
la documentación. El libro a menudo refleja la vivacidad de Duke, la mascota Ja-
va, y semeja una versión descafeinada de Ecce Homo: “¿Por qué soy tan inteli-
gente?”, “¿Por qué escribo tan buenos libros?”, “¿Por qué soy un destino?”43. Con
todo el texto se deja leer y querer, como un buen pasatiempo, pues dedica su
mayor parte a la descripción de applets y de páginas web. El CD-ROM que lo
acompaña incluye todos estos applets, código y páginas web de ejemplo y el
JDK para Windows 95/NT y Solaris 2.X. Así resulta que el libro es totalmente re-
comendable, pues frente a la tontería de los muchos artículos que inmisericordes
ya se han publicado, la ligereza exenta de errores y las opiniones de primera ma-
no finalmente resultan un buen plato. Si no le interesa Internet (o, en el mejor de
los casos, Intranet), lo mejor será, por otro lado, que olvide Java.

43
Recapacite el lector que Nietzsche admiró profundamente a Wagner antes de denostarlo por su
decadencia insoportable, así que la comparación musical con el C++ quizás no resulte desafor-
tunada.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 219
Mecklermedia’s Official Internet World 60 Minute Guide to Java, por Ed Tittel
y Mark Gaither, 1995, IDG Books, 1-56884-711-4.
Este texto es exactamente lo que anuncia: una guía que se pretende tan supues-
tamente fácil como el lenguaje que describe. La verdad es que el enfoque sintéti-
co favorece enormemente la comprensibilidad y el lector puede acceder a
información práctica librándose de bastante basura literaria. Así, en 253 páginas,
los autores describen las características básicas del entorno Java, del lenguaje en
sí y de los applets, para pasar a examinar con más detalle estos últimos en el con-
texto de internet/html, y después exponer varias técnicas de codificación de apli-
caciones Java (incluido el ejemplo de un manejador de protocolos) y hablar de su
futuro. La descripción de la gramática de Java y del API que conforman lenguaje,
bibliotecas y Hot Java terminan de redondear el texto. Naturalmente el libro no
enseña a programar, sino que más bien muestra ordenadamente lo que finalmen-
te hay (muchos adverbios). En fin, que lo más agradable del texto es su carencia
de pretensiones.

Java!, por Tim Ritchey, 1995, New Riders Publishing, 1-556205-533-X.


Se trata aquí de una descripción del lenguaje/entorno Java con un claro tono pe-
dagógico para programadores y administradores de sistemas. Así que tras la
típica introducción, se establecen procedimientos de instalación del software, de
variables de entorno, etc., para después pasar a una revisión de tipos, expresio-
nes, estructuras de control, clases, interfaces, paquetes, hilos, excepciones, bi-
bliotecas de clases, AWT, Internet (¿cómo no?), y dos interesantes secciones
sobre el interfaz con C y la máquina virtual Java. Sin duda el libro complementa
los dos anteriores.

The Java Handbook: The Authoritative Guide to the Java Revolution, por
Patrick Naughton, 1996, Osborne McGraw Hill, 0-07-882199-1.
Pese al petulante título, éste es quizá el más completo de los títulos aquí referidos.
Naughton cofundó FirstPerson Inc. y participó de forma clave en el proyecto
“Green” que dio lugar finalmente a Java, así que la información que aquí se da no
es producto de la publicidad del momento. En la primera parte se detalla el len-
guaje; en la segunda las clases del mismo y en la tercera el diseño detallado de
applets (quizá la literatura más precisa que hasta ahora haya leído sobre el tema).
El tono es abiertamente anti-C++, pero la gracia es que se pretende justificar ca-
da decisión de Java en razón de las supuestas insuficiencias de C++: así se dice
que C++ no es totalmente orientado-a-objetos por execrables razones de compa-
tibilidad y eficiencia (no como Java, claro), y sin embargo se da por sentado que
la decisión de Java de no considerar las clases como objetos en favor de la efi-
ciencia es absolutamente encomiable. El libro, con todo, está mucho menos infla-
do de aire que el de “Hooked on Java” y su lectura resulta divertida y estimulante,
sobre todo en los ejemplos de applets (donde se delata, de paso, la insuficiencia

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 220
clara de la actual versión de la AWT): el applet “Impresionista”, “DynaDraw” y
“Poesía en Imanes”. Si sólo ha de comprar un libro sobre Java, adquiera éste.

The Java Primer Plus: Supercharging Web Applications with the Java Pro-
gramming Language, por Paul M. Týma, Gabriel Torok y Troy Downing, 1996,
Wayte Group Press, 1-57169-062-X.
Con un título que podría venderse al peso y muy al estilo de la mayoría de los tex-
tos de el Grupo Wayte, esta obra intenta un acercamiento general al lenguaje y a
las bibliotecas de clases que comprende sin olvidar aspectos como la compara-
ción con C/C++, etc. El libro es muy completo y asequible, constituyendo una
suerte de manual de referencia elemental del lenguaje y su entorno, con muchos
dibujos bien cuidados y sin entrar en demasiadas profundidades. El CD que re-
gala incorpora la versión 1 del JDK, ya desfasada en favor del Java Workshop,
además del código fuente usado en el libro, applets incluidos. Nada nuevo, pero,
eso sí, explicado clarito y agradablemente. La parte III se titula “Uso de las capa-
cidades avanzadas de Java” y resulta la más interesante del libro, pues trata de
asuntos de red, hilos (threads), estructuras de datos, interfaz con C, gráficos y
sonidos.

LIBROS SOBRE SOFTWARE ORIENTADO-A-OBJETOS

El lector podría preguntarse aquí: ¿realmente necesito de textos sobre ideas ge-
nerales de la orientación-a-objetos? ¿Qué ayuda me pueden prestar tales ideas
en los procesos diarios de codificación en C++? Bueno, recordemos que C++ es
un lenguaje con facilidades para la Programación Orientada-a-Objetos, y que tal
es, en definitiva, la fase de implementación, tras las fases de diseño y análisis
orientados-a-objetos, de los objetos y sus relaciones modelados en base a los
conceptos de orientación-a-objetos a través de los que se matiza y visualiza nues-
tro problema en el mundo real. Las ideas generales de este nuevo paradigma nos
pueden ayudar, normalmente de forma inestimable, a encauzar nuestras codifica-
ciones hacia modelos conceptuales más adecuados a la nueva orientación, con-
siguiendo que nuestro software sea más robusto y fiable.

Object-Oriented Software Construction, por Bertrand Meyer, 1988, Prentice


Hall, 0-13-629031-0,534 pág.
El presente trabajo es, sin duda, la más rigurosa, acertada e inteligente exposi-
ción de los principios en los que se sustentan los sistemas orientados-a-objetos.
A la vez, y esto puede desconcertar al lector inadvertido, es una primera descrip-
ción del lenguaje de programación Eiffel, del que el Dr. Meyer es directo arquitec-
to y creador. Pero empecemos con método. La primera parte, "Ideas y Princi-
pios", es absolutamente impagable: lo que en ella se expone, substanciado en
cinco criterios y seis principios, es frecuentemente usado por mí mismo en buena

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 221
parte de los cursos de OOA & OOD que imparto. La exposición es sorprenden-
temente concisa, de forma que la revisión de conceptos supuestamente conoci-
dos por el lector se torna en extremo interesante. Tras este análisis general de los
sistemas orientados a objetos, el Dr. Meyer se cuestiona por un lenguaje con faci-
lidades para su implementación, y como quiera que, según sus propias palabras,
no encuentra ninguno, decide crear el suyo propio, sujeto con exactitud al para-
digma de objetos. Aparece así Eiffel, lenguaje orientado a objetos puro donde los
haya, de sintaxis tipo Pascal e interesantísimas características. Pero, ¿interesa
esto al programador de C++? Así lo creo. El Dr. Meyer no se limita a describir el
lenguaje, sino que, como artífice del mismo, explica las disyuntivas en las decisio-
nes de diseño y justifica las medidas adoptadas en cada caso, trayendo a cola-
ción interesantes problemas presentes en muchos de los diseños orienta-
dos-a-objetos. Como ejemplo sirva el tratamiento que en el lenguaje se dan a lo
que se denominan "precondiciones" y "postcondiciones": su claridad conceptual
ha redundado en que, en aras de la modularidad, limpieza y coherencia del códi-
go, tal enfoque haya sido posteriormente adoptado por distintas bibliotecas de
C++, como, verbigracia, "Tools.h++" de Rogue Wave. Una tercera parte del texto
se ocupa de revisiones genéricas de lenguajes clásicos de programación, así
como de sus extensiones a objetos, y aun de C++, Smalltalk, Ada, etc. Se revisan
también cuestiones de herencia y, de forma leve, cuestiones como la persistencia
de objetos, que en el momento de publicación de esta edición no estaban todavía
en el ojo del huracán, como ocurre ahora. En los apéndices se retoma, por fin, el
lenguaje Eiffel a modo de fragmentos referenciales. Se trata, en resumen, de un
libro indispensable para cualquier con pretensiones mínimamente serias en el
ámbito de la OOP: reserven, pues, un hueco en su estante para él.

A Book of Object-Oriented Knowledge, por Brian Henderson-Sellers, 1991,


Prentice Hall, 0-13-059445-8, 297 pág.
El presente libro podría ser considerado como una eficaz introducción al panora-
ma general de orientación-a-objetos y, aunque, según informa el propio autor, el
texto pretende ser una guía en el proceso de formación y aclimatación de la men-
talidad de los lectores al paradigma de objetos (para lo que incluye modelos de
transparencias "ad hoc" que pueden ser copiadas y utilizadas como ilustración de
cursos sobre la materia), la obra se constituye, en el fondo, en una esclarecedora
revisión, desde una adecuada distancia (y recuérdese aquí la frase de Ortega
sobre Cleopatra), de los distintos criterios que pueblan, a veces en descorazona-
dor desorden, el universo de los objetos. Se revisan, así, metodología de análisis,
diseño e implementación, intentando procurar al lector una suerte de plataforma
conceptual básica desde la que pueda acceder con mayor comodidad a técnicas
concretas. El tiempo, empero, no pasa en balde: actualmente yo uso las transpa-
rencias de este texto como soporte en el que basar una crítica de los enfoques
más extendidos de acercamiento a la Orientación-a-Objetos. O sea, que la histo-
ria, como siempre, nos sirve para aprender de ciertos errores justo antes de vol-
ver a repetirlos.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 222
Object-Oriented Methods, 2 nd Edition, por Ian M. Graham, 1994, Addison-Wes-
ley, 0-201-59371-8, 473 pág.
Es éste un libro caracterizado por la perspectiva globalizadora bajo la que se con-
templan las ideas y conceptos de orientación-a-objetos. La impronta pragmática
británica se deja notar, y su lectura es realmente amena. El texto comienza con
una bien entramada introducción crono-sectorial a los tópicos de la OOT: concep-
tos básicos, lenguajes de programación orientados-a-objetos, WIMPs, bases de
datos relacionales, bases de datos orientadas-a-objetos, etc.; hasta llegar a la
parte más significativa: análisis y diseño orientados-a-objetos. Aquí Graham, tras
una revisión crítica de bastantes métodos (Coad/Yourdon, Desfray, OOSE, OMT,
OOSA, etc.), expone su “metodología” (las comillas son mías) SOMA 44, una varia-
ción de Coad/Yourdon a la que se han añadido, simplificando, "triggers". La
orientación-a-objetos es filtrada a través de la experiencia del autor en los cam-
pos de inteligencia artificial e ingeniería del conocimiento y, así, resulta curiosa y
enormemente instructiva (a la par que descorazonadora y un tanto pretenciosa),
por ejemplo, la facilidad con que Graham aborda la fase de identificación de obje-
tos y de sus relaciones. La prototipación aparece descrita, seguidamente, de una
forma esclarecedora, para terminar con un vistazo al futuro posible y un muy inte-
resante apéndice sobre "objetos borrosos". En fin, se trata de un texto muy acon-
sejable para aquéllos que busquen una visión integradora de las nuevas técnicas
en el continuum de la evolución informática. Servirá, también, para aquellos que
deseen contemplar un panorama global de métodos, herramientas CASE, ten-
dencias y perspectivas en el área de la OT. No está de más, al fin, probar un poco
de solidez europea frente a las montañas (magníficas, por otro lado) norteameri-
canas.

Object-Oriented Programming, por Peter Coad y Jill Nicola, 1993, Prentice


Hall-Yourdon Press, 0-13-032616-X, 582 págs. y disquete incluido.
Bueno, en la misma tónica que otros libros firmados en colaboración por Peter
Coad, es éste un texto divertido, ameno y claramente pedagógico. Tras el índice,
de increíble longitud, el lector se encuentra con una obra estructurada en cuatro
partes, substanciadas cada una de ellas en un ejemplo completo y en orden cre-
ciente de dificultad: "Contador", "Una maquina expendedora", "ventas, ventas,
ventas" y "Sigue el flujo", seguido por apéndices en que se detallan las caracte-
rísticas esenciales de los lenguajes en que se desarrollan tales ejemplos: C++ y
Smalltalk. Para el análisis y diseño de éstos se sigue, cómo no, el método y la
notación de Coad/Yourdon, y cada paso es suficientemente explicado. La compa-
ración de los ejemplos en C++ y Smalltalk sirve, de paso, como incruenta intro-
ducción a ambos lenguajes. Lo cierto es que el libro imbuye fácilmente al lector en
el object-thinking, acostumbrándolo a pensar "en objetos", y sólo por esto valdría

44
Graham expone SOMA detalladamente en su siguiente texto “Migrating to Object Technology”.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 223
la pena leerlo. Aparecen, además, situaciones y decisiones de gran valor peda-
gógico. La campaña de comercialización del libro incluye "El Juego de los Obje-
tos", con pelotita, silbatos y fichas de cartón, junto con un vídeo en el que se pue-
de apreciar la vitalidad yanqui de Peter Coad (acompañado por señoritas ligeri-
tas de ropa que sostienen cartones con clases y otras barbaridades).

LIBROS DE ANÁLISIS Y DISEÑO ORIENTADO-A-OBJETOS (OOA & OOD)

A pesar de la apariencia de complejidad que normalmente se suele aplicar a es-


tas fases del proceso de desarrollo software, lo cierto es que, como resulta de mi
propia experiencia impartiendo cursos diferenciados de OOP, OOA y OOD, aun-
que normalmente se empieza por el lenguaje, cuando se revisan los métodos de
análisis y diseño Orientados-a-Objetos, se hace sentir entre los alumnos una fuer-
te sensación de que, comprendidas las bases de estas disciplinas, se pueden
aprovechar mejor los recursos de C++. No estoy propugnando que el lector esco-
ja necesariamente una de las metodologías presentes en el mercado, sino que
examine el sustrato en el que se apoyan y se apropie de alguna de las técnicas
que en ellas aparecen.

Designing Object-Oriented Software, por Rebecca Wirfs-Brock, Brian


Wilkerson & Lauren Wiener, 1990, Addison-Wesley, 0-13-629825-7, 368 pág.
Este es un texto absolutamente recomendable, sin contrapartida alguna, a aqué-
llos que buscan iniciarse en las procelosas aguas del Diseño Orientado-a-
Objetos. Esto no significa, empero, que se trate de un libro elemental: de hecho,
la solidez conceptual en la que se apoya convierte al texto en una muy fructífera
fuente y base referencial para profesionales y equipos con experiencia en Tecno-
logía de Objetos. Esta obra intenta, en síntesis, proporcionar un método de OOD
independiente de lenguajes y aun de notaciones especiales. Dado que el autor
del presente libro es especialmente sensible a lo que se conoce como diagrama-
nía (esa costosa, compleja y contraproducente acumulación de dibujos y conexio-
nes, escasamente diferenciados entre sí, y que suelen conducir a analista, dise-
ñador, cliente y, en general, a cualquiera que inocentemente los examine, a un
estado de perplejidad cercano al catatónico), la exposición que aquí se realiza,
capitaneada por Wirfs-Brock, es como una burbuja de oxígeno en la luna. El grue-
so del libro trata de lo que otros, en esta misma materia, dan mayormente por
supuesto o examinan muy brevemente: la identificación y asignación de objetos y
de sus interrelaciones. Para el tratamiento de la información relacionada con el
proceso de OOD se usan las denominadas fichas CRC (Clase-Responsabilidad-
Colaboración): en cada una de ellas se significa el nombre de la clase, si es abs-
tracta o no, las superclases y las subclases de la misma, una sucinta descripción
de su cometido, las responsabilidades que asume (esa suma de un cierto tipo de
conocimiento que la clase posee y las acciones que puede efectuar), y las cola-
boraciones necesarias para cumplimentar cada una de sus responsabilidades

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 224
(esto es, la relación de clases necesarias para llevar a cabo las tareas o respon-
sabilidades asignadas y para las que la clase no es autosuficiente). Se trata en
síntesis del siguiente proceso: en una primera etapa exploratoria se produce la
identificación de clases, identificación de clases abstractas, identificación y asig-
nación de responsabilidades e identificación de colaboraciones; en la siguiente
etapa, denominada de análisis, se modela la construcción de jerarquías (clases
en derivación), se identifican los contratos (una serie cohesiva de responsabilida-
des normalmente requeridas por otras clases) y se asignan a las colaboraciones,
se identifican los subsistemas (abstracciones que permiten un manejo más ade-
cuado del sistema global a modelar), se refinan las colaboraciones y se protocoli-
zan las responsabilidades (esto es, cada una de las responsabilidades se trans-
forma en el prototipo de una función). Se obtiene, al final, algo así como una es-
tructura de clases y sus relaciones vacía de implementación. Pero es que la im-
plementación no es lo importante: la identificación de las responsabilidades (por
servicios) públicos de las clases permitirá fragmentar la implementación de cla-
ses de una manera adecuada para el trabajo en equipo (una clase o serie de cla-
ses pueden serle asignadas a una persona, por ejemplo, y ésta únicamente sabrá
del resto de las clases que pueden ser accedidas a través de un protocolo públi-
co ya bien definido, independientemente de su implementación concreta). El se-
guimiento del clásico ejemplo del cajero automático es particularmente revelador
sobre el proceso de diseño expuesto, con abundantes comentarios y la explica-
ción detallada de las decisiones tomadas. Este ejemplo, y otros, están expuestos
en su totalidad en los apéndices del libro. Expuesto lo anterior, reitero mi reco-
mendación de uso de este libro quizás como el primer libro de OOD que los prin-
cipiantes debieran estudiar.

Using CRC Cards: An Informal Approach to Object-Oriented Development,


por Nancy M. Wilkinson, 1995, SIGS Books, 1-884842-07-0 (Prentice-Hall 0-13-
374679-8), 226 págs.
Frecuentemente he oído que el anterior libro está muy bien, pero que sólo propor-
ciona vagas ideas para emprender algo parecido a una transición mental al “uni-
verso de los objetos”. Naturalmente esa es la idea, pero no hay que olvidar el mé-
todo que la respalda: las fichas CRC son enormemente útiles para afrontar un
desarrollo orientado-a-objetos. “Sí, sí, pero ¿cuáles son los pasos a seguir en un
equipo real trabajando en un proyecto real?” Bueno, este texto aporta varias bue-
nas ideas hilvanadas por una narración bien asida a la tierra. El ejemplo de ges-
tión bibliotecaria, que discurre por toda la obra, resulta adecuado, en tanto que
permite otear distintos escenarios y problemas, de forma que los diálogos de los
componentes del equipo de desarrollo (pues el libro simula una especie de cua-
derno de bitacora dialogado) devienen reconocibles y clarificadores. Las fichas
CRC se muestran, pues, además de como excelentes herramientas exploratorias,
en calidad de paso previo a métodos formales como Booch, Shlaer-Mellor, etc.
Se muestran algunas herramientas de gestión de fichas CRC (como ObjectSys-
tem/CRC de Rational), y un capítulo entero se dedica a lo que muchos lectores

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 225
declararán inestimable: el mapeo de las fichas a construcciones C++. Oh, el libro
no expone ninguna idea de pasmo, pero la descripción del conjunto de prácticas
de uso de fichas CRC constituye un manual perfecto para un primer proyecto de
migración a la Tecnología de Objetos, así como el texto es excelente para un cur-
so complementario del método de Wirfs-Brock, o aún para la enseñanza primera
de la Tecnología.

Object-Oriented Modeling and Design, por James Rumbaugh, Michael Blaha,


William Premerlani, Frederick Eddy & William Lorensen, 1991, Prentice Hall, 0-
13-629841-9, 528 pág.
En este libro se expone una metodología de análisis y diseño orientados a obje-
tos, que abarca el ciclo completo de desarrollo de software, creada por el equipo
de investigación de General Electric liderado por James Rumbaugh. De todas las
metodologías propietarias examinadas aquí, ésta es una de las más completas y,
a la vez, la que menos reniega de las bien conocidas metodologías de análisis y
diseño estructurados en las que abiertamente se basa. Los autores plantean el
traslado del dominio del problema en el mundo real al campo software a través
del modelado de los objetos que aparecen en aquél, junto con sus relaciones. Y
aunque esto mismo podría decirse de cualquier otro técnica de OOA&OOD, el
método aquí expuesto, denominado OMT (Object Modeling Technique: Técnica
de Modelado de Objetos), se descompone en tres submétodos: el modelado de
objetos, el modelado dinámico y el modelado funcional. Examinemos, por su inte-
rés, aun de forma sucinta, estas tres subtécnicas: en el modelo de objetos se
describen, con una notación clara y precisa, los atributos y las relaciones estáti-
cas entre los clases a que pertenecen los objetos del problema y sus relaciones
ya modeladas; en el modelo dinámico se exponen, mediante diagramas de tran-
sición de estados, los aspectos del sistema relacionados con el control del es-
quema secuencial y temporal de las operaciones que afectan a los objetos mode-
lados, usando, para mayor facilidad en la comprensión del diagrama, la abstrac-
ción de Harel que permite el anidamiento de subsistemas de estados; en el mo-
delo funcional se exponen, por último, los aspectos del sistema relacionados con
las transformaciones de la representación interna de los objetos modelados, ya
expresadas estáticamente como operaciones en el modelo de objetos, a través
de los muy conocidos diagramas de flujo de datos. Quizá el modelado funcional
resulte el de peor acoplamiento en las bases conceptuales de un esquema de
objetos, pero, aun así, su interrelación con las demás técnicas es modélica. Estos
tres modelos se engarzan a través de lo que se pretende -o mejor, adivina- sea un
esquema no secuencial de fases: análisis, diseño del sistema y diseño de obje-
tos. La exposición final de ejemplos (un compilador de diagramas de objetos,
animación computerizada y un sistema de diseño de distribución eléctrica) es un
excelente complemento de esta técnica. Estamos, en definitiva, ante un excelente
texto, aderezado con comentarios más o menos acertados sobre bases de datos
relacionales y con extensiones a objetos, lenguajes no orientados-a-objetos, etc.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 226
Se aprecia, por último, una importante característica de cohesividad a lo largo de
la exposición de la metodología que le proporciona una solidez conceptual a que
otras son ajenas. La OMT ha ido ganando, desde su publicación, adeptos entre
los usuarios de OOA&OOD, habiéndose posicionado, en estas fechas, como una
de los métodos de uso más extendido. Se utilice o no esta técnica, lo cierto es
que el libro es, en todo caso, una valiosísima contribución a cualquier biblioteca
de OT. General Electric ha desarrollado una herramienta denominada OMTool
para distintas plataformas que computeriza estas técnicas, pero lo cierto es que
prácticamente cualquier herramienta actual multi-método soporta OMT. Finalmen-
te el método se ha asociado de forma indisoluble con James Rumbaugh, que jun-
to con Grady Booch e Ivar Jacobson en Rational están pariendo un entente unifi-
cado que ya ha cambiado de nombre más de una vez. ¡Oh, divina eclecsis!

Object-Oriented Analysis and Design with Applications, 2nd Edition, por


Grady Booch, 1994, Benjamin/Cummings, 0-8053-5340-2, 589 pág.
Este libro, muy en la línea de extensa difusión del anterior, es uno de los más con-
siderados por la comunidad de OT. Constituye la perfecta continuación (a pesar
de su independencia y aun a pesar del sr. Booch), con un carácter marcadamente
propio, del texto de Wirfs-Brock. Como la mayoría de los textos en OOA y OOD,
su primera sección (Conceptos) empieza por una revisión de los conceptos bási-
cos de la orientación a objetos, tales como clases, objetos, etc., exponiendo dis-
tintos mecanismos para la identificación de estos dentro del dominio de un pro-
blema: esta parte es grandemente reflexiva y procura una bien fundada iniciación
a la Tecnología de Objetos. La segunda sección (El Método) expone el método
de OOD de Booch, junto, cómo no, a su correspondiente notación (donde apare-
cen las famosas nubes), a más de convenientes consideraciones prácticas. La
última sección (Aplicaciones) detalla lo que Booch denomina “ejemplos no-
triviales” en que se aplica lo hasta entonces visto. Lo curioso es que, tras una pri-
mera y exitosa edición en la que Booch ejemplificaba su método mediante código
en CLOS, C++, Smalltalk, Ada y Object Pascal, esta segunda edición dedique
sus ejemplos con exclusividad al C++ (a la vez que ha añadido el vocablo “Analy-
sis” a su título, aunque sin demasiada representatividad real). Un apéndice sobre
lenguajes de programación orientados y basados en objetos, además de 58 pá-
ginas repletas de bibliografía completan el volumen. Nos encontramos, pues, ante
uno de esos raros textos densos en contenido y, a la vez, de brillante practicidad y
fuertes capacidades referenciales. Piense el lector que uno de los "reproches"
que se le imputan a Booch es la "excesiva riqueza" de su notación. ¿Mi consejo?
Este debe ser el segundo o, a lo sumo, tercer libro de diseño que adquieran. Y en
cuanto a herramientas destaca la bien conocida Rational Rose, pero lo cierto es
que este método lo soportan casi todas las herramientas actuales.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 227
Migrating to Object Technology, por Ian M. Graham, 1994, Addison-Wesley, 0-
201-59389-0, 552 pág.
El autor extiende su anterior libro “Object Oriented Methods” con esta obra, dividi-
da en dos claras partes: en la primera se examinan distintas cuestiones relacio-
nadas con el proceso de migración hacia la Tecnología de Objetos (necesidad,
oportunidad, beneficios, problemas, interoperación, reúso, interfaces gráficos,
almacenamiento de objetos, sistemas distribuidos y sistemas expertos), y en to-
das ellas se analizan dificultades, beneficios, direcciones, oportunidades y reali-
dades comerciales; en la segunda parte se detalla como abordar este proceso
de migración utilizando SOMA, con un muy interesante capítulo dedicado al análi-
sis y captura de requerimientos y una siempre bienvenida exposición dedicada a
métricas orientadas-a-objetos (Graham, por parte de la Corporación Suiza de
Banca, es uno de los promotores del Club de Métricas Orientadas-a-Objetos). No
faltan las referencias extensas a la re-ingeniería de procesos de negocio y los
aspectos de diseño físico e implementación para completar un libro redondo que,
no obstante, se complementa perfectamente con aquél primero del autor. El libro
esconde, finalmente, un disquete con una versión truncada (sin capacidades de
impresión) de una herramienta no-CASE denominada SOMATiK, que intenta au-
tomatizar el uso de SOMA.

Object-Oriented Analysis, 2nd Edition, por Peter Coad & Edward Yourdon,
1991, Yourdon Press/Prentice Hall, 233 pág.
Nos encontramos ante la segunda edición de uno de los primeros textos apareci-
dos en el mercado sobre OOA. Esta premura editorial consiguió que las ideas
propuestas por Coad y Yourdon se extendieran con gran rapidez, siendo así que
este método ha permanecido durante mucho tiempo como uno de los más am-
pliamente difundidos. El libro es uno de los más breves de los comentados en
este anexo y está escrito en un lenguaje coloquial de muy fácil lectura. Los con-
ceptos básicos del paradigma de objetos se exponen con la ayuda de definicio-
nes de diccionarios y enciclopedias. La graficación, una de las primeras y, por
tanto, de las más rudimentarias en este campo, básicamente expone el resultado
gráfico de la herramienta comercializada por los autores (OOATool). Las seccio-
nes de introducción y comparación de distintas metodologías de análisis anterio-
res al OOA (Yourdon-de Marco, Jackson, etc.) son agradablemente sucintas y
claras. Lo único que se puede reprochar es la falta de un formalismo metodológi-
co práctico que permita al lector hacer uso de lo aprendido. De hecho el lector se
queda al final del texto con una cierta sensación de borrachera de objetos, gene-
rada en buena medida por el énfasis y la excitación con que los mismos autores
tratan a la OT, pero sin direccionamiento práctico claro en el que sostener sus
primeros pasos en este campo. El libro es, pues, perfectamente aconsejable co-
mo texto introductorio a OOA.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 228
Object-Oriented Design, por Peter Coad & Edward Yourdon, 1991, Prentice
Hall, 0-13-630070-7.
Estamos ante una clara continuación del anterior libro de los autores sobre OOA,
que usa de una extensión apropiada a OOD de la herramienta de análisis OOA-
Tool de los autores. Tras una leve introducción (pues el texto, como el anterior, es
singularmente corto), se repasa la metodología de Análisis orientado-a-objetos
de Peter Coad: un modelo multicapa (sujeto, clase-objeto, structura, atributos y
servicios) y multicomponente (dominio del problema, interacción humana, gestión
de tareas y gestión de datos), con una notación específica de aplicación. Los au-
tores intentan, con cierto éxito, integrar de forma incruenta las técnicas de OOD
con las del proceso de OOA, para pasar después a exposiciones sobre sectores
de parcial interés, como el de las herramientas CASE o los distintos lenguajes de
programación. El texto, con las mismas salvedades de la obra anterior, es total-
mente recomendable como introducción no reglada al campo del OOD.

Object-Oriented Analysis: Modeling the World in Data, por Sally Shlaer &
Stephen J. Mellor, 1988, Yourdon Press/Prentice Hall, 144 pág.
Tenemos aquí a uno de los pocos textos serios focalizados en el área de análisis
orientado-a-objetos. Con un corto número de páginas, la obra comienza con una
simpática introducción a los típicos conceptos básicos, pasando a poco a clasifi-
car los objetos en: tangibles, roles, interacciones, incidentes y especificaciones
(he de reconocer que esta fragmentación conceptual yo siempre la he asumido
como de identificación de clases). Seguidamente expone un modelo de control
textual de especificaciones de clases como soporte de la técnica desarrollada
por los autores y denominada modelado de información. No es éste un libro que
proporcione un bagaje semejante al de Wirfs-Brock, ni la técnica en él descrita es
particularmente fácil de aplicar, pero, con todo, el texto ofrece detalles muy intere-
santes para el estudioso, así como ejemplos altamente intuitivos y de ilustracio-
nes autoexplicativas.

Object Lifecycles: Modeling the World in States, por Sally Shlaer & Stephen
J. Mellor, 1991, Prentice Hall, 0-13-629940-7.
Esta obra estudia, tras un breve repaso comprehensivo de la anterior obra de los
autores, el comportamiento dinámico de los sistemas de objetos. Éstos se asimi-
lan a máquinas de estados (así como las clases a modelos de estados), de tal
forma que el ciclo de vida de un objeto puede modelarse como un conjunto de
estados, de eventos, de reglas de transición y de acciones. Seguidamente se
muestra el desarrollo de las relaciones entre objetos afectadas por el tiempo, pa-
ra pasar después a la exposición de los métodos de modelado de secuenciacio-
nes de eventos y terminar con una extensión de los diagramas de flujos de datos
denominada ADFD y referida a los datos asociados a acciones de los objetos.
Se exponen diversas posibles aplicaciones de la técnica de la obra y se muestran

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 229
algunas líneas de migración a esta metodología desde el enfoque estructurado.
En definitiva nos encontramos ante el perfecto compañero de la anterior obra.

Object-Oriented Systems Analysis: A Model-Driven Approach, por David W.


Embley, Barry D. Kurtz & Scott N. Woodfield, 1992, Prentice Hall, 0-13-629973-
3, 302 pág.
He aquí, a mi entender, una de las aportaciones más significativas realizadas en
los últimos tiempos al área del análisis orientado-a-objetos. Los autores proveen,
a más de una definición formal de su método de análisis (OSA: Object-Oriented
Systems Analysis) basado en lo que llaman ORM (Object-Relationship-Model), de
la que carece el resto, una nueva y rica notación que se aplica con exactitud al
nuevo enfoque de aproximación al OOA: relaciones, estados y modelos de inter-
acción de objetos. Si bien los modelos de relación y de estados son fácilmente
asimilables (salvadas ciertas distancias) desde otras metodologías (diagramas
entidad-relación, etc.), el modelo de interacción reemplaza al clásico diagrama de
flujo de datos. El tratamiento dado, de cualquier forma, a las relaciones es particu-
larmente rico y revelador: éstas aparecen ya como objetos reales, y la notación
cualificadora de los conjuntos de relaciones resulta increíblemente clara, en per-
fecto desarrollo de aproximaciones más tradicionales como la de Rumbaugh
(OMT). La obra contiene una gran cantidad de ejemplos y cuestiones (existe un
libro adicional con la respuesta a los ejercicios propuestos) sencillamente perfec-
tos. El capítulo introductorio es, por otro lado, suficientemente explicativo, y el
ejemplo del trayecto en la ciudad es realmente bueno. Bien: su biblioteca queda-
ría incompleta sin esta obra. Así de simple.

Object-Oriented Software Engineering: A Use Case Driven Approach, por


Ivar Jacobson, Magnus Christerson, Patrik Jonsson y Gunnar Övergaard, 1992,
Addison-Wesley, 0-201-54435-0, 524 págs.
Pocos libros han influido tanto, tan rápido y tan extensamente en la comunidad de
objetos como el presente, de forma que los Casos de Uso que en él se exponen
(entre otras ideas) fueron inmediatamente adoptados, en mayor o menor medida,
por la práctica totalidad de los otros métodos. El texto entero supone una aproxi-
mación fundada a lo que debiera ser la ingeniería de software orientada-a-objetos
y su legibilidad es francamente excelente, de forma que maliciosamente se acon-
seja su lectura a gestores y responsables de proyectos. El texto empieza -¿cómo
no?- con una parte introductoria (más práctica, a mi gusto, que la de Booch), para
en la segunda parte abordar una pila de conceptos esenciales (arquitectura, aná-
lisis, componentes, validación, etc.) y terminar con una tercera parte dedicada a la
gestión práctica de proyectos, con dos capítulos dedicados a sendos ejemplos,
una cierta guía mínima de gestión de proyectos y un repaso de algunos otros mé-
todos de OOA/OOD. El libro es claro y preciso, pues se apoya en los muchos
años de experiencia de su autor principal, pero hay que advertir (y en el texto se

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 230
nota sin tapujos) que no es más que un breviario del método de Jacobson (Objec-
tory), de forma que algunos de mis clientes no han comprendido el alcance del
mismo hasta que han pagado la licencia del método entero.

Object Oriented Program Design with Examples in C++, por Mark Mullin,
1990, Addison-Wesley, 0-201-51722-1, 303 pág.
Este texto ha llegado a ser muy popular entre un cierto sector de la comunidad
C++, principalmente entre los desarrolladores provenientes de C y de esquemas
de diseño estructurado, debido quizás a la inmediata practicidad de los concep-
tos que, sin esquematización rigurosa, se van proponiendo: es como si se dise-
ñara en C++. El autor asume un ejemplo (la creación de una base de datos corpo-
rativa para Bancroft Trading Company) y en sucesivos capítulos va refinando su
diseño. La mayor ventaja para algunos de este libro (su proximidad a la vida real)
es, sin embargo, también su principal defecto, pues aparte del diseño de bases
de datos la importancia habría de traspasarse a la identificación de clases y rela-
ciones, algo que en el texto se relega a un segundo plano. Planteada esta obser-
vación, por lo demás el libro es levemente recomendable como primer estadio de
paso en la codificación C++ basada en la conceptualización de objetos, sin de-
masiadas pretensiones adicionales. Yo diría que sin ninguna pretensión adicional.

A Complete Object-Oriented Design Example, por Joseph E. Richardson,


Ronald C. Schultz & Edward V. Berard, 1992, Berard Software Engineering Inc,
1-881974-01-4, 350 pág.
Se suele echar de menos, al introducirse en el terreno del OOD, la disponibilidad
de un ejemplo completo, en todas su fases, que rellene esas carencias, obviadas
por una cuestión esencial de espacio en otros textos, y que colocan al novicio en
disyuntivas normalmente difíciles de superar. El presente texto intenta cubrir tan
urgente necesidad. El lector fácilmente podrá apreciar que en el libro se explici-
tan, con toda clase de detalles, las fases consideradas más farragosas y de tra-
bajo más tedioso, normalmente substanciadas en listas selectivas: listas y más
listas. Se usan, después, diagramas de transición de estados, redes de Petri y
diagramas de Booch para resolver el problema de diseño de una utilidad de con-
cordancias, típica de un procesador de textos. Finalmente se detalla la implemen-
tación completa de la solución encontrada, tanto en Smalltalk como en C++. En el
apéndice se detallan distintas especificaciones para terminar con una exposición
parcial de las diapositivas empleadas por la ya desaparecida firma de ingeniería
de software, editora del libro, en sus cursos y trainings de OOD. ¿El resultado?
Una aproximación pedagógica al mundo real con un ejemplo no del todo de ju-
guete.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 231
Working With Objects: The OOram Software Engineering Method, por Tryg-
ve Reenskaug con Per Wold y Odd Arild Lehne, 1996, Manning Publications, 1-
884777-10-4 (Prentice Hall: 0-13-452930-8)
Atención, lector: a mi entender este es uno de los textos más importantes publi-
cados en los últimos años sobre construcción de sistemas software, de forma que
arriesgarse a ignorarlo es comprometerse con la oscuridad. Sepa el lector, de
cualquier forma, que no soy fanático: sólo vehemente. Esto es, en el texto se ex-
plicitan muchas ideas que a mí me parecen no sólo naturales y adecuadas, sino
también inteligentes, efectivas y, finalmente, humanas. Es curioso que sean los
trabajos europeos, y sobre todo los del norte, los que enfaticen la primordial im-
portancia del factor humano en la construcción de sistemas software. El libro
contiene, además, un importante componente pedagógico, pues evidencia los
importantes logros conseguidos mediante el uso prudente de Smalltalk y de la
más general Tecnología de Objetos en una empresa (Taskon) a lo largo de más
de 25 años. El autor principal, Trygve Reenskaug, es, además de un reputado
experto en el campo de la Orientación-a-Objetos, el creador del concepto
Modelo-Vista-Controlador (MVC) que seguro todos conocen. El libro destila
inteligencia y sentada experiencia en todas sus páginas, a la vez que pone de
manifiesto un principio que no por evidente se aplica en otros métodos: las
mismas técnicas empleadas en el análisis y diseño de software debieran servir
para el análisis y diseño de organizaciones humanas. En fin, casi me aventuro a
proclamar: si sólo han de comprar un libro sobre objetos, adquieran éste.

LIBROS SOBRE PATRONES DE DISEÑO

Hay unos cuantos textos relacionados con los patrones de diseño software, buena
parte de ellos de Alexander, naturalmente. Para evitar el pataleo de los lectores,
reseñaré brevemente las obras de arquitectura. He aquí mi selección:

Notes on the Synthesis of Design, de Christopher Alexander, 1964, Harvard


University Press.
Este es el texto basado en la tesis doctoral de Alexander, y base, a la vez, del
estudio de Alexander sobre el sistema BART, en el que el autor establece por vez
primera que la funcionalidad del sistema no depende tan sólo de un conjunto de
requerimientos. Y si esta idea, sabiéndola no originaria de la escritura automáti-
ca, no despierta la curiosidad del lector, nada podrá hacerlo.

A Pattern Language: Towns/Building/Construction, de Christopher Alexan-


der, Sara Ishikawa, Murray Silverstein, Max Jacobson, Ingrid Fiksdahl-King y
Shlomo Angel, 1977, Oxford University Press, 0-19-501919-9.
253 patrones, con el formato específico propuesto por Alexander y repartidos en

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 232
1.171 páginas (muchas pero pequeñas y con fotos e ilustraciones, no tema el lec-
tor), se dan cita en este texto (ya difícil de conseguir), en el que además se pro-
pugna una integración del mejor-vivir con el medio físico circundante: gente-gente-
patrones-gente. Cuando se habla del “libro de Alexander” (CA patterns book) o
del “libro AIS” (las iniciales de los primeros autores) se refieren a esta obra. Para
que el lector se forme una adecuada idea del tono del libro, sirvan estos ejemplos
descafeinados y salvajemente simplificados: ¿Cómo se sabe que un parque está
adecuadamente insertado en el terreno urbano? !Cuando los mendigos duermen
en él! ¿Dónde deben construirse las paradas de autobuses: en zonas tranquilas o
en zonas de bullicio vital urbanístico? Pues en ... humm, dejaré que el lector lea el
libro. Realmente estas ideas tienen impacto en los arquitectos noveles (como las
ideas de Le Corbusier sobre las medidas humanas de las farolas, etc.), para bien
o para mal.

The Oregon Experiment, de Christopher Alexander, 1978, Oxford University


Press.
Aquí se explicitan los planteamientos participativos (usuario-
constructor/diseñador) puestos en práctica en el plan maestro de Berkeley de
1970. El resultado, fiel a la trayectoria práctica alexanderiana, no fue el esperado.

The Timeless Way of Building, de Christopher Alexander, 1979, Oxford Uni-


versity Press.
Junto con el de “A Pattern Language” ésta es una de las obras más difundidas de
Alexander. Si a estas alturas ya soportamos bien el estilo pseudo-filosófico-
religioso del autor, el texto merece la lectura, pues en él se establecen las bases
de una “arquitectura post-industrial creada por la gente”.

The Production of Houses, de Christopher Alexander, 1985, Oxford University


Press.
Se relata aquí la desafortunada historia del fallido proyecto de construcción de
una comunidad en Mexicali. Este texto es especialmente interesante por cuanto
en él Alexander reflexiona sobre los problemas y carencias que llevaron al fracaso
del proyecto. Algunas reflexiones resultan emocionalmente ingenuas (sobre todo
las relacionadas con la escasa comprensión del gobierno mexicano), pero de
ellas se desprende un tono de advertencia al que los nuevos apóstoles de los
DPs no debieran resultar ajenos.

Advanced C++ Programming Styles and Idioms, por James O. Coplien, 1992,
Addison-Wesley, 0-201-54855-0.
Desde su publicación he venido recomendando efusiva e insistentemente este
inteligente libro a todo aquél que de verdad quiera conocer el lenguaje C++ y aun

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 233
usarlo. Resulta claro ahora, como el mismo autor expresamente reconoce, que los
idiomas aquí descritos son en realidad patrones, presentados empero sin formato
definido. El libro es, pues, doblemente (mi comentario ya se dió en la sección de
C++ Avanzado) imprescindible.

Design Patterns: Elements of Reusable Object-Oriented Software, de Erich


Gamma, Richard Helm, Ralph Johnson y John Vlissides, 1995, Addison-
Wesley, 0-201-63361-2.
Este texto se conoce como “GOF book” o libro del GOF (Gang-of-Four: Clan-de-
los-Cuatro, en evidente referencia a los autores del texto), y es sencillamente in-
dispensable para cualquiera interesado en los patrones de diseño. Tras una ade-
cuada introducción conceptual se introduce al lector en un caso práctico (un editor
WYSIWYG) y ante los sorprendidos ojos de éste desfilan patrones donde no pa-
recía haberlos. Seguidamente se detallan distintos patrones agrupados en tres
subcatálogos: de creación, estructurales y de comportamiento, con ejemplos en
C++ y Smalltalk. Por su solidez este libro puede hacer cambiar de parecer a los
que pudieran pensar que es demasiado pronto para redactar un catálogo de pa-
trones. Puedo contarles que, habiendo impartido varias conferencias sobre pa-
trones, los asistentes que finalmente leyeron el libro llegaron a confesarme que no
habían valorado bien la practicidad de las ideas que en él se exponen hasta que
lo vieron y leyeron con sorpresa y satisfacción. En fin, imprescindible: tanto para
los programadores de C++ como ... para cualquiera.

Design Patterns for Object-Oriented Software Development, de Wolfgang


Pree, 1995, Addison Wesley.
Tras un breve examen de la historia, estado y clasificación de los patrones (patro-
nes orientados a objetos, patrones de codificación, recetarios de marcos, contra-
tos formales y catálogos de patrones de diseño), Pree nos adentra en los meta-
patrones, o piezas reusables que encierran el diseño de complejos marcos-
entornos. En realidad el libro trata sobre marcos (frameworks), y sobre cómo
puede encontrarse en estos (bien establecidos y maduros) un conjunto mínimo de
metapatrones transportable al desarrollo de otros marcos. Pree expone siete me-
tapatrones y después aboga por su integración en el proceso de OOA/OOD me-
diante lo que él mismo denomina Hot-Spot-Driven Design (HSDD: diseño enfo-
cado-a-zonas-sensibles, en insana traducción). En esencia, una Zona-Sensible
es, respecto de un escenario de diseño, un área bien delimitada susceptible de
cambio (esto es, un área flexible). El texto es interesante, con varias referencias al
libro GOF, pero quizás se centra demasiado en el académico (aunque tremen-
damente interesante) ET++ (un marco para desarrollo de GUIs) obviando otros
marcos comercialmente bien establecidos; a la vez, las referencias a los patrones
de codificación en C++ resultan un tanto ingenuas. El libro resulta, con todo, re-
comendable para aquéllos que pretender historizar su tesis doctoral haciendo

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 234
sayo en ella de la tecnología entera. Humm ... ¿Quise decir pretencioso?

Pattern Languages of Program Design, editado por James O. Coplien y Dou-


glas C. Schmidt, 1995, Addison-Wesley, 0-201-60734-4.
Tras leer el libro GOF uno podría preguntarse si el círculo de patrones allí expues-
to podría fácilmente ampliarse. La primera conferencia sobre Pattern Languages
of Program Design (PLoP) supuso la confirmación práctica de las expectativas
generadas. En el presente libro los editores han reunido, comentado y unificado el
material de tal conferencia, que se caracterizó por una organización singularmen-
te efectiva: los papeles no fueron expuestos por los autores, sino por distintos
conjuntos de revisores que expusieron al público las bondades y flaquezas encon-
tradas en cada uno de los patrones presentados. Los 30 patrones presentados
en esta obra son eminentemente textuales (en el sentido clásico de “patrón” de
Alexander), y se han dividido en Marcos, Sistemas y Procesos Distribuidos, Obje-
tos de Negocio, Procesos y Organización, Patrones de Diseño y Catálogos, Ar-
quitectura y Comunicación, Uso de Objetos y Estilo, para terminar con Eventos y
Manejadores de Eventos. El libro representa para el lector la oportunidad de des-
ligar los patrones de tal o cual lenguaje de programación y resulta, a la par que
entretenido, grandemente revelador sobre las ínfulas que debieran animar los
procesos de construcción de software.

LIBROS SOBRE BASES DE OBJETOS

La literatura sobre la mixtura de Bases de Datos y Orientación-a-Objetos y, en


general, sobre sistemas persistentes está poblada de informes técnicos, de im-
plementaciones comerciales y de aventurados esquemas de investigación. En la
bibliografía que sigue me centraré en los títulos que no requieren un bagaje técni-
co excesivo, favoreciendo las visiones simplistas que, al final, generan una cierta
cultura de Bases de Objetos.

Object-Oriented Concepts, Databases, and Applications, editado por Won


Kim & Frederick H. Lochovsky, 1989, Addison-Wesley, 0-201-14410-7.
Este temprano texto contiene artículos muy citados (como el de Roger King, “Mi
Gato está orientado-a-objetos”, que proporciona una leve visión sobre modelos
semánticos), aunque los dedicados a las bases de objetos han quedado un tanto
“historizados”, manteniendo empero unos aspectos intencionales de lectura
aconsejable.

Object-Orientation: Concepts, Languages, Databases, User Interfaces, Se-


trag Khoshafian & Razmik Abnous, 1990, John Wiley & Sons, 0-471-51801-8.
Este libro es un tanto viejo y bastante elemental, aunque confieso que le dispenso

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 235
cierta simpatía (que por definición podría calificarse de afinidad con lo inútil). Las
ideas presentadas aquí son claras -por simples- y leves -por meramente intuiti-
vas-: así, por ejemplo, la parte dedicada a Interfaces de Usuario Orientados-a-
Objetos rebosa candor desde nuestra perspectiva actual. El libro se constituye,
pues, en una introducción amable a esta área tecnológica. Las Selecciones del
Reader’s Digest serían una buena comparanza.

Object Data Management: Object-Oriented and Extended Relational Data-


base systems, R.G.G. Catell, 1991, Addison-Wesley, 0-201-53092-9.
Este es uno de los textos más apreciados por gestores en relación al soporte de
decisiones sobre las Bases de Objetos. Cattell, un “activista” del sector, plantea
un texto con afán de completitud que revisa todos los tópicos de la Tecnología,
incluyendo un repaso de sistemas tradicionales, una panorámica de los nuevos
conceptos de gestión de información, algunas ideas sobre implementación, un
detalle de objetivos y la revisión sumaria de bastantes productos, para acabar
con unas agradables referencias bibliográficas brevemente comentadas.

Object-Oriented Databases, Setrag Khoshafian, 1993, John Wiley & Sons, 0-


471-57056-7.
Yo diría que este es un “texto para gestores (managers)” en el que, tras la consa-
bida y evitable introducción genérica a la Tecnología de Objetos, se muestran al-
gunos aspectos básicos y prácticos de las OODBMSs: modelado, diseño, persis-
tencia, versionamiento, transacciones, concurrencia y ... arquitectura cliente-
servidor. Se puede leer de un tirón, lo que es bueno y malo, naturalmente. En fin:
se trata de una introducción no académica a un área que adolece de demasiado
formalismo huero, así que el lector puede aplicarse el auto-test y decidir.

Sistemas de Bases de Datos Orientadas a Objetos: Conceptos y Arquitec-


turas, Elisa Bertino & Lorenzo Martino, 1993 (traducción 1995), Addison-
Wesley Iberoamericana, 0-201-65356-7.
Hay que decir que el Dr. Miguel Katrib ha realizado una traducción magnífica de
este texto, que se transforma así en el único en castellano que aborda de forma
seria algunos aspectos genéricos teóricos y prácticos sobre las Bases de Obje-
tos: indexación, consultas, evolución dinámica de objetos y esquemas, modelos
de datos y mecanismos de autorización se sopesan entre ejemplos aplicados,
fundamentalmente, a GemStone, O2 e Iris. Siendo, empero, un texto académico
recomendable, adolece de cierta vaguedad anímica si se considera como lectura
de convencimiento para gestores de proyectos y programadores en general. En
resumen: si su biblioteca ha de constar de más de cincuenta libros, compre tam-
bién éste.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 236
The Object Database Standard: ODMG-93, editado por R.G.G. Catell, 1994,
Morgan Kaufmann Publishers, 1-55860-302-6.
Con la intención de normalización “de facto” subyaciendo en cada párrafo, este
libro, instructivo y afortunadamente breve, es de lectura obligada no tanto por la
realidad que refleja sino por la pretensión formalizadora que insufla. ¿OMG está
muerto? No sabría decirlo, pero su cadáver (en todo caso) atrae muchas moscas.

Object-Oriented Databases: Technology, Appllications and Products, Bindu


R. Rao, 1994, McGraw-Hill, 0-07-051279-5.
En este libro asistimos a una demostración de conocimientos prácticos un tanto
deslavazada, aunque útil para determinados lectores: Rao nos lleva a galope por
capítulos triviales y relatos recopilatorios entre los que cruzan exposiciones de
algunas Bases de Objetos comerciales: ObjectStore, Versant, Objectivity/DB; y en
menor medida GemStone, IRIS, UniSQL y otras. El texto aparece adecuado para
gestores que deseen asimilar de forma rápida las técnicas y esquemas más usa-
dos en los productos comerciales, aunque adolece de coherencia. Yo suelo, no
obstante, recomendar la lectura directa de los manuales de las distintas
OODBMSs, pues éstos suelen incluir unas buenas introducciones genéricas y
usualmente están pedagógicamente estructurados para su pronta asimilación.

Object Databases: The Essentials, Mary E. S. Loomis, 1995, Addison-Wesley,


0-201-56341-X.
Debo reconocer mi debilidad por la Dra. Loomis: el libro, al igual que sus colum-
nas periódicas en el JOOP, está escrito en un lenguaje sorprendentemente claro
que hace gala de un pragmatismo agradable y natural. La exposición está bien
estructurada, es amena, encaja a la vez con un público técnico y de gestión, y
resulta, al fin, en un buen sabor post-lectura. La asunción, por otra parte, de
distintos puntos de vista (del administrador, del programador, etc.) confiere al
texto una perspectiva única que se convierte en perfecta guía respecto de las
distintas opciones teóricas: si la persistencia ha de ser ortogonal al tipo, si el
enfoque pesimista de concurrencia es apropiado, etc. Un colega, experto desde
hace muchísimos años en el área de la Tecnología de Objetos, me comentaba
hace poco entusiasmado: “He leído ya la mitad del libro y ... ¡lo entiendo todo!”. El
texto es totalmente autónomo, aunque es opinión general (y por tanto discutible de
pleno) que complementa el más antiguo de Cattell. En verdad que si sólo ha de
comprar un libro sobre Bases de Objetos, elija éste. Y es que esta vez la pasión y
la razón andan juntas.

Modern Database Systems: The Object Model, Interoperability, and Be-


yond, editado por Won Kim, 1995, Addison-Wesley, 0-201-59098-0.
Siguiendo un esquema similar al texto del 89, aparecen aquí muchos artículos de

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 237
distintos autores que se estructuran en dos capítulos tecnológicos: Bases de Da-
tos de Próxima Generación e Interoperabilidad con Bases de Datos Preexisten-
tes. La adecuada disposición de las contribuciones, mayormente originales para
el texto, permite una lectura secuencial y muestra una precisa visión del estado
actual de la Tecnología.

LIBROS SOBRE INGENIERÍA DE PROCESOS

Reengineering the Corporation: A Manifesto for Business Revolution, por


Michael Hammer & James Champy, New York: HarperCollins Publishers, 1993,
0-88730-640-3.
216 páginas (en la edición inglesa, claro) de lectura fácil con muchos ejemplos y
borrosas formas-de-hacer tratan de comunicar al lector una supuesta mayor efec-
tividad (partiendo desde la nada) en el enfoque de las corporaciones del mañana.
Aquí se examinan (y esto es un eufemismo) distintos ejemplos de cómo la focali-
zación en la importancia e los procesos ha llevado a algunas empresas a desa-
rrollos brillantemente efectivos. Se trata, en definitiva, de una buena lectura de
verano. Nada más. Y nada menos. Claro que no es un manual, y ni siquiera con-
tiene postulados o métodos. Es ... un best-seller. Su inclusión aquí se debe a que
sin este libro el lector no podría comprender los siguientes (y no el texto, sino la
razón de su publicación, claro).

The Object Advantage: Business Process Reengineering with Object Tech-


nology, por Ivar Jacobson, Maria Ericsson y Agneta Jacobson, ACM Press,
1994.
Los Casos-de-Uso de Jacobson encajan perfectamente en el nicho comercial de
la “Reingeniería de Procesos de Negocio Orientada-a-Objetos” (OOBPR), así que
en este libro se relata (más que detalla) un enfoque de integración orientada-a-
objetos con entregables, ejemplos y una clarísima intención de promoción comer-
cial. ¡Claro! Hay que vender re-ingeniería, y el cóctel con procesos, negocios y
objetos es publicitariamente explosivo. En fin, yo les aconsejaría este libro para
que puedan comprobar por sí mismos como las necesidades comerciales con-
vierten a un sólido autor en un autor de fama corporativa.

Business Engineering with Object Oriented Technology, por David A. Taylor,


John Wiley & Sons, 1995.
La primera parte de este libro, de forma curiosamente interesante, simila una pro-
longación “negocial” del texto de Wirfs-Brock, pero cuando el lector empieza a
desesperanzarse empieza a aparecer y conformarse lo que Taylor denomina In-
geniería Convergente (Convergent Engineering): un marco básico orientado-a-
objetos de sólida practicidad que pretende constituirse en base del desarrollo de

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 238
software fiable. De una forma un tanto desmitificadora en estos tiempos de reina-
do del “proceso” Taylor prácticamente señala: “¿Focalización en los procesos?
¡No, gracias! No sustituya: ¡Integre!”. El libro es agradablemente corto y deja un
buen regusto final.

LIBROS SOBRE GESTIÓN DE PROYECTOS

Succeeding with Objects: Decision Frameworks for Project Management,


por Adele Goldberg & Kenneth S. Rubin, 1995, Addison-Wesley, 0-201-62878-
3, 542 págs.
Goldberg y Rubin se han despachado con un libro imprescindible para cualquiera
interesado en la gestión práctica de proyectos orientados-a-objetos. Los marcos
de decisión a que se refiere el título son “secuencias organizadas de decisiones
que deben tomarse” en relación directa con los objetos insertos en tales marcos y
también con la arquitectura del proyecto a abordar, y cuyo objetivo último es satis-
facer con éxito las metas impuestas por el proyecto mismo. En este inteligente y
sentido libro los autores examinan el acercamiento inicial a la Tecnología, la se-
lección del adecuado modelo de proceso y la planificación y el control del proyec-
to. El reúso se discute seguidamente, pero inserto en esquemas organizativos
que permitirían su efectiva aplicación. La composición de equipos es fundamental
y en el marco correspondiente se exponen las decisiones a tomar para la consti-
tución de un equipo exitoso, para despues examinar el problema de qué herra-
mientas usar, que planes de formación asumir, cómo validar el trabajo realizado y
unas últimas palabras sobre el fracaso con objetos. El estilo es suelto y los apén-
dices inestimables (verbigratia, el cuestionario sugerido para gestores de proyec-
tos). Un responsable de informática ignorará esté libro a su riesgo y cuenta.

Pitfalls of Object-Oriented Development, por Bruce F. Webster, 1995, M&T -


Books, 1-55851-397-3.
He de confesar que este libro no colmó las expectativas que el título y su publici-
dad me habían sugerido. Claro que satisfacerme a mí es azarosamente prolijo.
Con todo el texto es buen material para los efectivos practicantes, novicios o no,
de la Tecnología de Objetos. Cada “pitfall” consta de una descripción primera,
seguida de un detalle de los síntomas, más las posibles consecuencias de su
aplicación, técnicas de detección, consejos para su eliminación/extracción y, para
finalizar, algunas pistas encaminadas a su prevención. Webster divide los pro-
blemas en: conceptuales, políticos, de gestión, de análisis y diseño, de entornos,
lenguajes y herramientas, de implementación, de clases y objetos, de codifica-
ción, de calidad y de reuso: un ambicioso conjunto que pretende abarcar el ciclo
de vida completo del software y su redención mediante la penitencia constructiva.
Atiendan a algunas de las trampas: (5.4) Usar C++, (5.5) No usar C++, (3.5) Inten-
tar demasiadas cosas, demasiado pronto, demasiado rápido, (1.1) Adoptar la

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 239
Orientación-a-Objetos por razones equivocadas, (6.8) Ser seducido por el lado
oscuro, etc. etc. En fin, con esta obra (que se supone complementaria del magní-
fico texto de Goldberg y Rubin, Succeeding with Objects) se pretende conseguir
algo parecido a lo que con los patrones de diseño: comunicar la experiencia con-
trastada en diseño real de sistemas a los postulantes, aunque aquí por el lado
morboso del castigo y el didactismo. Muy entretenido, al fin y a la postre.

Object Lessons: Lessons Learned in Object-Oriented Development Pro-


jects, por Tom Love, 1993, SIGS Books, 0-9627477-3-4 (Prentice-Hall 0-13-
472432-1).
El Dr. Love semeja los modos y estilos de Drucker en un libro amable que destila,
a partes iguales, conocimiento y ligereza. Es curioso que el relato del texto se
asuma intuitivamente como lanzado “desde arriba”, en un implícito reconocimiento
de la experiencia con escasas fisuras del autor en el área de gestión de proyec-
tos. El libro empieza con el histórico ejemplo de la construcción del navío “Vasa”,
cuyo desarrollo y último fracaso práctico se ajustan condenadamente bien a los
esquemas de fallo en proyectos software. Tras este ejemplo afortunado, Love se
despacha con un nuevo símil sobre objetos (un objeto es una “máquina electróni-
ca”, con entrada, salida y alimentación eléctrica), tras lo que intenta una introduc-
ción liviana a la Tecnología de Objetos que servirá de base para un conjunto de
capítulos en los que se citan normas difusas (por extendidas en el texto), pero de
gran ejemplicidad práctica. Así, Love nota que “los programadores del mundo real
no reutilizan”, o “Escoja proyectos importantes, no aquéllos sin importancia”, tam-
bién como “La inmersión funciona mejor” planteando la comparación con el
aprendizaje de un idioma extranjero. Se trata, como el título indica, de la comuni-
cación de la valiosa experiencia personal en un área que necesita de menos
pasmo comercial y más practicidad “real”. Yo diría que este libro es una buena
introducción (a modo de vistazo atemperado) de la Tecnología de Objetos para
jefes de proyecto o departamento de software, con el único aviso: “No contiene
fórmulas mágicas”.

LIBROS CONTRA LA TONTERÍA

Los siguientes textos no se ciñen al ámbito estricto de la Tecnología de Objetos,


sino que exponen de forma afortunadamente acertada un cúmulo de criterios y
principios, explícitos o no, subyacentes en el diseño de sistemas software de ca-
lidad. La selección, como siempre, es absolutamente personal y medida: por ca-
da libro interesante que estudio he de leer otros cuatro lesivos, triviales o ridícu-
los. Naturalmente yo intento leer únicamente los buenos, pero es difícil (y poco
educado) juzgar en voz alta sin conocer, así que tras conocer y desechar, he aquí
mi selección, mascada ya para que el lector trabaje menos:

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 240
201 Principles of Software Development, por Alan M. Davis, 1995, McGraw-
Hill, 0-07-015840-1.
Davis plantea en este texto una completa conjunción de princi-
pios/mandamientos/patrones que, aplicados, debieran procurar sistemas softwa-
re más racionales, sólidos y mantenibles: en resumen, de calidad. Los principios
son cortos y no tienen desperdicio: (34) Todo documento software necesita un
índice, (82) Los grandes diseños vienen de grandes diseñadores, (30) Sigue a
los lemmings con cuidado, (22) Técnica antes que herramientas, (170) Sé pesi-
mista sobre la evolución del software, ... y muchos otros. Cada principio se acom-
paña de una corta justificación, sirviendo así el texto de guia referencial a ingenie-
ros (sic) software, directivos y estudiantes. Conviene recordar que los seres
humanos tienden a trivializar rápidamente lo que les parece obvio en un contexto
dado, así que a más de uno le resultará tremendamente instructivo repasar algu-
nos conceptos que tenía por asimilados pero que nunca realmente ha aplicado
con claridad.

Software Requirements & Specifications: A Lexicon of Practice, Principles


and Prejudices, por Michael Jackson, 1995, Addison-Wesley, 0-201-87712-0.
Este libro, aunque liviano, me ha encantado hasta la sonrisa: su estructura es la
de un diccionario, de forma que las distintas secciones están ordenadas por or-
den alfabético, y aun así puede leerse de corrido. Jackson aporta aquí importan-
tes dosis de sentido común y claridad distribuidas en pequeñas porciones reple-
tas de inteligencia y solidez. La traslación, por ejemplo, del problema de los puen-
tes de Königsberg al terreno de los escenarios en especificaciones es particu-
larmente afortunada; el planteamiento, bajo el epígrafe “Brillantez”, de los analis-
tas insustituibles que generan diseños que ni ellos mismos alcanzan a compren-
der es, por otro lado, grandemente instructivo (Oh Sancta Simplicitas). La incom-
prensión, por ejemplo, de los menús por parte de los comensales, en el apartado
“Restaurantes”, resulta intelectualmente punzante. En fin, Jackson se despacha
con sorpresas página tras página y desarrolla temas tan interesantes como El
Marco JSP, Dekker (sobre el Problema de la Mutua Exclusión), Eventos e Interva-
los, etc. En el sentido más cercano al espíritu de D’Alembert, este diccionario es
una buena herramienta contra la estupidez y el fanatismo.

Ricardo Devis Botella C++: STL, Plantillas, Excepciones, Roles y Objetos Página 241

También podría gustarte