Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   OOP (https://www.clubdelphi.com/foros/forumdisplay.php?f=5)
-   -   Patrón (https://www.clubdelphi.com/foros/showthread.php?t=54372)

harpo 16-03-2008 23:34:14

Patrón
 
Buenas gente,

a ver si me podeis echar una manita con el rediseño de un programa.

Tengo un form principal, una serie de frames y una máquina de estados para controlar la visualización de los frames, entre otras cosas. Hasta ahora el diseño era un poco chapuza, el form principal contiene las referencias a todos los frames y la creación de los estados de la máquina. A parte, cada frame contiene referencias a otros frames "hermanos" que necesita y referencias al frame principal, con lo cual tengo un lío tremendo de referencias circulares.

Algunas partes del código

Form principal
Código:

TfrMain = class(TForm)
    frToolbar: TfrToolbar;
    procedure mmExitClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    frNavBarLeft    :TfrNavBarLeft;
    frNavBarRight  :TfrNavBarRight;
    frEditPatient  :TfrEditPatient;
    frViewPatient  :TfrViewPatient;
    frBlank        :TfrBlank;
    frViewImage    :TfrViewImage;
    frViewStudy    :TfrVIewStudy;
    frViewImageTwin :TfrViewImageTwin;
    frCamera        :TfrCamera;
    frEditStudy    :TfrEditStudy;
    frPrint        :TfrPrint;
    frProcesses    :TfrProcesses;
  end;


var
  frMain: TfrMain;

implementation

{$R *.dfm}

procedure TfrMain.mmExitClick(Sender: TObject);
begin
  frMain.Close;
end;

procedure TfrMain.FormCreate(Sender: TObject);
begin


  frBlank        :=TfrBlank.Create(Self);
  frNavBarLeft    :=TfrNavBarLeft.Create(Self);
  frViewPatient  :=TfrViewPatient.Create(self);
  frEditPatient  :=TfrEditPatient.Create(Self);
  frViewImage    :=TfrViewImage.Create(Self);
  frViewStudy    :=TfrViewStudy.Create(Self);
  frViewImageTwin :=TfrViewImageTwin.Create(Self);
  frCamera        :=TfrCamera.Create(Self);
  frEditStudy    :=TfrEditStudy.Create(Self);
  frPrint        :=TfrPrint.Create(Self);
  frProcesses    :=TfrProcesses.Create(Self);

  frBlank.Parent          :=Self;
  frNavBarLeft.Parent    :=Self;
  frViewPatient.Parent    :=Self;
  frEditPatient.Parent    :=Self;
  frViewImage.Parent      :=Self;
  frViewStudy.Parent      :=Self;
  frViewImageTwin.Parent  :=Self;
  frCamera.Parent        :=Self;
  frEditStudy.Parent      :=Self;
  frPrint.Parent          :=Self;
  frProcesses.Parent      :=Self;

  sessiondate:=now;
  states:=TStateMachine.Create;

  frNavBarLeft.ReLoadTreeView;
  frMain.TabStop:=false;

  states.Add (
                stBlank,
                frBlank.frEnter,
                frBlank.frExit,
                frBlank,
                true,
                frToolBar.menu
              );

  states.Add (
                stViewPatient,
                frViewPatient.frEnter,
                frViewPatient.frExit,
                frViewPatient,
                true,
                frToolBar.menu
              );

 // y bla bla bla... demás estados ....

  states.Enter(stBlank);
end;

Máquina Estados (parte de interés)
Código:

  TProc = procedure of object;
  {
    TState:
    state type for the State Machine
  }
  TState = record
    id:integer;  //id state (for ex. stBlank, stViewPatient)
    inProc:TProc;    //initial process
    outProc:TProc;    //fianl process
    frame:TFrame; //frame corresponding for this state
    navBarL:Boolean; //bars must be shown
    menu:TMainMenu; //main menu for this frame (use to be main menu of the
                    //toolbar frame
  end;

  {
    TStateMachine:
    Controls the state of the program and manage the interface active frame
  }
  TStateMachine = class
  private
    states:array of TState;    //posible states         
    procedure stExit(id:integer);
  public
    current:integer; // current state
    last:integer;    //last state (for goback function)
    OnRecordChange:TNotifyEvent;
    stack:TStack;
    procedure Add(id:integer;ip,op:TProc;frame:TFrame;nbl:boolean;menu:TMainMenu);    procedure SetInProc(proc:TProc;state:integer);
    procedure Enter(id:integer);

    constructor Create;
    destructor Destroy; override;

var
  states      :TStateMachine;
  sessionDate :TDateTime;

implementation


procedure TStateMachine.Add(id:integer;ip,op:TProc;frame:TFrame;
                            nbl:boolean;menu:TMainMenu);
var
  l :integer;
begin
  l := Length(states);
  SetLength(states,l+1);
  states[l].id := id;
  states[l].inProc := ip;
  states[l].outProc := op;
  states[l].frame := frame;
  states[l].menu := menu;
  states[l].navBarL := nbl;
end;

procedure TStateMachine.Enter(id: integer);
var
  i,j:integer;
begin
  for i := 0 to length(states)-1 do
    if id = states[i].id then
    begin
      stExit(current);
      current := id;
      frMain.frNavBarLeft.Visible := states[i].navBarL;
      frMain.Menu := states[i].menu;
      with states[i].frame do
      begin
        Align := alClient;
        Visible := True;
      end;
      states[i].inProc;
    end;

end;

procedure TStateMachine.stExit(id:integer);
var
  i :integer;
begin
  for i := 0 to length(states)-1 do
  if id=states[i].id then
  begin
    last := current;
    current := -1;
    states[i].outProc;
    states[i].frame.Align := alNone;
    states[i].frame.Visible := False;
  end;
end;

Pues bien, ahora la idea es hacer un diseño más correcto, algo de este tipo:



Y un pequeño resumen, de cómo considero que son/deberían ser las cosas, pero que no consigo ver claro cómo hacer:
  • Un frame representa un "estado" del programa claramente definido.
  • Las acciones del usuario cambian el estado del programa, por lo tanto debe haber un sistema para cambiar entre los estados correspondientes.
  • Cada frame debe ser independiente y no debe incluir units de otros frames "hermanos".
  • Los frames NO deben referenciar al formulario principal directamente, ni otros miembros del mismo.
  • Para los elementos de la pantalla que si o si deben compartirse entre frames, como los botones, o que deben ser persistentes, como los thumbnails, debe haber un sistema que publique globalmente los diferentes bloques (botonera, thumbnails), para que cada frame pueda obtener y manejar los bloques que necesita y conozca, mientras el resto de los bloques permanece desactivado.
  • Debe haber un sistema para transferir datos entre los distintos frames durante un cambio de estado, ya sean imagenes, claves de la DB, u otros parametros.
  • El uso y la asociacion de los frames a los estados, debe ser lo mas transparente posible ( o sea, no tener que agregar el frame en 50 lugares diferentes)
  • Cada frame o estado deberia tener un procedimiento de entrada y de salida, en el que recibe el estado desde o hacia el cual se transfiere, junto con parametros opcionales. Este procedimiento debe ser privado, y no utilizado afuera del frame en cuestion.
  • Los parametros utilizados para la transferencia de datos deben estar especificados en una estructura estandar, como podria ser una lista de nombres y valores.
Ahora las preguntas...
¿Qué patrones de diseño veis aquí?
¿Singleton para la máquina de estados?
¿Alguna manera de evitar el rollo de las referencias circulares... sin hacer el chapuceo de los uses en la implementación?

(Perdón por el rollo :P )

Delphius 16-03-2008 23:51:49

Cita:

Empezado por harpo (Mensaje 273669)
Buenas gente,

a ver si me podeis echar una manita con el rediseño de un programa.

Tengo un form principal, una serie de frames y una máquina de estados para controlar la visualización de los frames, entre otras cosas. Hasta ahora el diseño era un poco chapuza, el form principal contiene las referencias a todos los frames y la creación de los estados de la máquina. A parte, cada frame contiene referencias a otros frames "hermanos" que necesita y referencias al frame principal, con lo cual tengo un lío tremendo de referencias circulares.

Pues bien, ahora la idea es hacer un diseño más correcto, algo de este tipo:



Y un pequeño resumen, de cómo considero que son/deberían ser las cosas, pero que no consigo ver claro cómo hacer:
  • Un frame representa un "estado" del programa claramente definido.
  • Las acciones del usuario cambian el estado del programa, por lo tanto debe haber un sistema para cambiar entre los estados correspondientes.
  • Cada frame debe ser independiente y no debe incluir units de otros frames "hermanos".
  • Los frames NO deben referenciar al formulario principal directamente, ni otros miembros del mismo.
  • Para los elementos de la pantalla que si o si deben compartirse entre frames, como los botones, o que deben ser persistentes, como los thumbnails, debe haber un sistema que publique globalmente los diferentes bloques (botonera, thumbnails), para que cada frame pueda obtener y manejar los bloques que necesita y conozca, mientras el resto de los bloques permanece desactivado.
  • Debe haber un sistema para transferir datos entre los distintos frames durante un cambio de estado, ya sean imagenes, claves de la DB, u otros parametros.
  • El uso y la asociacion de los frames a los estados, debe ser lo mas transparente posible ( o sea, no tener que agregar el frame en 50 lugares diferentes)
  • Cada frame o estado deberia tener un procedimiento de entrada y de salida, en el que recibe el estado desde o hacia el cual se transfiere, junto con parametros opcionales. Este procedimiento debe ser privado, y no utilizado afuera del frame en cuestion.
  • Los parametros utilizados para la transferencia de datos deben estar especificados en una estructura estandar, como podria ser una lista de nombres y valores.
Ahora las preguntas...
¿Qué patrones de diseño veis aquí?
¿Singleton para la máquina de estados?
¿Alguna manera de evitar el rollo de las referencias circulares... sin hacer el chapuceo de los uses en la implementación?

(Perdón por el rollo :P )

Bueno, una lecturá rápida me dio una interpretación de esto:

1. Existe una única manquina de estados, por lo que estamos hablando de un singleton.
2. Cada estado es en cierta medida independiente pero las acciones del usuario conducen a que se provoquen ciertos cambios y en la elección de los estados. Eso es lo que entiendo:confused:. A simple vista pareciera que estamos en una combinación de estrategia (Strategy) y Composite.

3. Los frames son indenpendientes pero son el reflejo de los estados.:confused: Um.... pareciera ser algo como el Patrón Observador/Observado pues, si los frames representan "visualmente" a los estados y estos dependerán de un curso de acción... y si se desea una cierta independencia entre la lógica de estados y los frames lo más "simple" es tener un modelo de Observador (Observer).

Bueno... eso fue un primer análisis de lo que voy entiendo acerca de lo que deseas hacer. Muy posiblemente esté equivocado pero al menos ya tienes un punto de vista.

Por otro lado recuerda que no existe una única alternativa, esto de los patrones, UML y POO es bastante subjetivo y pueden existir tantos diseños como personas en el mundo.

Saludos,


La franja horaria es GMT +2. Ahora son las 20:44:30.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi