principios del diseño orientado a objetos (ood) aplicados en c

82
Principios del diseño orientado a objetos (OOD) aplicados en C Ing. Leandro Francucci ( [email protected] ) Vortex 10 de agosto de 2016 1

Upload: leandro-francucci

Post on 15-Apr-2017

89 views

Category:

Software


1 download

TRANSCRIPT

Page 1: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Principios del diseño

orientado a objetos (OOD)

aplicados en CIng. Leandro Francucci ([email protected])

Vortex

10 de agosto de 2016

1

Page 2: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Objetivo

Comprender los principios fundamentales del diseño orientado a objetos (OOD) aplicados al desarrollo de software en lenguaje C.

Y por medio de estos:

Fomentar la producción de software flexible, fácil de mantener y reutilizable.

Lograr un diseño de software adaptable a los cambios, hoy conocido como diseño ágil.

Disminuir el tiempo, complejidad y costo del desarrollo.

2

Page 3: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Agenda

Comprender y aplicar el encapsulamiento y la modularidad en lenguaje C

Aplicación del proceso de análisis, generalización, parametrización e instanciación de una solución general a un problema específico.

Identificación de instancias por referencia y por descriptores.

Creación de instancias mediante los patrones: asignación dinámica de memoria, asignación estática de memoria, asignación desde un pool.

Abstracción mediante punteros opacos, aplicando los métodos por definición privada y definición privada parcial.

Utilizar abstracciones, mediante la herencia y el polimorfismo en lenguaje C

Abstracciones. ¿Porqué y cuándo aplicarlas?. Definición y aplicación de interfaces y operaciones polimórficas.

Selección de implementación de interfaces en tiempo de compilación y en tiempo de ejecución.

3

Page 4: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Encapsulamiento,

modularidad e instancias4

Page 5: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Instancia única o singleton

Supongamos que un determinado sistema procesa comandos recibidos asincrónicamente desde un único canal de comunicaciones.

Los cuales se constituyen por cadenas de caracteres ASCII.

La entidad que los interpreta y procesa, se ejecuta de manera independiente, en contexto y tiempo, a la recepción de los mismos.

Por tal motivo, el sistema utiliza una estructura de datos cola, ya que la misma permite almacenar en forma ordenada una cierta cantidad de elementos, en este caso caracteres ASCII del tipo char, para luego, en otro contexto y tiempo, ser extraídos y procesados acordemente, desacoplando así, la recepción del procesamiento de los datos.

Particularmente, en esta estructura, los elementos se extraen en orden cronológico, desde el más antiguo al más reciente, esto determina que la cola sea una estructura de datos FIFO, en la cual el primer elemento agregado será el primero en extraerse.

Generalmente, se implementa como un buffer circular o una lista enlazada.

5

Page 6: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Módulo ChQueue

Archivo de inclusión, ChQueue.h.

Define sus características (atributos

y operaciones) públicas.

6

Parte del archivo de

implementación, ChQueue.c

Define las características privadas

Page 7: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Observaciones

Tanto las operaciones como los atributos de la cola de caracteres

se concentran (encapsulan) en un único par de archivos, donde el archivo ChQueue.h hace visibles las variables y operaciones

públicas, mientras que el archivo de implementación ChQueue.c

mantiene el cuerpo de las funciones públicas y privadas, al igual

que sus variables.

Por lo general, el nombre de las operaciones públicas de un

módulo con funciones específicas, contiene un prefijo, que se

utiliza para identificar claramente a qué módulo pertenece y

además evitar que se solapen nombres entre módulos del sistema.

7

Page 8: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Módulo BitPattQueue y el

código duplicado Ahora, el sistema requiere una nueva cola, esta vez para almacenar

patrones de datos de 48-bits, typedef unsigned char

BITPAT_T[NUM_BYTE_PAT], donde NUM_BYTE_PAT es igual a 6.

