Ir al contenido


Foto

¿Cuándo y porqué emplear subprocedimientos/subfunciones?


  • Por favor identifícate para responder
36 respuestas en este tema

#1 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 18 marzo 2010 - 12:32

Hola a todos,
Mi duda, quizá como la gran mayoría de todas las que hice, es un tanto filosófica.

Me estaba preguntando que diferencia hay entre hacer ésto:



delphi
  1. procedure ProcedimientoA;
  2.   procedure SubProcedimientoB;
  3.   begin
  4.   ....
  5.   end;
  6. ....
  7. begin
  8. ...
  9. // llamamos a SubProcedimientoB
  10. SubProcedimientoB;
  11. ...
  12. end;



Y esto otro:



delphi
  1. procedure ProcedimientoA;
  2. begin
  3. ...
  4. // llamamos a SubProcedimientoB
  5. SubProcedimientoB;
  6. ...
  7. end;
  8.  
  9. procedure SubProcedimientoB;
  10. begin
  11. ...
  12. end;



Es bien conocido que en la primera opción el subprocedimiento sólo es visible desde el procedimiento padre y por tanto es una buena opción cuando queremos "ocultar" parte de un proceso a los demás.

Pero por el otro lado esto no aporta demasiada claridad puesto que se pierde de vista cuando comienza y termina los subprocedimientos (sobre todo cuando son muchos y largos).

En la segunda alternativa se consigue una mejor claridad y se gana cierta independencia. Ahora bien, si se desea ocultar a este subprocedimiento nos queda la opción de sólo declararlo en la sección implementation y no darle alcance público, pero claro eso no quita que internamente dentro de la unidad se tenga alcance.

Por ello yo me pregunto, fuera de los pros y contras que he enumerado (tratando de ser lo más breve y simple posible), ¿que otras ventajas o diferencias hay entre estas opciones? ¿Afecta en algo el rendimiento de una u otra alternativa?

Podríamos discutir el tema basándonos en una muestra... Un ejemplo "claro" lo podemos ver en el método ParseSQL de la clase TParams:



