2011-01-11 16 views
5

Estoy escribiendo una simulación de física (modelo de Ising) en C++ que opera en celosías cuadradas. El corazón de mi programa es mi clase Ising con un constructor que solicita las dimensiones de fila y columna del enrejado. Tengo otros dos métodos para establecer otros parámetros del sistema (temperatura & estado inicial) ¡que se debe llamar al antes de desarrollar el sistema! Así, por ejemplo, un programa de ejemplo podría tener este aspecto¿Considera que los pasos de inicialización múltiples son "deficientes"?

int main() { 
    Ising system(30, 30); 
    system.set_state(up); 
    system.set_temperature(2); 

    for(int t = 0; t < 1000; t++) { 
     system.step(); 
    } 

    return 0; 
} 

Si el system.set _ *() métodos no son llamados antes system.step(), system.step() lanza una excepción de alerta el usuario al problema Lo implementé de esta manera para simplificar mi constructor; es esta mala practica?

+0

No, no es mala forma, más que absoluto principiante el código es de forma pobre. Es como ese código absoluto para principiantes. Con algo más de experiencia, aprenderá sobre invariantes de clase y RAII (ambos requieren un solo paso de construcción), y aprenderá sobre técnicas para admitir el uso simple de constructores, como NPI. –

Respuesta

13

Se recomienda poner todos los parámetros obligatorios en el constructor siempre que sea posible (hay excepciones, por supuesto, pero deben ser raras - I have seen one real-world example hasta ahora). De esta manera, haces que tu clase sea más fácil y segura de usar.

Tenga en cuenta también que al simplificar su constructor, en su lugar, el código del cliente es más complicado, lo que IMO es una mala compensación. El constructor se escribe solo una vez, pero es posible que el código de la persona que llama deba escribirse muchas veces más (aumentando tanto la cantidad de código que se va a escribir como la posibilidad de errores).

+1

También puede considerar una clase 'IsingInitialState' que su usuario debe completar y pasar como argumento a su clase' Ising'. –

+0

@Alexandre C., de hecho, si hay demasiados parámetros, vale la pena introducir un Objeto de parámetro. Pero los parámetros IMHO 4 no son un gran problema todavía. –

+2

+1. Tenga en cuenta que el constructor se implementa solo una vez, pero se puede llamar desde muchos lugares diferentes, aumentando así la probabilidad de error. –

0

Si los métodos de inicialización deben invocarse en un orden específico, envolveré la llamada en su propio método ya que esto indica que los métodos no son atómicos por sí mismos, por lo que el 'conocimiento' de cómo se deben llamar debe llevarse a cabo en un solo lugar.

Bueno, esa es mi opinión, de todos modos!

1

IMO esta es una forma deficiente si se deben invocar todos estos pasos de inicialización cada vez. Uno de los objetivos de un software bien diseñado es minimizar las oportunidades de arruinarlo, y tener múltiples métodos que deben invocarse antes de que un objeto sea "utilizable" simplemente hace que sea más difícil hacerlo bien. Si estas llamadas fueran opcionales, tenerlas como métodos separados estaría bien.

Comparte y disfruta.

2

Nada, IMO. Me enfrento a lo mismo al cargar datos de archivos externos. Cuando se crean los objetos (es decir, se llaman sus respectivos ctors), los datos aún no están disponibles y solo se pueden recuperar en una etapa posterior. Así que dividir la inicialización en diferentes etapas:

  1. constructor
  2. inicialización (llamado por el motor de marco cuando un objeto se activa por primera vez)
  3. activación (llamado cada vez que se activa un objeto).

Esto es muy específico para el framework que estoy desarrollando, pero no hay forma de tratar todo con solo el constructor.

Sin embargo, si conoce las variables en el momento en que se llama al ctor, es mejor no complicar el código. Es una posible fuente de dolores de cabeza para cualquiera que use su código.

+2

Quizás un comentario que detalla POR QUÉ mi respuesta ha sido downvoted? – mingos

1

Todo el punto de una clase es presentar algún tipo de abstracción. Como usuario de una clase, debería ser capaz de asumir que se comporta como la abstracción que modela.

Y parte de eso es que la clase siempre debe ser válida. Una vez que el objeto ha sido creado (llamando al constructor), la clase debe estar en un estado significativo y válido. Debería estar listo para usar. Si no es así, entonces ya no es una buena abstracción.

0

Diría que establecer las condiciones iniciales debe estar separado del constructor si planea inicializar y ejecutar más de un transitorio en el mismo enrejado.

Si ejecuta un transitorio y se detiene, entonces es posible mover estableciendo las condiciones iniciales dentro del constructor, pero esto significa que debe pasar los valores de los parámetros para poder hacerlo.

Estoy totalmente de acuerdo con la idea de que un objeto debe estar 100% listo para ser utilizado después de que se llame a su constructor, pero creo que eso es diferente de la física de establecer el campo de temperatura inicial. El objeto puede ser completamente utilizable, pero tiene todos los nodos en el problema a la misma temperatura de cero absoluto. Un campo de temperatura uniforme en un cuerpo aislado no es de mucho interés desde el punto de vista de la transferencia de calor.

0

Como señaló otro comentarista, tener que llamar a un montón de funciones de inicialización es de mala calidad. Me gustaría terminar con esto en una clase:

class SimulationInfo 
{ 
private: 
    int x; 
    int y; 
    int state; 
    int temperature; 

public: 
    SimulationArgs() : x(30), y (30), state(up), temperature(2) { }; // default ctor 
    // custom constructors here! 

    // properties 
    int x() const { return x; }; 
    int y() const { return y; }; 
    int state() const { return state; }; 
    int temperature() const { return temperature; }; 
}; // eo class SimulationInfo 


class Simulation 
{ 
private: 
    Ising m_system; 

public: 
    Simulation(const SimulationInfo& _info) : m_system(_info.x(), _info.y()) 
    { 
     m_system.set_state(_info.state()); 
     m_system.set_temperature(_info.temperature()); 
    } // eo ctor 


    void simulate(int _steps) 
    { 
     for(int step(0); step < _steps; ++steps) 
      m_system.step(); 
    } // eo simulate 
}; // eo class Simulation 

Hay otherways, pero esto hace que las cosas infinitamente más utilizables a partir de la configuración por defecto:

SimulationInfo si; // accept all defaults 
Simulation sim(si); 
sim.simulate(1000); 
Cuestiones relacionadas