Dado que el sistema ya cuenta con una implementación probada y funcionando de una cola (ChQueue), la solución inmediata es tan simple como copiar (duplicar) y adaptar esta última, cambiando nombres, tipos de datos y funciones, creando así la cola BitPattQueue.

8

¿Qué desventajas

ocasiona esta solución?

Archivo de inclusión, BitPattQueue.h

Page 9: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Generalización

Evitando la duplicación Para evitar los potenciales inconvenientes de la solución anterior

(código duplicado), necesitamos un único módulo capaz de administrar múltiples colas, que pueda adaptarse a las necesidades funcionales de cada cola en particular. Por lo tanto:

Partimos de ChQueueue, analizamos los requerimientos de ambas colas, las comparamos y encontramos sus diferencias. Estas definirán los parámetros del nuevo módulo.

9

Page 10: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Generalización

Partimos del módulo ChQueue, probado y en pleno funcionamiento.

Generalizamos ChQueue, de modo que pueda manipular múltiples colas, concentrando los datos de cada cola en una única y nueva entidad, Queue.

Esto implica que exista una instancia de Queue por cada cola que se requiera.

En este contexto, una instancia es simplemente un objeto de tipo Queue ubicado en memoria.

Luego, Queue debe proveer al menos las operaciones básicas para manipular una cola o instancia de Queue.

10

Generalización mediante estructura Queue

Page 11: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Abstracción

El proceso anterior genera la abstracción Queue.

La mejor manera de eliminar el código redundante es crear abstracciones.

Al eliminarlo, tiende a lograr sistemas más fáciles de entender y mantener.

Definición de abstracción: “la amplificación de lo esencial y la eliminación de lo irrelevante”.

11

Page 12: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Identificando las instancias

Dado que Queue administra múltiples colas o instancias de Queue, para asegurar que sus funciones accedan a los datos de la instancia correcta, necesitamos indicarle de qué instancia se trata.

Si bien existen diversas maneras de resolver dicha situación, sólo exploraremos la identificación de instancias por referencia y por descriptores.

Esto está directamente ligado con el tipo de interfaz que presente Queue.

12

Page 13: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Por referencias13

Archivo de inclusión, Queue.h

Operaciones de ChQueue, por referencia

Page 14: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Observaciones

La manera más tradicional de indicarle de qué instancia se trata, consiste en pasar a cada función (operación) de Queue un puntero a la instancia en cuestión, generalmente de nombre me.

En este caso, el nombre de las operaciones se forma por <prefijo>_<operación>(), donde el prefijo proviene del nombre del módulo.

La palabra const luego del * en la declaración del puntero me, asegura que las operaciones no puedan cambiar su valor, o sea, la instancia sobre la que se intenta operar.

Si bien el método presentado se emplea asiduamente en lenguaje C, manifiesta un inconveniente: la definición de la estructura Queue queda innecesariamente visible (pública) al sistema.

¿Qué potenciales problemas puede ocasionar el hecho de publicar la estructura de datos interna del módulo?

14

Page 15: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Por descriptores15

Definiciones públicas de Queue

por descriptores

Operaciones de ChQueue por descriptores

Page 16: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Creando instancias

Una instancia de Queue es un objeto de tipo Queue asignado en memoria, el cual representa en tiempo de ejecución una cola determinada, como ChQueue o BitPattQueue.

Por definición, los objetos existen en tiempo de ejecución y en un momento determinado, lo que significa que pueden existir y dejar de hacerlo, a criterio del sistema.

Al instanciar, la memoria asignada se reserva durante la existencia del objeto, desde su creación hasta su destrucción.

Existen diversas maneras de implementar la asignación de memoria, aquí exploraremos los patrones de asignación de memoria: dinámico, estático y pool de objetos.

Por "pool" entendemos una colección o lista de objetos disponibles y pre-asignados en memoria.

Otro de los métodos muy utilizados en embedded, es el patrón de bloques de tamaño fijo (no presentado en este tutorial).

Si bien la aplicación de un patrón u otro se determina según las características particulares del sistema, es común encontrar que un mismo embedded system de complejidad media o superior, utilice uno o varios de los patrones listados.

16

Page 17: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Patrón de asignación

dinámica de memoria El hecho que un objeto exista durante un momento determinado,

implica un instante de creación y otro de destrucción.

Por destrucción, entendemos que el objeto en cuestión ya no se utiliza en el sistema y en consecuencia, es una buena oportunidad para que el sistema recupere la memoria que le asignó, de modo tal que, en principio, el sistema pueda re-utilizarla.

En general, esta mecánica produce sistemas óptimos respecto de la utilización de memoria.

La manera más tradicional y simple de asignar y liberar memoria es mediante la asignación dinámica de memoria.

Sin embargo, presenta dos problemas: (1) la temporización no determinística durante la asignación y liberación de memoria, y (2) la fragmentación de memoria, esto último puede provocar una falla fatal en el sistema.

Por dichas razones, el uso de este método debe justificarse claramente, caso contrario, es preferible aplicar un método alternativo.

17

Page 18: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Patrón de asignación

dinámica de memoria

18

Archivo de inclusión Queue.h

para asignación dinámica de

memoria, con operaciones

“especiales”

El constructor también suele nombrarse: Queue_construct(), Queue_ctor(), Queue_create() o Queue_alloc()

El destructor también suele nombrarse: Queue_destroy(), Queue_dtor(), Queue_close() o Queue_free()

Page 19: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Patrón de asignación

dinámica de memoria

19

Implementación de

las operaciones

“especiales”

Page 20: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Patrón de asignación

dinámica de memoria

20

Constructor e inicializador

Simplificación

constructor e

inicializador en una

única operación

Page 21: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Patrón de asignación

dinámica de memoria

21

Parte de las operaciones básicas

Inicializador de ChQueue

Page 22: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Patrón de asignación

estática de memoria

22

Inicializador de Chqueue

Page 23: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Patrón de asignación

estática de memoria

23

Constructor de Queue

Page 24: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Patrón de asignación

estática de memoria

24

Inicializador estático en startup

Uso del inicializador estático en startup

Este patrón puede aplicarse aún en sistemas que fueron concebidos utilizando el patrón de asignación dinámica.

Por ejemplo, eliminando el uso del destructor en todo el sistema.

Page 25: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Patrón de asignación de

objetos desde un pool

25

Definición de operaciones especiales identificando la instancia

mediante referencias

Definición de operaciones especiales identificando la instancia

mediante descriptores

Page 26: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Patrón de asignación de

objetos desde un pool

26

Implementación de

operaciones

especiales

identificando la

instancia mediante

descriptores

Colección de

objetos o “pool”

Page 27: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Patrón de asignación de

objetos desde un pool

27

Inicializador de ChQueue identificando la

instancia mediante descriptores

Page 28: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Aún más abstracción:

punteros opacos

28

Declara el tipo Queue como un tipo incompleto

En este contexto, un puntero opaco es uno tal que su tipo no está completo o definido.

No permite instanciar un objeto de tipo Queue

pero si un puntero a ese tipo. ¿Porqué?

Ahora, completamos el tipo incompleto y

podemos declarar tanto punteros como objetos de tipo Queue.

Page 29: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Método: definición privada 29

Este método oculta por

completo la definición de Queue, por lo tanto sus

atributos son privados y no

visibles al resto del sistema.

La operaciones mantienen la misma forma que con la definición

basada en referencias.

De esta manera se refuerza el encapsulamiento y la abstracción.

Page 30: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Método: definición privada 30

Este método

impone que la

asignación de

memoria sea

responsabilidad del

módulo Queue

Implementación de

Queue, Queue.cUso del método.

Page 31: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Método: definición privada

parcial

31

Este método publica parte de la definición de Queue. Utiliza la

herencia simple implementada en lenguaje C.

Definición de la estructura pública, en Queue.h

Definición de la estructura privada, en Queue.c. Ver

ubicación y tipo del miembro base

La definición de las operaciones mantiene la forma.

