OOP Estilo de codificación: ¿inicializar todo en el constructor?

14

Todavía me considero un aprendiz de programador, por lo que siempre busco aprender una "mejor" forma para la programación típica. Hoy, mi compañero de trabajo ha argumentado que mi estilo de codificación hace un trabajo innecesario y quiero escuchar las opiniones de otros. Normalmente, cuando diseño una clase en lenguaje OOP (generalmente C ++ o Python), separaría la inicialización en dos partes diferentes:

class MyClass1 {
public:
    Myclass1(type1 arg1, type2 arg2, type3 arg3);
    initMyClass1();
private:
    type1 param1;
    type2 param2;
    type3 param3;
    type4 anotherParam1;
};

// Only the direct assignments from the input arguments are done in the constructor
MyClass1::myClass1(type1 arg1, type2 arg2, type3 arg3)
    : param1(arg1)
    , param2(arg2)
    , param3(arg3)
    {}

// Any other procedure is done in a separate initialization function 
MyClass1::initMyClass1() {
    // Validate input arguments before calculations
    if (checkInputs()) {
    // Do some calculations here to figure out the value of anotherParam1
        anotherParam1 = someCalculation();
    } else {
        printf("Something went wrong!\n");
        ASSERT(FALSE)
    }
}

(o, equivalente de python)

class MyClass1:

    def __init__(self, arg1, arg2, arg3):
        self.arg1 = arg1
        self.arg2 = arg2
        self.arg3 = arg3
        #optional
        self.anotherParam1 = None

    def initMyClass1():
        if checkInputs():
            anotherParam1 = someCalculation()
        else:
            raise "Something went wrong!"

¿Cuál es su opinión sobre este enfoque? ¿Debo abstenerme de dividir el proceso de inicialización? La pregunta no solo se limita a C ++ y Python, y también se agradecen las respuestas a otros idiomas.

    
pregunta Caladbolgll 23.11.2016 - 22:24

5 respuestas

28

Aunque a veces es problemático, hay muchas ventajas al inicializar todo en el constructor:

  1. Si se produce un error, sucede lo más rápido posible y es más fácil de diagnosticar. Por ejemplo, si nulo es un valor de argumento no válido, pruebe y falle en el constructor.
  2. El objeto está siempre en un estado válido. Un compañero de trabajo no puede cometer un error y se olvida de llamar a initMyClass1() porque no está allí . "Los componentes más baratos, más rápidos y más confiables son aquellos que no están allí".
  3. Si tiene sentido, el objeto puede ser inmutable que tiene muchas ventajas.
respondido por el user949300 23.11.2016 - 23:17
2

Piense acerca de la abstracción que está brindando a sus usuarios.

¿Por qué dividir algo que podría hacerse en un disparo en dos?

La inicialización adicional es solo algo extra para los programadores que usan su API para recordar, y ofrece más para que no funcionen correctamente, pero ¿para qué valor para esta carga adicional?

Desea proporcionar abstracciones simples, fáciles de usar y difíciles de equivocar. La programación es lo suficientemente difícil como para que no se recuerden cosas gratuitas / aros para saltar. Desea que los usuarios de su API (incluso si solo está utilizando su propia API) caigan en el pit of success .

    
respondido por el Erik Eidt 24.11.2016 - 01:07
1

Inicializa todo excepto el área de big data. Las herramientas de análisis estático marcarán los campos no inicializados en el constructor. Sin embargo, la forma más productiva / segura es tener todas las variables miembro con constructores predeterminados e inicializar explícitamente solo aquellas que requieren inicialización no predeterminada.

    
respondido por el zzz777 23.11.2016 - 22:31
0

Hay casos en que el objeto tiene mucha inicialización que se puede dividir en dos categorías:

  1. Atributos que son inmutables o que no necesitan restablecerse.

  2. Los atributos que pueden necesitar revertir a los valores originales (o valores templados) en función de alguna condición luego de cumplir su trabajo, son una especie de restablecimiento automático. p.ej. Conexiones en un pool de conexiones.

Aquí, la segunda parte de la inicialización que se mantiene en una función separada, por ejemplo, InitialiseObject (), se puede llamar en el ctor.

Se puede llamar a la misma función más adelante si se requiere un reinicio por software, sin tener que descartar y recrear el objeto.

    
respondido por el Ramakant 25.11.2016 - 11:41
0

Como han dicho otros, generalmente es una buena idea iniciar en el constructor.

Sin embargo, existen razones para que esto no se aplique o no en casos específicos.

Manejo de errores

En muchos idiomas, la única forma de señalar un error en un constructor es generar una excepción.

Si su inicialización tiene una posibilidad razonable de generar un error, por ejemplo. implica que IO o sus parámetros podrían ser aportados por el usuario, entonces el único mecanismo disponible para usted es generar una excepción. En algunos casos, esto puede no ser lo que desea y puede tener más sentido separar el código propenso a errores a una función de inicialización separada.

Probablemente, el ejemplo más común de esto es en C ++ si el estándar del proyecto / organización es desactivar las excepciones.

Máquina de estado

Este es el caso en el que está modelando un objeto que tiene transiciones de estado explícitas. Por ejemplo, un archivo o un socket que se puede abrir y cerrar.

En este caso, es común que la construcción del objeto (y la eliminación) se ocupe únicamente de los atributos orientados a la memoria (nombre de archivo, puerto, etc.). Luego habrá funciones para administrar específicamente las transiciones de estado, p. Ej. abrir, cerrar, que son efectivamente funciones de inicialización y desmontaje.

Las ventajas se encuentran en el manejo de errores, como se indicó anteriormente, pero también puede haber un caso para separar la construcción de la inicialización (por ejemplo, crea un vector de archivos y los abre de forma asíncrona).

La desventaja, como han dicho otros, es que ahora pone la carga de la administración del estado en el usuario de sus clases. Si pudiera administrar solo con la construcción, entonces podría, digamos, usar RAII para hacer esto automáticamente.

    
respondido por el Alex 25.11.2016 - 17:58

Lea otras preguntas en las etiquetas