2009-08-18 18 views
10

Responderé a esta pregunta yo mismo, pero no dude en darme su respuesta si es más rápido que yo o si no le gusta mi solución. Acabo de presentar esta idea y me gustaría tener algunas opiniones al respecto.¿Cuál es la mejor manera de serializar la configuración de la aplicación Delphi?

Objetivo: una clase de configuración que se puede leer (como un archivo INI), pero sin tener que escribir (y adaptarse después de un nuevo elemento de configuración se ha añadido) la carga y guardar métodos.

que quieren crear una clase como

TMyConfiguration = class (TConfiguration) 
    ... 
    property ShowFlags : Boolean read FShowFlags write FShowFlags; 
    property NumFlags : Integer read FNumFlags write FNumFlags; 
end; 

Calling TMyConfiguration.Save (heredado de TConfiguration) debe crear un archivo como

[Options] 
ShowFlags=1 
NumFlags=42 

Pregunta: Cuál es la mejor manera de hacerlo ¿esta?

Respuesta

7

Esta es mi solución propuesta.

que tienen una clase base

TConfiguration = class 
protected 
    type 
    TCustomSaveMethod = function (Self : TObject; P : Pointer) : String; 
    TCustomLoadMethod = procedure (Self : TObject; const Str : String); 
public 
    procedure Save (const FileName : String); 
    procedure Load (const FileName : String); 
end; 

La carga métodos de este aspecto (método Save en consecuencia):

procedure TConfiguration.Load (const FileName : String); 
const 
    PropNotFound = '_PROP_NOT_FOUND_'; 
var 
    IniFile : TIniFile; 
    Count : Integer; 
    List : PPropList; 
    TypeName, PropName, InputString, MethodName : String; 
    LoadMethod : TCustomLoadMethod; 
begin 
    IniFile := TIniFile.Create (FileName); 
    try 
    Count := GetPropList (Self.ClassInfo, tkProperties, nil) ; 
    GetMem (List, Count * SizeOf (PPropInfo)) ; 
    try 
     GetPropList (Self.ClassInfo, tkProperties, List); 
     for I := 0 to Count-1 do 
     begin 
     TypeName := String (List [I]^.PropType^.Name); 
     PropName := String (List [I]^.Name); 
     InputString := IniFile.ReadString ('Options', PropName, PropNotFound); 
     if (InputString = PropNotFound) then 
      Continue; 
     MethodName := 'Load' + TypeName; 
     LoadMethod := Self.MethodAddress (MethodName); 
     if not Assigned (LoadMethod) then 
      raise EConfigLoadError.Create ('No load method for custom type ' + TypeName); 
     LoadMethod (Self, InputString); 
     end; 
    finally 
     FreeMem (List, Count * SizeOf (PPropInfo)); 
    end; 
    finally 
    FreeAndNil (IniFile); 
    end; 

La clase base podría proporcionar cargar y guardar métodos para los tipos predeterminados Delphi. A continuación, puedo crear una configuración para mi aplicación como esta:

TMyConfiguration = class (TConfiguration) 
... 
published 
    function SaveTObject (P : Pointer) : String; 
    procedure LoadTObject (const Str : String); 
published 
    property BoolOption : Boolean read FBoolOption write FBoolOption; 
    property ObjOption : TObject read FObjOption write FObjOption; 
end; 

Ejemplo de una costumbre método para guardar:

function TMyConfiguration.SaveTObject (P : Pointer) : String; 
var 
    Obj : TObject; 
begin 
    Obj := TObject (P); 
    Result := Obj.ClassName; // does not make sense; only example; 
end;  
+0

Esto se ve bastante bien para mí.¿Qué te hace pensar que podría haber una solución más inteligente? –

+0

@Jeroen: La experiencia que en la mayoría de los casos cuando pregunto aquí recibo muchos comentarios inteligentes, sugerencias de mejora y críticas :) Además de eso, quería compartir esa parte del código para que otros puedan beneficiarse. – jpfollenius

0

Esto sería para Java.

Me gusta usar la clase java.util.Properties para leer archivos de configuración o de propiedades. Lo que me gusta es que coloque su archivo con líneas de la misma forma que mostró arriba (clave = valor). Además, usa un # (signo de almohadilla) para una línea que es un comentario, algo así como una gran cantidad de lenguajes de scripting.

lo que podría utilizar:

ShowFlags=true 
# this line is a comment  
NumFlags=42 

etc

A continuación, sólo tiene un código como:

Properties props = new Properties(); 
props.load(new FileInputStream(PROPERTIES_FILENAME)); 
String value = props.getProperty("ShowFlags"); 
boolean showFlags = Boolean.parseBoolean(value); 

fácil como eso.

+0

No ayuda mucho porque estaba pidiendo Delphi y la clase que propone es específica de Java ... – jpfollenius

+0

sí, lo siento. En realidad no estabas especificando, así que le di una oportunidad. La pregunta fue formulada de manera muy general. No viste tu etiqueta delphi. – Nick

+0

no hay problema. gracias de cualquier manera. – jpfollenius

1

Básicamente está pidiendo una solución para serializar un objeto dado (en su caso una configuración de archivos ini). Hay componentes listos para eso y puede comenzar a buscar here y here.

6

Uso XML para todas mis aplicaciones como medio de configuración.Es:

  • flexibles
  • función a prueba de futuro
  • fácil de leer con cualquier lector de texto
  • muy fácil de extender en su aplicación. No se necesitan modificaciones de clase