Page 32: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Método: definición privada

parcial

32

Parte de la implementación propuesta para Queue.c

PrivQueue agrupa los atributos

privados y públicos.

Page 33: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Método: definición privada

parcial

33

Su uso no tiene cambio alguno respecto del método anterior

Puede accederse a los miembros públicos

El puntero chque es opaco

Page 34: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Abstracción, interfaces y

polimorfismo34

Page 35: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Un simple ejemplo

Supongamos una función, Message_receive(), que recibe

por argumento un mensaje, Z, que transporta cierta info.

El mensaje Z, se representa por:

Message_receive(), recibe y procesa el mensaje Z.

35

Una estructura struct en C es una colección de

una o más variables, agrupadas bajo un mismo nombre. Estas se denominan miembros o

campos. Frecuentemente, definen los atributos

de la entidad que las agrupa.

Page 36: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Un simple ejemplo

Supongamos ahora que se necesitan procesar dos nuevos

mensajes, X e Y.

36

Page 37: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Premisas de la solución

Message_receive() debe ser el único acceso para la recepción

de mensajes.

Su prototipo debe mantenerse tal como fue concebido.

Debe ser flexible para soportar la extensión de nuevos mensajes y

modificaciones a los existentes.

Debe minimizar las modificaciones del programa existente.

37

Page 38: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Reordenando mensajes

De las estructuras de mensajes X, Y y Z:

Se ordenan sus miembros e identifican sus atributos comunes

Se agrega un campo para identificarlos

Luego, se agrupan los

miembros comunes en una

nueva estructura y se re-definen los mensajes.

38

Page 39: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Mensajes re-definidos

¿Podremos mantener el mismo prototipo de Message_receive()

para todo mensaje, evitando

modificar el código que la invoca?

39

timestamp

id

Message

var2

var1

base

MsgX

Base classDerived class

var2

var1

timestamp

id

Storage

La estructura común (base)

se ubica como el primer

miembro de la estructura

derivada. Esto es parte

fundamental de la solución

propuesta

Page 40: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Generación de mensajes

setMsg() completa

la estructura común

entre mensajes.

Observar la conversión

de tipo (casting).

40

Page 41: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Descripción

La conversión de tipo (casting) efectuada es legal,

transportable y garantizado por el standard de C, ya que el

anidamiento de estructuras propuesto siempre alinea el primer miembro base, al comienzo de las estructuras, de

acuerdo con IEC/IEC9899:TC2 (informalmente C99), es decir,

no hay rellenos (padding) al comienzo de una estructura.

41

Diferentes alternativas que

realizan la conversión de tipo.

Page 42: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Con macros

Para encapsular la conversión de tipo, y así aumentar la

legibilidad y ocultar los detalles innecesarios, puede

recurrirse al uso de macros.

42

Page 43: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Recepción de mensajes

Cumple con la premisa de

mantener el prototipo de Message_receive()

(interface).

Observar la conversión de

tipo

Esta función es polimórfica,

¿porqué?.

En este tipo de función,

¿qué consecuencias tiene el uso del switch y el miembro

id?

43

Page 44: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Descripción

Recibe el mensaje como un puntero a Message, extiende

el soporte de nuevos mensajes manteniendo su prototipo

El campo id permite identificar el tipo de mensaje

Se realiza el casting apropiado de acuerdo al mensaje

recibido.

MSG_CAST() permite tratar el puntero m de tipo Message

como uno de tipo MsgX. Por ejemplo:

44

Page 45: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Recepción de mensajes

Accede y muestra la información básica del mensaje

recibido

45

Page 46: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

En resumen

El anidamiento de estructuras permite mantener el prototipo de Message_receive(), extendiendo sus servicios.

El método aplicado, agiliza el soporte de nuevos mensajes y

modificaciones a los atributos asociados.

Message_receive() recibe punteros a Message, aunque

son referencias a instancias de tipos más elaborados, como MsgX o MsgY.