delphi
  1. function TParams.ParseSQL(SQL: String; DoCreate: Boolean): String;
  2. const
  3.   Literals = ['''', '"', '`'];
  4. var
  5.   Value, CurPos, StartPos: PChar;
  6.   CurChar: Char;
  7.   Literal: Boolean;
  8.   EmbeddedLiteral: Boolean;
  9.   Name: string;
  10.  
  11.   function NameDelimiter: Boolean;
  12.   begin
  13.     Result := CurChar in [' ', ',', ';', ')', #13, #10];
  14.   end;
  15.  
  16.   function IsLiteral: Boolean;
  17.   begin
  18.     Result := CurChar in Literals;
  19.   end;
  20.  
  21.   function StripLiterals(Buffer: PChar): string;
  22.   var
  23.     Len: Word;
  24.     TempBuf: PChar;
  25.  
  26.     procedure StripChar;
  27.     begin
  28.       if TempBuf^ in Literals then
  29.         StrMove(TempBuf, TempBuf + 1, Len - 1);
  30.       if TempBuf[StrLen(TempBuf) - 1] in Literals then
  31.         TempBuf[StrLen(TempBuf) - 1] := #0;
  32.     end;
  33.  
  34.   begin
  35.     Len := StrLen(Buffer) + 1;
  36.     TempBuf := AllocMem(Len);
  37.     try
  38.       StrCopy(TempBuf, Buffer);
  39.       StripChar;
  40.       Result := StrPas(TempBuf);
  41.     finally
  42.       FreeMem(TempBuf, Len);
  43.     end;
  44.   end;
  45.  
  46. begin
  47.   Result := SQL;
  48.   Value := PChar(Result);
  49.   if DoCreate then Clear;
  50.   CurPos := Value;
  51.   Literal := False;
  52.   EmbeddedLiteral := False;
  53.   repeat
  54.     while (CurPos^ in LeadBytes) do Inc(CurPos, 2);
  55.     CurChar := CurPos^;
  56.     if (CurChar = ':') and not Literal and ((CurPos + 1)^ <> ':') then
  57.     begin
  58.       StartPos := CurPos;
  59.       while (CurChar <> #0) and (Literal or not NameDelimiter) do
  60.       begin
  61.         Inc(CurPos);
  62.         while (CurPos^ in LeadBytes) do Inc(CurPos, 2);
  63.         CurChar := CurPos^;
  64.         if IsLiteral then
  65.         begin
  66.           Literal := Literal xor True;
  67.           if CurPos = StartPos + 1 then EmbeddedLiteral := True;
  68.         end;
  69.       end;
  70.       CurPos^ := #0;
  71.       if EmbeddedLiteral then
  72.       begin
  73.         Name := StripLiterals(StartPos + 1);
  74.         EmbeddedLiteral := False;
  75.       end
  76.       else Name := StrPas(StartPos + 1);
  77.       if DoCreate then
  78.         TParam(Add).Name := Name;
  79.       CurPos^ := CurChar;
  80.       StartPos^ := '?';
  81.       Inc(StartPos);
  82.       StrMove(StartPos, CurPos, StrLen(CurPos) + 1);
  83.       CurPos := StartPos;
  84.     end
  85.     else if (CurChar = ':') and not Literal and ((CurPos + 1)^ = ':') then
  86.       StrMove(CurPos, CurPos + 1, StrLen(CurPos) + 1)
  87.     else if IsLiteral then Literal := Literal xor True;
  88.     Inc(CurPos);
  89.   until CurChar = #0;
  90. end;



¿No creen que las subfunciones estarían mejor como métodos privados fuera de este método?

¿Que opinan?

Saludos,
  • 0

#2 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.446 mensajes
  • LocationMéxico

Escrito 18 marzo 2010 - 01:23

Hola

Desde que tengo uso de razón he utilizado las dos formas, sin embargo, creo que si un procedimiento o función sólo es requerida por un procedimiento específico no veo la necesidad de decararlo fuera de él, incluso me parece más práctico que esté dentro del procedimiento "padre" por cuestiones de identificación.

Yo creo que también es cuestión de estilo ;)

Salud OS


  • 0

#3 kafastoforman

kafastoforman

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 277 mensajes
  • LocationMexico D.F.

Escrito 18 marzo 2010 - 01:42

Lo que yo creo es que cuando se genera una subfuncion, esta aparta un espacio en memoria hasta que es llamada la función padre y el espacio de memoria es liberado en cuanto la funcion padre termina. Entonces si solo se ocupa una vez y solo para una función, no tiene sentido alojar en memoria una funcion que ya no va a ser ocupada. SUPONGO, por que la verdad no se jejejejeje

:cool:

Saludos

Kafastoforman
  • 0

#4 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 18 marzo 2010 - 03:06

Opino como delphius. En C/C++ no existen, sencillamente no son necesarias y alteran la lectura del código. Si queremos encapsular una función, para eso están las clases.

Bien es verdad que puede resultar cómodo su uso en ciertas ocasiones y según el estilo de cada uno, como apunta egostar.

Lo que yo creo es que cuando se genera una subfuncion, esta aparta un espacio en memoria hasta que es llamada la función padre y el espacio de memoria es liberado en cuanto la funcion padre termina.


Pues no estoy de acuerdo. Cada parte de un proceso está muy bien identificado en la estructura del archivo ejecutable PE. Existe código, datos y recursos. ¿Una subfunción que no ocupe lugar? Su código existe (opcodes) como una llamada mas, ni aún utilizando la volatilidad de la pila dejaría de ocupar (por cierto, que peligroso usar la pila como espacio de código). Y filosóficamente como diría delphius la función padre puede ser llamada cuantas veces se quiere, y esta llamará a sus hijas.

En definitiva y desde mi punto de vista, casi es mas cuestión de gustos.

Saludos.
  • 0

#5 kafastoforman

kafastoforman

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 277 mensajes
  • LocationMexico D.F.

Escrito 18 marzo 2010 - 03:12

Opino como delphius. En C/C++ no existen, sencillamente no son necesarias y alteran la lectura del código. Si queremos encapsular una función, para eso están las clases.

Bien es verdad que puede resultar cómodo su uso en ciertas ocasiones y según el estilo de cada uno, como apunta egostar.


Lo que yo creo es que cuando se genera una subfuncion, esta aparta un espacio en memoria hasta que es llamada la función padre y el espacio de memoria es liberado en cuanto la funcion padre termina.


Pues no estoy de acuerdo. Cada parte de un proceso está muy bien identificado en la estructura del archivo ejecutable PE. Existe código, datos y recursos. ¿Una subfunción que no ocupe lugar? Su código existe (opcodes) como una llamada mas, ni aún utilizando la volatilidad de la pila dejaría de ocupar (por cierto, que peligroso usar la pila como espacio de código). Y filosóficamente como diría delphius la función padre puede ser llamada cuantas veces se quiere, y esta llamará a sus hijas.

En definitiva y desde mi punto de vista, casi es mas cuestión de gustos.

Saludos.


Gracias escafandra, eso no lo sabia ;) por eso dije creo  :D :D :D

Saludos

Kafastoforman
  • 0

#6 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.446 mensajes
  • LocationMéxico

Escrito 18 marzo 2010 - 03:22

Pues si, en realidad yo utilizo "subprocesos" solo para optimizar lineas de código y si un bloque solo es llamado dentro de un procedimiento específico lo dejo dentro, no creo que valga la pena hacer mas complicaciones, pero bueno, ese es mi estilo :) y nada mas.

Además y sin ánimo de debate :D, si Dios lo sabe que lo sepa todo el mundo....  ahh no perdón, si Delphi lo permite, pues habrá que usarlo :p

Salud OS
  • 0

#7 pcicom

pcicom

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 267 mensajes
  • LocationMéxico

Escrito 18 marzo 2010 - 03:55

Desde mi punto de vista le quita facilidad de entendimiento..

En mi caso aunque solo sea para uso de un procedimiento la declaro fuera... 
y para el caso de que sea usada por otros procesos la meto en una unit de librerias, que se agregaria
a la unidad que la requiriera...

Creo que es de gustos...  porque en algunos casos de procedimientos que he leido en donde son usadas
de esa forma, en ocaciones se pierde legibilidad al codigo...



SALUDOS..




  • 0

#8 JoAnCa

JoAnCa

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 775 mensajes
  • LocationPinar del Río, Cuba

Escrito 18 marzo 2010 - 03:58

Hola

Desde que tengo uso de razón he utilizado las dos formas, sin embargo, creo que si un procedimiento o función sólo es requerida por un procedimiento específico no veo la necesidad de decararlo fuera de él, incluso me parece más práctico que esté dentro del procedimiento "padre" por cuestiones de identificación.

Yo creo que también es cuestión de estilo ;)

Salud OS




Pues yo coincido con egostar, opino lo mismo
  • 0

#9 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 18 marzo 2010 - 05:07

Hola,

Interesante sus opiniones. Les agradezco que se hayan tomado el tiempo de responder.

Como pueden apreciar, el tema es bastante filosófico.
Y como lo resume una frase de Eliseo: DEPENDE, DEPENDE, DEPENDE. :D
E incluso, por gusto como han apuntado varios, por no decir todos.

Si me lo permiten, y si alguien se anima me gustaría que se diera un poquito más de debate. No hay problema con que se discutan y comparen opciones.
Por mi parte, si me esperan yo quisiera exponer algunas observaciones, basadas en el ejemplo que he señalado; desde un plazo quizá algo más técnico.

Espero que eso no les aburra. Mi intención es darle un nuevo giro al tema e inspirarlos a ustedes a que se animen a opinar un poco más en profundidad según sus ópticas.

Saludos,
  • 0

#10 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.446 mensajes
  • LocationMéxico

Escrito 18 marzo 2010 - 05:37

Me parece perfecto, a veces uno se acostumbra tanto a ciertas tendencias que no ve mas allá, así que por mi parte soy todo ojos :D :D :D

Estoy seguro que este hilo puede ser mas que productivo. (y)

Salud OS
  • 0

#11 TiammatMX

TiammatMX

    Advanced Member

  • Miembros
  • PipPipPip
  • 1.750 mensajes
  • LocationUniverso Curvo\Vía Láctea\Sistema Solar\Planeta Tierra\América\México\Ciudad de México\Xochimilco\San Gregorio Atlapulco\Home

Escrito 18 marzo 2010 - 05:43

...Desde que tengo uso de razón...


¿Hace 15 minutos, Egostar? No seas tramposo... jajajajajajajajajaja

Volviendo al tema, he de confesar que abuso mucho de los procedimientos externos, he llegado a generar alguna especie de fobia por las subfunciones..., aunque también, cuando es necesario "esconder" parte de la funcionalidad las utilizo (el DeDe no las detecta, por ejemplo) y a veces es más "elegante" ponerle una subfunción que realice el trabajo pesado.

Una razón más para no usar subfunciones..., tuve un maestro de sistemas que era casi casi el alumno predilecto de Niklaus Wirth y conocía el PASCAL como si él lo hubiese parido. Y nos decía que la recursividad no quiere decir generar código a lo tonto, y que partir en subfunciones lógicamente pensadas hacía de nosotros seres mejores. Y creánme, le doy la razón.
  • 0

#12 pcicom

pcicom

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 267 mensajes
  • LocationMéxico

Escrito 18 marzo 2010 - 07:20

Pues yo las CERVEZAS las prefiero de la HIELERA, y no del REFRIGERADOR !!!1  :cool:
:tongue:    (b)  (b)  (b)  (b)  (b)  (b)  (b)

Cada quien agarre la suya...



  • 0

#13 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 18 marzo 2010 - 07:36

Lamentablemente me van a tener que esperar hasta mañana porque tengo asuntos que atender.
Pensaba que me podría quedar hasta bien tarde realizando mi "exposición" pero recordé que debo levantarme temprano :@
Espero que Dios me dote de poder de síntesis :p :D

Saludos,
  • 0

#14 eduarcol

eduarcol

    Advanced Member

  • Administrador
  • 4.483 mensajes
  • LocationVenezuela

Escrito 19 marzo 2010 - 07:44

Espero que Dios me dote de poder de síntesis :p :D

Saludos,


jajaja todos esperamos que puedas encontrar la iluminación divina, jejeje

Respecto a las subfunciones las utilizo si y solo si esa función o procedimiento no lo voy a utilizar en ninguna otra área del código, se me hace mas fácil luego saber que hay en cada unidad.
  • 0

#15 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 19 marzo 2010 - 07:41

Jeje...
La iluminación divina la encontré :$

Pero parece que lo que me Dios me da por un lado, me quita por el otro :p

Lamento decepcionarlos. Hoy casi no toqué la PC, mañana por fin podré estar tranquilo y exponer mi "análisis"

Saludos,


  • 0

#16 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 20 marzo 2010 - 10:08

Para ilustrar lo que apuntada en mi comentario anterior, he realizado una aplicación simple:


delphi
  1. program Project1;
  2.  
  3. {$APPTYPE CONSOLE}
  4.  
  5. uses
  6.   SysUtils;
  7.  
  8. procedure Una;
  9.   procedure otra;
  10.   begin
  11.   end;
  12. begin
  13.   otra;
  14. end;
  15.  
  16. begin
  17.   una;
  18. end.



Sobre ella he aplicado un debug en asm (Alt F2) y veamos que es lo que pasa:

Tras comenzar el programa nos encontramos con una llamada al procedimiento una:


delphi
  1. call una



Imagen Enviada

Si le seguimos la pista, este llama a la subfunción otra ubicada en la dirección 00407C60:


delphi
  1. call otra



Imagen Enviada

Esto ilustra como una vez compilado y a nivel asm no existe tal subfunción sino que es una función mas "con todas las letras".

Si complicamos el ejemplo usando funciones con variables locales en lugar de procedimientos, ambos, el padre y el hijo reservan su espacio de pila y se comportan como verdaderas funciones autónomas.

La conclusión es que técnicamente no aporta ventaja alguna y que sólo existe la subfunción en alto nivel. Por lo tanto su utilidad viene dada sólo por la organización que cada uno quiera dar al código, sin aumentar ni restar eficacia al mismo.

Saludos.
  • 0

#17 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 20 marzo 2010 - 10:14

excelente explicación escfandra, ahora tengo algo más en claro, muchas gracias (y)
  • 0

#18 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 20 marzo 2010 - 11:24

Gracias escafandra por aportar una visión de menor nivel y más técnica sobre el tema.

Esa es la parte que le hace falta al análisis que he hecho.
Y como dice la frase... más vale tarde que nunca... aquí van mis conclusiones.

Saludos,
  • 0

#19 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 20 marzo 2010 - 11:47

Analizando a ParseSQL

Como ha quedado escrito unos mensajes antes, como ejemplo nos apoyaremos en el método ParseSQL de TParams.

Para hacer corta la historia ParseSQL se encarga de pre-armar la instrucción SQL con los parámetros que hemos introducido. Básicamente localiza los parámetros y los reemplaza por signos de interrogación (?). Tiene la capacidad de reconocer como parámetros a todo literal precedido por dos puntos ( : ), dobles puntos ( :: ), como así también a los que estén entre comillas.
Además se encarga de crear los Param con el nombre igual al literal encontrado.

Nos dedicaremos ahora a analizar el código, que es lo importante.
Si se observa el código con atención podemos distinguir 4 submétodos (vamos a optar por este término a fin de ser coherentes con la terminología OO):

NameDelimiter
IsLiteral
StripLiterals
StripChar

Para ofrecer una visión analítica nos apoyaremos en dos medidas y una métrica. La forma más básica de estudiarlo es calculando:
1. LDC o Líneas De Código. No necesita mayor descripción.
2. V(G) o Complejidad Ciclomática: que determina la complejidad algorítmica e indica la cantidad de caminos posibles que se pueden ejecutar en el código.

La métrica a emplear se conoce como MPC o Método Ponderado de Clase, que determina la complejidad de todos los métodos de una clase. Cuanto más alto es el valor, más compleja resulta ser la clase.

Para mayor información Sobre estas medidas y métricas recomiendo la lectura del libro "Ingeniería de Software, un enfoque práctico" de Robert Pressman.

Los cálculos para estos métodos se encuentran en la tabla 1:

                +-----+------+-------+
                | LDC | V(G) | Prop. |
+---------------+------------+-------+
| NameDelimiter | 1  | 1    | 1    |
+---------------+-----+------+-------+
| IsLiteral    | 1  | 1    | 1    |
+---------------+-----+------+-------+
| StripLiterals | 8  | 1    | 8    |
+---------------+-----+------+-------+
| StripChar    | 4  | 3    | 1,33  |
+---------------+-----+------+-------+
| ParseSQL      | 38  | 18  | 2,1  |
+---------------+-----+------+-------+


Se añadió a la lista a ParseSQL. La Columna etiquetada como Prop. es la proporción o división entre LDC y V(G).

Continuemos.
  • 0

#20 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.446 mensajes
  • LocationMéxico

Escrito 20 marzo 2010 - 11:53

Tendré que esperar a la siguiente parte :p. Pero puedo adelantar que esto quedaría muy bien como un excelente manual. ;)

Salud OS
  • 0




IP.Board spam blocked by CleanTalk.