Ver Mensaje Individual
  #4  
Antiguo 27-05-2015
Avatar de mamcx
mamcx mamcx is offline
Moderador
 
Registrado: sep 2004
Ubicación: Medellín - Colombia
Posts: 3.941
Reputación: 27
mamcx Tiene un aura espectacularmamcx Tiene un aura espectacularmamcx Tiene un aura espectacular
He estado aprendiendo mucho últimamente sobre programación funcional, en especial con F#.

Este articulo lo recomiendo muchísimo:

http://fsharpforfunandprofit.com/fppatterns/

Pongo esto porque conceptualmente aclara muchas cosas, lo que es mas "difícil" es aplicarlo a un lenguaje OO. Afortunadamente, Delphi es lo suficiente flexible y resulta mas simple que con Java!

Ahora voy a varios puntos:
---

Cita:
he notado que todos los códigos de ejemplo no protegen la creación de
Lo cual es correcto, tanto conceptual como técnicamente. En vez de irme por las ramas, un concepto muy practico que viene del lenguaje funcional Erlang es:

"Fallar de inmediato"
https://www.sics.se/~joe/thesis/arms...hesis_2003.pdf

(Secciones 4.3 / 4.4).

En resumen? La forma correcta de hacer programas robustos es no hacer programación "primariamente" defensiva, sino permitir fallar rápido, y tener un "supervisor" que se encargue de aplicar una política de recuperación.

Cita:
Error handling in Erlang is radically diferent to error handing in most
other programming languages. The Erlang philosophy for handling errors
can be expressed in a number of slogans:

• Let some other process do the error recovery.
• If you can’t do what you want to do, die.
• Let it crash.
• Do not program defensively.
Que es casi exactamente lo que sucede aquí. Si la creación del stream fracasa, NO TIENE SENTIDO SEGUIR ADELANTE. Es responsabilidad de su "supervisor" (ie: Quien llamo al método donde se intento crear el stream) determinar que hacer (reintentar? Abortar todo el sistema? Notificar y esperar un tiempo? etc).

Esto significa que solo te debes preocupar por resolver el problema a mano, no todos los posibles. Intentar aplicar una programación excesivamente defensiva es un anti-patron, que lleva a tonterías como:

Código Delphi [-]
try
//Alguna cosa
except:
  //No hacer nada o suponer que el error fue de permisos insuficientes!
end;

Es curioso, porque el uso de excepciones es una forma simplista de aplicar lo que dicen en Erlang, el problema es que la gente no le pone cuidado a lo que lees. Como es? "EXCEPCIONES", solo, "EXCEPCIONAL!". Una vez manejado (si acaso) lo excepcional, debe andar en la "ruta feliz" donde se asume que todo ira bien.

Te preguntaras porque arranco por aquí. Es simplemente porque quiero que solo te preocupes por "resolver el problema que tienes a mano", lo cual, lleva a hacer funciones/métodos cortos y faciles de testear.

Esa es la tesis del creado de Erlang. Y Erlang es famoso por ser el lenguaje/runtime mas robusto de todos, asi que tienen de donde saber porque es mejor asi

--

La segunda tesis importante que se aprende con programación funcional, es que el "manejo de estado" es una de las principales razones de la complejidad no esencial en el desarrollo de software. Eso se nota en tu código con el asunto de la variable ConvE.

La pregunta es: Como hago el código, que siga el modelo de la computacion I/O: Entrada -> Proceso -> Salida, con el minimo de dependencias y estado no esencial?

---

Ahora como lo aplicaria yo (no testee el codigo, solo lo reformatee), sin desviarme mucho del estilo que tienes:

Código Delphi [-]

function LoadFile(FileName: string):TFileStream;
begin
    Result := FFile.Create(FileName, fmOpenRead or fmShareDenyWrite);
    //FInUse := true; Redundante: El OS ya hace esto
    //Hasta
    FFile.ReadBuffer(Fmt.IDEnd, SizeOf(Fmt.IDEnd)); // ID.End
end;

procedure ReadMatrix(AMatrix: TAMatrix; FFile: TFileStream; RM, CM:Int);
begin
    // Mover los problemas mas cerca de donde se detectan!
    if not(ISValidFormat(Fmt, Header)) then 
    begin
        raise EFileOperation.Create('Invalid matrix file format');
    end;

    if not((Header.Rows = RM) AND (Header.Cols = CM))then
    begin
        raise EInconsistentArray.Create('The dimensions of the matrix and file data do not match', itDimNotMatch);
    end;

    //Ahora vamos por el "Happy Path!"
    // Leemos data
    if Header.Orientation = aoCol
     then begin
            // ... por columnas
            for j := 0 to CM - 1 do
              for i := 0 to RM - 1 do
                FFile.ReadBuffer(AMatrix[i, j], SizeOf(TYPEDATA));
          end
     else begin
            // ... por filas
            for i := 0 to RM - 1 do
              for j := 0 to CM - 1 do
                FFile.ReadBuffer(AMatrix[i, j], SizeOf(TYPEDATA));
          end;
    end    
end;

procedure TArrayConverter.LoadMatrix(AMatrix: TAMatrix; FileName: string);
var Header: TMatrixHeader;
    Fmt: TFileFormat;
    i, j, RM, CM, Err, ConvE: integer;
    Can: boolean;
begin
    Can := CheckMatrix(AMatrix, RM, CM, Err);
    if not(Can AND (Err = OPERATION_DONE)) then 
    begin
        EInconsistentArray.Create('Check of matrix failed', itCheck)
    end;

    FFile = LoadFile(FileName);
    try
        ReadMatrix(AMatrix, FFile, RM, CM);
    finally
       FFile.Free;
    end;
end;

Podras notar varias cosas:

1- Eliminar el nesting hacer mucho mas legible el código, mas de 3 niveles de identacion suele ser una mala señal

2- Poner cerca la detección de los problemas, hace mas claro el codigo (y concuerda con "fallar rápido" ie: poner primero fallar, luego el "happy path")

3- Es mejor programar con el "happy path" o la ruta feliz: Una vez que estoy en la ruta feliz, se asume que todo debe funcionar ok, a menos que algo realmente inesperado suceda (como quedarse sin memoria) donde la única opción sana, es matar el programa.

4- El uso de "FInUse" no le veo sentido: Si lo que quieres es decir que el archivo esta en uso, no es tu codigo, sino el OS, quien realmente sabe si es verdad y quien es el responsable.

Si lo que quieres es evitar que se carguen 2 veces el archivo, esa no es la manera robusta.

5- Separar en funciones es algo que me gusta porque simplifica la lectura, pero en este caso con la reduccion de la identacion y mover la deteccion quedaria igual de chulo.


Aun mas que se puede hacer, pero no quise hacer un cambio muy radical, y como hace un rato que estoy oxidado con Delphi y estoy muy metido con otros lenguajes no quise hacer algo muy alienigena
__________________
El malabarista.
Responder Con Cita