La generación de mensajes: trata un puntero de tipo MsgX

como uno de tipo Message.

La recepción de mensajes: trata un puntero de tipo Message como uno de tipo MsgX.

El miembro id, permite identificar el tipo de mensaje.

46

Page 47: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Formalizando la práctica

¿Qué hay detrás de estas prácticas de programación?

El método descripto no es simplemente una técnica de codificación

Es una implementación en C, que emula el concepto de herencia de clases de la OOP

Una clase es una estructura de C, con una particularidad, además de contener datos (atributos) contiene comportamiento (operaciones), a diferencia de las estructuras en C que sólo contienen datos.

Aún así, el comportamiento puede asociarse indirectamente a una estructura de C, definiendo funciones separadamente e incluyendo punteros a estas dentro de la estructura de C.

47

Page 48: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Herencia

Es una manera de representar clases que son un “tipo

especializado de” otra clase.

La clase especializada (derivada o hija) hereda las

características de la clase más general (base o padre).

Todas las características de la clase padre son también

características de la case derivada.

Aún así, está permitido especializar y/o extender una clase

más general.

Permite la reutilización tanto del diseño como el código,

evitando su duplicación.

48

Page 49: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Mensajes derivados

Del ejemplo anterior, la estructura Message, es común a

todos los mensajes, es la estructura base.

Las estructuras MsgX, MsgY y MsgZ son derivadas de

Message.

Las estructuras derivadas extienden los atributos de la

estructura base.

49

Page 50: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Especialización

Significa que una clase derivada puede redefinir las

operaciones provistas por la clase base.

Está relacionado con el polimorfismo de lenguajes

orientados a objetos.

Permite a un mismo nombre de función representar diferentes

comportamientos, de acuerdo al contexto.

Por supuesto, C no soporta implícitamente el polimorfismo.

Aunque puede imitarse.

¿Cómo podríamos implementar el polimorfismo en C?

50

Page 51: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Extensión

Significa que una clase derivada define nuevas

características, respecto a su clase base.

Una de las maneras de implementarla en C, es incluyendo

la clase base como una estructura dentro de la clase

derivada, ubicada específicamente como primer miembro de esta última.

Esto se denomina herencia por composición.

51

Page 52: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Diagrama del ejemplo52

id: MsgIdtimestamp: TimeStamp

Message

var1: double;var2: uint32_t

MsgX

var1: uint8[Y_VAR1_SIZE];

MsgY

var1: uint8_tvar2: uint32_t

MsgZ

Relation:

generalization

Derived classesExtended

attributes

struct MsgX

{

Message base;

double var1;

uint32_t var1;

};

MSGID_XMSGID_YMSGID_Z

<<enumeration>>MsgId

<<usage>>

ClientBase class

Page 53: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Upcasting

En términos de la OOP, la conversión de tipo de una clase

derivada a una clase más general se denomina

upcasting. Del ejemplo:

De manera equivalente con una macro:

53

Page 54: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Downcasting

En términos de la OOP, la conversión de tipo de una clase

base a una clase derivada se denomina downcasting.

Aunque no es una práctica 100% segura. ¿Porqué?. Del ejemplo:

De manera equivalente con una macro:

54

Page 55: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Herencia simple en C

La estructura RKH_SBSC_T desciende de RKH_ST_T y esta de

RKH_BASE_T.

typedef struct RKH_BASE_T

{

ruint type;

const char *name;

} RKH_BASE_T;

typedef struct RKH_ST_T

{

struct RKH_BASE_T base;

RKH_ENT_ACT_T enter;

RKH_EXT_ACT_T exit;

RKHROM struct RKH_ST_T *parent;

} RKH_ST_T;

typedef struct RKH_SCMP_t

{

RKH_ST_T st;

RKHROM struct RKH_TR_T *trtbl;

RKH_PPRO_T prepro;

RKHROM void *defchild;

RKHROM struct RKH_SHIST_T *history;

} RKH_SCMP_T;

Instance of the

base structure

RKH_BASE_T