Tengo una biblioteca XML que hace que sea extremadamente fácil de leer o modificar la configuración, sin siquiera tener que mirar los valores perdidos. Ahora también puede asignar el XML a una clase dentro de la aplicación para un acceso más rápido si la velocidad es el problema, o ciertos valores se leen constantemente.

encuentro a otros métodos de configuración mucho menos opcional:

  • archivo Ini: ninguna estructura en profundidad, mucho menos flexibles
  • registro: sólo mantener lejos de eso.
+1

Hago más o menos lo mismo, pero utilizo el XML Data Binding Wizard de Delphi para asignar el archivo a los objetos. –

+1

La cosa es: creo que los archivos INI pueden ser fácilmente entendidos incluso por los usuarios, mientras que no todos pueden entender XML. Sé que debería tener diálogos de configuración para eso, pero me gustaría que cualquiera pueda cambiar las configuraciones más avanzadas sin tener que sumergirse en XML. – jpfollenius

+1

Los archivos XML son muy fáciles de leer. Estoy hablando de XML básico aquí. Solo nodos con atributos y valores (texto). No es menos legible en comparación con un INI. Ok, tienes más "chapado de calderas" en las etiquetas. Pero si te preocupa que los usuarios modifiquen, crea una GUI para ellos. No puede confiar en que el usuario modifique un archivo por sí mismo. Incluso un INI. – Runner

3

Mi método preferido es el de crear una interfaz en mi unidad de interfaces mundial:

type 
    IConfiguration = interface 
    ['{95F70366-19D4-4B45-AEB9-8E1B74697AEA}'] 
    procedure SetConfigValue(const Section, Name,Value:String); 
    function GetConfigValue(const Section, Name:string):string; 
    end; 

Esta interfaz se "expone" en mi forma principal:

type 
    tMainForm = class(TForm,IConfiguration) 
    ... 
    end; 

mayoría de las veces la implementación real no está en la forma principal, solo es un marcador de posición y utilizo la palabra clave implements para redirigir la interfaz a otro objeto propiedad del formulario principal. El objetivo de esto es que se delega la responsabilidad de la configuración. A cada unidad no le importa si la configuración está almacenada en una tabla, archivo ini, archivo xml o incluso (gasp) en el registro. Lo que esto no me permite hacer en cualquier unidad que utiliza la unidad de las interfaces globales es hacer una llamada como la siguiente:

var 
    Config : IConfiguration; 
    Value : string; 
begin 
    if Supports(Application.MainForm,IConfiguration,Config) then 
    value := Config.GetConfiguration('section','name'); 
    ...  
end; 

Todo lo que se necesita es la adición de formas y mi unidad de interfaz global para la unidad que estoy trabajando en. Y debido a que NO UTILIZA el formulario principal, si decido volver a utilizarlo para otro proyecto, no tengo que hacer más cambios ... simplemente funciona, incluso si el esquema de almacenamiento de configuración es completamente diferente.

Mi preferencia general es crear una tabla (si estoy tratando con una aplicación de base de datos) o un archivo XML. Si es una aplicación de base de datos multiusuario, crearé dos tablas. Uno para la configuración global y otro para la configuración del usuario.

+0

+1, porque estoy de acuerdo con la mayoría de lo que escribiste, y las diferencias son principalmente cuestiones de estilo. Pero la respuesta, tal como está, no tiene mucho que ver con la pregunta, que entendí como "¿cuál es la mejor forma de escribir una clase base que sea capaz de cargarse y almacenarse en un almacenamiento persistente sin la necesidad de cambios en los descendientes? clases ". Esa es al menos la pregunta que respondió el OP, el título simplemente no coincide con el texto de la pregunta. Aún así, smasher debe seguir su consejo en lugar de codificar una solución de archivo INI. – mghie

+0

Quiero que mi configuración de configuración tome el menor tiempo posible, porque se repite con cada aplicación. Aunque me gusta su enfoque en cierta medida, no es KISS lo suficiente para mí. El singleton global simple con back-end XML y una buena interfaz para acceder a él es lo suficientemente bueno en la mayoría de los casos. Y más que suficiente para pequeñas aplicaciones simples. Incluso la aplicación completamente orientada a la base de datos todavía necesita un archivo de configuración simple para acceder a esa información :) – Runner

+0

No podría estar más de acuerdo con el comentario de mghie. Usted propone una buena alternativa a la variable global utilizando una interfaz y delegación, pero no dice nada sobre cómo se realiza la serialización. No sería ningún problema extraer un IPropertyStorer de mi solución para separar la configuración de la implementación concreta (archivo INI/XML/base de datos). Pero eso no cambia los puntos sobre el almacenamiento persistente con un mínimo esfuerzo. – jpfollenius

1

Hace algún tiempo escribí una unidad pequeña para la misma tarea - para guardar/cargar la configuración de la aplicación en el archivo xml.

Compruebe la unidad Obj2XML.pas en nuestra biblioteca SMComponent freeware: http://www.scalabium.com/download/smcmpnt.zip

0

Nicks respuesta (utilizando propiedades de Java) tiene un punto: esta sencilla manera de leer y pasar configuración alrededor entre las partes de la aplicación no introduce dependencias en una clase de configuración especial. Una lista simple de clave/valor puede reducir las dependencias entre los módulos de la aplicación y facilitar la reutilización del código.

En Delphi, una configuración simple basada en TStrings es una forma fácil de implementar una configuración. Ejemplo:

mail.smtp.host=192.168.10.8  
mail.smtp.user=joe  
mail.smtp.pass=******* 
Cuestiones relacionadas