Esta noche anterior, leía el último artículo de Nico Aragon que publicaba en su bitácora:
El cortador de lineas
y tras analizar el código, le hice algunos comentarios que me parecían de interés. De hecho, he leido su respuesta y creo que estamos básicamente de acuerdo en lo de objetos «inteligentes o tontos», o como el llama «activos o pasivos». Lo de menos es quizás el nombre sino el concepto que queda en el transfondo.
Acabo de modificar un poquito su código para que si algún compañero no lo ve, pueda apreciar la distinción a la que me refiero.
En el caso de esta unidad, la invocación del proceso cortador de lineas se haría mas o menos así, con la modificación o variación propuesta:
Cutter := TLineCutter.Create(//aquí el parámetro de tipo string); try Cutter.OnNewLine := AddLine; Cutter.Start; finally Cutter.Free; end;
La diferencia entre un objeto u otro, es simplemente sobre quien recae la responsabilidad del analisis y de determinar cuando ha finalizado. Relacionado con este punto, he suprimido el estado de finalización por una función de control Eof, que le indica al objeto cuando ha finalizado el análisis.
Realmente hacen los dos objetos, el de Nico, o esta variación exactamente lo mismo, y responden los dos al mismo diagrama de estados que planteó como fondo de su propuesta.
Lo dificil en todos estos casos, no es plantear modificaciones, sino esa primera fase de análisis del algoritmo. A posteriori, siempre es relativamente fácil encontrar optimizaciones sobre algo que ya tienes planteado previamente. Y en ese sentido, le doy toda la razón a Nico cuando explica en las primeras lineas de su artículo, la necesidad de apoyarnos en razonamientos formales, como pueda ser un diagrama de estados para la implementación de un algoritmo determinado.
//variacion de la unidad LineCutters //******************************
unit LineCutters;
interface
type
TOnNewLineEvent = procedure(const Line: string) of object;
TNotifyEvent = procedure(Sender: TObject) of object;
TLineCutterState = (stText, stCR, stCRLF, stLF);
TLineCutter = class
private
Buffer: String;
FIndex: Integer;
FActive: Boolean;
FOnFinish: TNotifyEvent;
FOnNewLine: TOnNewLineEvent;
protected
FTexto: string;
State: TLineCutterState;
procedure Process(const c: Char);
procedure ReportLine;
procedure DoProcess; virtual;
procedure DoFinish; virtual;
function EOF: Boolean;
public
constructor Create(const ACadena: String); virtual;
procedure Start;
property Active: Boolean read FActive;
property OnNewLine: TOnNewLineEvent read FOnNewLine write FOnNewLine;
property OnFinish: TNotifyEvent read FOnFinish write FOnFinish;
end;
implementation
uses SysUtils;
{ TLineCutter }
constructor TLineCutter.Create(const ACadena: String);
begin
inherited Create;
FActive := False;
FTexto := ACadena;
State := stText;
end;
function TLineCutter.EOF: Boolean;
begin
Result:= FIndex > Length(FTexto);
end;
procedure TLineCutter.Process(const c: Char);
begin
case State of
stText: begin
if c = #13 then begin
ReportLine;
State := stCR;
end else if c = #10 then begin
ReportLine;
State := stLF;
end else begin
Buffer := Buffer + c;
end;
end;
stCr: begin
if c = #13 then begin
ReportLine;
end else if c = #10 then begin
State := stCRLF;
end else begin
Buffer := Buffer + c;
State := stText;
end;
end;
stLf: begin
if c = #13 then begin
ReportLine;
State := stCR;
end else if c = #10 then begin
ReportLine;
State := stLF;
end else begin
Buffer := Buffer + c;
State := stText;
end;
end;
stCRLF: begin
if c = #13 then begin
ReportLine;
State := stCR;
end else if c = #10 then begin
ReportLine;
State := stLF;
end else begin
Buffer := Buffer + c;
State := stText;
end;
end;
end;
end;
procedure TLineCutter.DoProcess;
begin
//prevenimos que no se pueda invocar si está en proceso
FActive:= True;
FIndex:= 1;
Buffer:= '';
while not Eof do begin
Process(FTexto[FIndex]);
Inc(FIndex);
end;
if Buffer <> '' then ReportLine;
//marcamos el final del algoritmo
FActive:= False;
end;
procedure TLineCutter.ReportLine;
begin
if Assigned(FOnNewLine) then FOnNewLine(Buffer);
Buffer := '';
end;
procedure TLineCutter.DoFinish;
begin
if Assigned(FOnFinish) then FOnFinish(self);
end;
procedure TLineCutter.Start;
begin
if FActive then Raise Exception.Create('TLineCutter: In process');
DoProcess;
DoFinish; //comunicamos al usuario que el algoritmo finalizó
end;
end.
Comentarios recientes