RK

H_

SB

SC

_T

RK

H_

BA

SE

_T

RK

H_

ST

_T

Members added in

the derived structure

RKH_ST_T

Members added in the

derived structure

RKH_SBSC_T

55

Page 56: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Ejemplo simple

e: RKH_SIG_Tnref: rui8_tpool: rui8_t

RKH_EVT_T

cmd: rui8_t

RPC_REQ_T

reason: rui8_t

RPY_SMS_REJ_T

txtsz: rui8_tdst: EADR_Tsc: EADR_Ttxt: char txt[ PDU_SIZEOF_UD_ASCII ]

REQ_SEND_SMS_T

rmj: rui8_trmn: rui8_t

REQ_DIC_REV_T

56

Page 57: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Ejemplo con polimorfismo57

ComServer, especializa (cambia el

comportamiento de) las funciones heredadas post() y dispatch()

Cleaner hereda todas las

características de ActiveObject

ActiveObject como base provee su

propia implementación de las funciones init(), post() y

dispatch(). Podría denominarse

comportamiento por defecto.

init()post()dispatch()

ActiveObject

post()dispatch()

ComServer Cleaner

Page 58: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Ejemplo con polimorfismo58

Command no provee implementación de la función format(),

esta es abstracta.

Cada una de las estructuras BaudRate, Pin y FacilityLock,

proveen su propia implementación de la función format()

De esta manera, Client invoca a format() desconociendo

los detalles de su implementación, es decir, se abstrae de esta.

format()

Command

format()

baudrate: uint32_t

BaudRate

format()

actual: char *new: char *

Pin

format()

facility: char *oldPwd: char *newPwd: char *

FacilityLock

Client

Page 59: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

¿Porqué estas prácticas?

Construir software que posea una buena estructura de

modo tal que sea flexible, fácil de mantener y reutilizable.

La aplicación de estos principios, prácticas y patrones para

mejorar la estructura y legibilidad del software, es un

proceso, no un evento, es decir se aplican continuamente,

manteniendo en todo momento el diseño del sistema tan

simple, claro y expresivo como sea posible.

A su vez, si el desarrollo se realiza en pequeños incrementos,

aplicando prácticas que provean la disciplina y la

realimentación necesaria, el software se construye

rápidamente, aún ante cambios vertiginosos de requerimientos.

Todo esto constituyen el Desarrollo Ágil

59

Page 60: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Polimorfismo en C

Una clase derivada puede especializar o redefinir las

operaciones de su clase base. En C++ funciones virtuales.

Una alternativa para lograr el polimorfismo en C es

mediante punteros a función y anidamiento de estructuras.

Aplicando esto último a Message_receive():

60

Compararla contra su versión con switch y el miembro id.

Page 61: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Tabla de funciones virtuales

En C se requiere un nivel más de indirección, que permita

acceder a la operación correspondiente según el tipo de

estructura derivada (del ejemplo, mensaje recibido).

Las operaciones (funciones virtuales) se definen en una tabla, vtbl (tabla de funciones virtuales).

La funciones virtuales definen las operaciones de la clase.

La vtbl es una estructura anidada.

Toda estructura derivada que requiera especializar o

extender las operaciones heredadas, proveerá su propia vtbl. Referenciada por el puntero vptr.

61

Page 62: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Implementación en C62

Ya no se requiere el identificador de tipo de mensaje, id.

Se agrega el puntero vptr.

Toda estructura derivada que lo requiera, puede proveer su propia tabla de funciones.

La tabla de funciones virtuales es simplemente una estructura, donde cada miembro es un puntero a función.

La clase derivada no sufre cambio alguno respecto de la implementación sin soporte de funciones virtuales. ¿Porqué?.

Regla general: un cambio común a todos los mensajes, sólo impacta en la clase base. ¿Porqué?.

¿Cuál es la razón del const y del puntero *me?.

Page 63: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Inicialización de vtbl

Esta práctica requiere que cada instancia inicialice vptr.

