2009-06-08 18 views
7

Tengo una clase que pasa en una carpeta y luego se apaga y procesa una gran cantidad de datos dentro de la carpeta especificada.C# Constructor Design

Por ejemplo:

MyClass myClass = new MyClass(@"C:\temp"); 

Ahora lo que hace sale y lee decir un par de miles de archivos y llena la clase de datos.

¿Debo pasar esta información a cabo desde el constructor y tenerlo como un método independiente, como por ejemplo:

MyClass myClass = new MyClass(); 
myClass.LoadFromDirectory(@"C:\temp"); 

Respuesta

22

tal vez debería tratar de esta manera con un método estático que devuelve una instancia del objeto.

var myClass = MyClass.LoadFromDirectory(@"C:\temp"); 

Esto evitará que el código de inicialización fuera de su constructor, así como que le da esa declaración "una línea" que busca.


El ir en el comentario de abajo del cartel, añadiendo Estado una implementación podría ser así:

public class MyClass 
{ 

#region Constructors 

    public MyClass(string directory) 
    { 
     this.Directory = directory; 
    } 

#endregion 

#region Properties 

    public MyClassState State {get;private set;} 

    private string _directory; 

    public string Directory 
    { 
     get { return _directory;} 
     private set 
     { 
      _directory = value; 
      if (string.IsNullOrEmpty(value)) 
       this.State = MyClassState.Unknown; 
      else 
       this.State = MyClassState.Initialized; 
     } 
    } 

#endregion 



    public void LoadFromDirectory() 
    { 
     if (this.State != MyClassState.Initialized || this.State != MyClassState.Loaded) 
      throw new InvalidStateException(); 

     // Do loading 

     this.State = MyClassState.Loaded; 
    } 

} 

public class InvalidStateException : Exception {} 


public enum MyClassState 
{ 
    Unknown, 
    Initialized, 
    Loaded 
} 
+0

La buena idea, la inicialización y el uso de una clase son a menudo bastante diferentes. Esto los separa muy bien. Para una separación aún mayor, puede mover la lógica de inicialización a una clase de fábrica o constructora. – Mendelt

1

Es esto todo lo que su clase hace? Si es así, entonces diría que en realidad no importa. Pero es probable que tu clase esté haciendo más de lo que has demostrado. ¿Tiene algún error de manejo, por ejemplo?

El propósito del constructor es construir un objeto. El propósito de un método es realizar una acción. Entonces mi voto es para este formulario:

MyClass myClass = new MyClass(); 
myClass.LoadFromDirectory(@"C:\temp"); 
+0

Habrá mucho más para la clase y estoy en el proceso de diseñarlo. Sin embargo, las agallas de la clase requieren que se llene a través de los datos pasados ​​a la clase. –

5

Depende. Debes evaluar el propósito básico de la clase. ¿Qué función desempeña?

Lo que suelo preferir es que un constructor de clase haga la inicialización necesaria para el funcionamiento de la clase. Luego invoco métodos en la clase que pueden asumir con seguridad que se ha realizado la inicialización necesaria.

Por lo general, la fase de inicialización no debe ser demasiado intensa. Una forma alternativa de hacer lo anterior puede ser:

// Instantiate the class and get ready to load data from files. 
MyClass myClass = new MyClass(@"C:\temp"); 

// Parse the file collection and load necessary data. 
myClass.PopulateData(); 
+0

Esto es realmente una mejor práctica y la mayoría de las herramientas de refactorización le indicarán que no llame a los métodos de sus constructores a menos que sean métodos de inicialización, como la creación de controles o la configuración de propiedades. –

+5

Eso lleva a que la clase tenga dos estados muy distintos: uno donde tiene un nombre de archivo y otro donde tiene los datos reales. Después de la población, parece que no necesitará el nombre del archivo, pero aún necesitará una variable para él. Ese tipo de cosas siempre me pone nervioso. Prefiero el enfoque de método estático, donde el resultado es un objeto que está "listo para la acción". –

+0

@Jon Skeet, increíble, no es el mejor lugar, pero tu libro C# en profundidad me ha ayudado mucho. ¡Gracias! - Mi preocupación es que esta clase puede tardar varios minutos en crearse ya que procesa los datos y tener un estado dual es lo que esperaba evitar. –

0

creo que debe decidir entre los dos enfoques anteriores ("primero inicializar, a continuación, ejecute "vs" empty init, perform with params ") en función de si planea reutilizar el mismo objeto para realizar la misma operación en una entrada diferente.
si la clase solo se usa para ejecutar la tarea en un parámetro fijo, me gustaría inicializarlo en el constructor (por lo que es de solo lectura), y luego realizar la tarea en un método diferente.
Si desea seguir realizando la tarea en params diferentes, lo pondría en el método de la tarea en sí.

