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.