Si la clase derivada sólo hereda el comportamiento de su base, vptr se inicializa con la tabla de funciones virtuales

de su clase base, podríamos decir adopta el

comportamiento “por defecto”. Aún así, las operaciones pueden especializarse.

Si la clase base define operaciones pero no provee

implementación alguna, esta no es “instanciable”. En cuyo

caso el comportamiento lo provee la instancia de la clase

derivada.

¿Qué ventajas provee el anidamiento de estructuras en la

tabla de funciones virtuales?

63

Page 64: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Definición de vtbl

Ya no es necesario el miembro id de

Message. ¿Porqué?

¿Qué diferencias se

observan entre esta

implementación y la

anterior de Message_receive()?

64

Page 65: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Esquema de memoria

resultante

65

timestamp

vptr receive

Message

var2

var1

base

MsgX MsgXVtbl

control

base

controller

MsgX_receive

MsgVtbl

Base classDerived class

Derived vtbl

class

Base vtbl

class

Virtual

functions

Page 66: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Alternativa

Agrega el miembro offset a

Message, de forma tal de

independizar la posición

relativa de esta estructura en

cada una de sus derivadas

66

Page 67: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Abstracciones

Supongamos que disponemos de un sistema que registra y

emite la trazabilidad de una aplicación.

67

Client Tracer

Client Tracer FileOut

El módulo Tracer almacena los registros de la trazabilidad en un archivo determinado, como una entidad monolítica.

Ahora, delegamos la emisión de los registros de Tracer al módulo

FileOut dedicado

exclusivamente a la

manipulación de estos registros

sobre archivos.

Page 68: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Abstracciones68

Client Tracer FileOut

StdOut

SerialOut

SocketOut

El sistema evoluciona y ahora

requiere emitir los registros a

diferentes medios.

Por lo tanto, modificamos Tracer

para lograr el soporte necesario.

De ahora en más, un cambio en

los módulos de salida puede

implicar cambios en el módulo Tracer.

¿Cómo podríamos evitar esto último?

Page 69: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Abstracciones69

La mejor manera de aislar o desacoplar dos o más módulos es mediante una abstracción.

Si bien las abstracciones están fijas, representan un grupo ilimitado de posibles comportamientos.

Ahora, Tracer accede a la abstracción, y por lo tanto permanece “cerrado” para su modificación, ya que depende de una abstracción que está fija. Sin embargo, su comportamiento puede extenderse mediante la creación de nuevos módulos derivados de la abstracción.

Esto último conforma el Principio de Abierto/Cerrado (OCP).

Tracer <<interface>>TracerOut

FileOut StdOut SerialOut SocketOut ...

Client

Page 70: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Abstracciones70

El módulo de alto nivel Tracer no depende de los módulos de bajo nivel, FileOut, StdOut, etc. Ambos dependen de la abstracción TracerOut.

La abstracción no depende de los detalles de la implementación. Por el contrario, estos dependen de la abstracción.

Esto conforma el Principio de Inversión de Dependencias (DIP)

Tracer <<interface>>TracerOut

FileOut StdOut SerialOut SocketOut ...

Client

Page 71: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Vinculando la implementación

con la abstracción

71

Establecida la abstracción, resta vincularla con las

implementaciones. En lenguaje C, podemos utilizar varios

métodos para lograrlo, básicamente:

Vinculación estática

Se produce al momento de compilar y/o linkear

Mediante el preprocesador o mediante el linker

Vinculación dinámica

Se efectúa en momento de ejecución

Mediante punteros a función

La elección de uno u otro depende de la relación de compromiso entre sus ventajas y desventajas para una

aplicación determinada.

Page 72: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Vinculación preprocesador72

La solución más básica

consiste en una macro

que provea la

abstracción.

Entre otras cuestiones, todo cambio en la

implementación de la

abstracción provoca

cambios en el código

que utiliza la

abstracción.

Page 73: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Vinculación preprocesador73