Si toda la clase lo hace, también consideraría cambiarlo todo a una clase/métodos estáticos; no es necesario que mantenga su estado interno.

De todos modos, nunca pondría la tarea en el constructor. Como dijo Cerebrus, la inicialización debe ser rápida.

0

A menos que el objetivo principal de su clase sea realizar E/S, probablemente no deba realizar E/S (potencialmente lanzando una IOException) en el constructor.

considerar dividir la clase en dos:

interface IMyDataSource 
{ 
    // ... 
} 

class FileDataSource: IMyDataSource 
{ 
    public FileDataSource(string path) 
    { 
     // ... 
    } 
} 

class MyClass 
{ 
    public MyClass(IMyDataSource source) 
    { 
     // ... 
    } 
} 

IMyDataSource myDS = new FileDataSource(@"C:\temp"); 
MyClass myClass = new MyClass(myDS); 

De esta manera la clase principal se centrará en la gestión de su propio estado, mientras que la fuente de datos se basa el estado inicial de los contenidos del archivo.

+1

Pero ahora el FileDataSource tiene el potencial de arrojar excepciones en su constructor, pasando los errores a un objeto separado. –

+0

@Tom Anderson, esa es la idea. FileDataSource está diseñado para hacer E/S, por lo que se esperan excepciones de E/S en el constructor. MyClass no está diseñado para hacer E/S, por lo que se evitan las excepciones de E/S en el constructor. – finnw

0

Si ese es el único recurso con el que trabaja la clase, probablemente sería mejor pasar la ruta al constructor. De lo contrario, sería un parámetro para los miembros de tu clase.

0

Mi preferencia personal sería utilizar los inicializadores C# 3.0.

class MyClass { 
    public string directory; 
    public void Load() { 
     // Load files from the current directory 
     } 
    } 

MyClass myClass = new MyClass{ directory = @"C:\temp" }; 
myClass.Load(); 

Esto tiene algunas ventajas:

  • de instancias de objeto no tendrá un efecto secundario del sistema automática de archivos.
  • Todos los argumentos son nombrados.
  • Todos los argumentos son opcionales (pero, por supuesto, podría lanzar una excepción en Load() si no se define)
  • Puede inicializar todas las propiedades que se desea en la llamada instanciación sin tener que sobrecargar el constructor. Por ejemplo, las opciones para recurse directorios, o un comodín para la especificación de archivos para buscar para.
  • Todavía puede tener un poco de lógica en el setter para el directorio para hacer algunas cosas, pero una vez más, los efectos secundarios generalmente no son una buena cosa.
  • Mediante la realización de operaciones de archivo en una llamada de procedimiento separado , se evita el problema de no ser capaz de referencia a su myClass ejemplo, en el manejador de excepciones.
+0

Aunque todavía no es un mal enfoque, aunque tiene un estado, campo establecido o no establecido. –

0

Voy a hacer eco de la gente de "dividirlos" aquí. Si ayuda, intente lo siguiente:

  1. Pregúntese: "¿Qué hace este método/propiedad/terreno hacer?"
  2. Haz que haga eso; ni mas ni menos.

la aplicación de ese aquí, se obtiene lo siguiente:

  1. El constructor se supone que crear el objeto.
  2. Se supone que su método carga sus datos del sistema de archivos.

Eso me parece ser mucho más lógico que "El constructor se supone que crear el objeto y cargar sus datos desde el sistema de archivos.

+0

De acuerdo. Si desea bloquear y solo regresa cuando se haya rellenado su objeto posterior, use una fábrica con un método estático. – richardtallent

1

Estoy de acuerdo con Ari y otros, divídalos.

Un constructor realmente debe hacer la mínima cantidad de trabajo (simplemente inicialice el objeto listo para usar y déjelo así). Al usar un método diferente para hacer el trabajo:

  • Es más claro para quien llama que la función de trabajador puede llevar mucho tiempo.
  • Es fácil proporcionar varios constructores para inicializar el objeto con información diferente (por ejemplo, es posible que pueda pasar su propia clase (en lugar de una cadena) que puede proporcionar la ruta de acceso. O podría pasar un parámetro adicional que especifica un nombre de archivo comodín para que coincida, o un indicador para especificar si la búsqueda debe recurrir a las subcarpetas).
  • Evita cualquier problema con el constructor. En el constructor, el objeto no está completamente formado, por lo que puede ser peligroso trabajar, p. Ej. Llamar a una función virtual dentro de un constructor es una muy mala idea. Cuanto menos código pongas en el constructor, menos probable es que hagas algo "malo" por accidente.
  • Es un estilo de codificación más limpio para separar diferentes comportamientos/funciones en métodos separados. Mantener la inicialización y el trabajo separados
  • Una clase dividida será más fácil de mantener y refactorizar en el futuro.