Si afrontamos un problema de incompatibilidad entre la interfaz que necesita el cliente y la provista por el servidor, necesitamos definir un adaptador que interceda entre el cliente y el servidor.

Difícil determinar que código se compila realmente para diferentes situaciones (compilación condicional).

Page 74: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Vinculación por linker74

TraceOut.h define la abstracción

Cada implementación se define en su propio archivo, donde se respeta la firma de la operación definida en la abstracción.

Se elije para linkear el archivo de la implementación requerida.

Un cambio en la implementación no provoca un cambio en el código que utiliza la abstracción.

Page 75: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Vinculación dinámica

Singleton

75

La implementación se

vincula en tiempo de

ejecución.

El módulo TraceOut

administra una única

instancia (singleton).

Permite obtener un único

binario ejecutable para

todas las implementaciones.

Page 76: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Vinculación dinámica

Singleton

76

Cada implementación

se define en un archivo

único.

Page 77: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Vinculación dinámica

Instancia múltiple

77

El módulo TraceOut

administra múltiples

instancias de un mismo tipo

(clase derivada),

identificadas por el parámetro me.

Page 78: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Vinculación dinámica

Instancia múltiple

78

A través de me, cada

operación identifica la

instancia sobre la cual

opera.

Cada módulo se crea a partir de TraceOut

mediante la herencia

simple.

Dentro de cada operación

se realiza un downcast

para acceder a los datos de la clase derivada.

Page 79: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Principios S.O.L.I.D

Cada uno de los criterios y métodos presentados se basan fundamentalmente en estos principios.

Estos forman parte de la estrategia del desarrollo ágil y adaptativo de software.

79

Inicial Acrónimo Concepto

S SRPPrincipio de responsabilidad única (Single Responsibility Principle). Una clase debería tener

una única razón para cambiar.

O OCPPrincipio de abierto/cerrado (Open/Closed Principle). Las entidades de software (clases,

módulos, funciones, etc) deben estar abiertas para su extensión, pero cerradas para su

modificación.

L LSPPrincipio de sustitución de Liskov (Liskov Substitution Principle). Los subtipos deben ser

sustituibles por sus tipos bases.

I ISPPrincipio de segregación de la interfaz (Interface Segregation Principle). la noción de que

“muchas interfaces cliente específicas son mejores que una interfaz de propósito general”

D DIPPrincipio de inversión de la dependencia (Dependency Inversion Principle). Las

abstracciones no deberían depender de los detalles. Los detalles deberían depender de

las abstracciones.

Page 80: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Conclusiones

La aplicación de los principios del OOD, como el

encapsulamiento, la abstracción, la herencia, y el

polimorfismo, como fundamentos del diseño de embedded

software en C, no es una imitación caprichosa de lenguajes

orientados a objetos, sino más bien es la aplicación de

ideas y conceptos maduros y formalizados, que bien

empleados y en su “justa medida” permiten no sólo

aumentar la productividad sino también la calidad de

nuestros desarrollos.

Y así lograr software más flexible, fácil de mantener y

reutilizable.

80

Page 81: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Preguntas81

Page 82: Principios del Diseño Orientado a Objetos (OOD) Aplicados en C

Referencias

[1] Leandro Francucci, “Principios de OOP aplicados en C para Embedded Systems”, Febrary, 2014, embedded-exploited.com.ar

[2] Leandro Francucci, “Modularidad, abstracción y múltiples instancias en C para Embedded Software”, April, 2014, embedded-exploited.com.ar

[3] Dan Saks, "Alternatives Idioms for Inheritance in C", Embedded.com, April 2, 2013.

[4] Robert C. Martin, “Clean Code”, Prentice Hall, 2009

[5] Robert C. Martin, “Agile Principles Patterns and Practices in C#”, Pearson Education, 20 jul. 2006

[6] James W. Grenning, “Test Driven Development for Embedded C”, Pragmatic Bookshelf, 2011

[7] Kernighan & Ritchie, "C Programming Language (2nd Edition)", April 1, 1988.

82