¿Viola algún principio OOP si una función miembro no usa ninguna de las propiedades de clase / variables miembro?

7

Tengo una clase existente que interactúa que se puede abrir, leer o escribir en un archivo. Necesito recuperar una modificación de archivo para ese propósito. Tengo que agregar un nuevo método

Supongamos que esta es mi definición de clase donde quiero agregar un nuevo método.

class IO_file
{
 std::string m_file_name;
 public:
 IO();
 IO(std::string file_name);

+ time_t get_mtime(file_name);
+       OR
+ time_t get_mtime();
};

Tengo dos opciones -

  1. Cree un objeto vacío y luego pase el file_name en el argumento del método para el cual deseo recuperar la hora de modificación del archivo.

  2. Pase el nombre del archivo en la construcción del objeto y simplemente llame a la función miembro que operará en la variable miembro.

Ambas opciones servirán al propósito. También creo que el segundo enfoque es mejor que el primero. Pero lo que no entiendo es ¿Cómo ? ¿El primer enfoque es de mal diseño ya que no hace uso de una variable miembro? ¿Qué principio de diseño orientado a objetos viola? Si una función miembro no usa la variable miembro, ¿debería esa función miembro siempre debe estar estática?

    
pregunta Rahul 10.11.2016 - 19:19

4 respuestas

7
  

¿Viola cualquier principal OOP si una función miembro no usa ninguna de las propiedades de clase / variables miembro?

No.

A la POO no le importa si su función miembro usa, o no usa, propiedades de clase o variables de miembro. OOP se preocupa por el polimorfismo y no la implementación de codificación dura. Las funciones estáticas tienen su uso, pero una función no debería ser estática simplemente porque no depende del estado del objeto. Si esa es tu forma de pensar bien, pero no culpes a OOP porque esa idea no vino de OOP.

  

¿Es [it] mal diseño [no] hacer uso de variables miembro?

Si no necesita recordar el estado de una a otra, no hay una buena razón para utilizar el estado.

  

¿Qué principio de diseño orientado a objetos [viola]?

Ninguna.

  

Si una función miembro no usa la variable miembro, ¿esa función miembro siempre debería estar estática?

No. Este pensamiento tiene la flecha de implicación que va en la dirección equivocada.

  • Una función estática no puede acceder al estado de la instancia

  • Si la función no tiene necesidad de acceder al estado de instancia, la función puede ser estática o no estática

Hacer la función estática aquí depende completamente de ti. Pero lo hará más como un global si lo haces. Antes de ir estático, considere alojar la función en una clase sin estado. Es más flexible.

Tengo aquí un ejemplo OOP de una función miembro que no usa propiedades de clase o variables miembro.

La función miembro (y su clase sin estado) :

#include <iostream>

class Strategy
{
public:
     virtual int execute (int a, int b) = 0; // execute() is a so-called pure virtual 
                                             // function. As a consequence, Strategy 
                                             // is a so-called abstract class.
};


Tres implementaciones diferentes:

class ConcreteStrategyAdd:public Strategy
{
public:
    int execute(int a, int b)
    {
        std::cout << "Called ConcreteStrategyAdd's execute()\n";
        return a + b;
    }
};

class ConcreteStrategySubstract:public Strategy
{
public:
    int execute(int a, int b)
    {
        std::cout << "Called ConcreteStrategySubstract's execute()\n";
        return a - b;
    }
};

class ConcreteStrategyMultiply:public Strategy
{
public:
    int execute(int a, int b)
    {
        std::cout << "Called ConcreteStrategyMultiply's execute()\n";
        return a * b;
    }
};


Un lugar para almacenar la opción de implementación:

class Context
{
private:
    Strategy* pStrategy;

public:

    Context (Strategy& strategy)
        : pStrategy(&strategy)
    {
    }

    void SetStrategy(Strategy& strategy)
    {
        pStrategy = &strategy;
    }

    int executeStrategy(int a, int b)
    {
        return pStrategy->execute(a,b);
    }
};


Un ejemplo de uso

int main()
{
    ConcreteStrategyAdd       concreteStrategyAdd;
    ConcreteStrategySubstract concreteStrategySubstract;
    ConcreteStrategyMultiply  concreteStrategyMultiply;

    Context context(concreteStrategyAdd);
    int resultA = context.executeStrategy(3,4);

    context.SetStrategy(concreteStrategySubstract);
    int resultB = context.executeStrategy(3,4);

    context.SetStrategy(concreteStrategyMultiply);
    int resultC = context.executeStrategy(3,4);

    std::cout << "\nresultA: " << resultA 
              << "\nresultB: " << resultB 
              << "\nresultC: " << resultC 
              << "\n";
}

Outputs:

Called ConcreteStrategyAdd's execute()
Called ConcreteStrategySubstract's execute()
Called ConcreteStrategyMultiply's execute()

resultA: 7       
resultB: -1       
resultC: 12

Y todo sin execute() preocupándose por el estado de cualquier objeto. La clase Strategy es en realidad sin estado. Solo el estado está en Context . Los objetos sin estado están perfectamente bien en OOP.

Encontré este código aquí .

    
respondido por el candied_orange 10.11.2016 - 23:19
5

get_mtime tendría más sentido aquí como una función independiente o como una función static , en lugar de como se muestra aquí.

El mtime de un archivo se lee, en la mayoría de los sistemas, desde una llamada a lstat o similar, y no requiere un descriptor de archivo abierto, por lo que no tiene sentido tenerlo como miembro Función de una clase instanciada.

    
respondido por el greyfade 10.11.2016 - 19:38
3

La razón por la que la segunda opción parece instintivamente mejor (y, IMO, ES mejor) es porque su primera opción le da un objeto que en realidad no representa nada.

Al no especificar el nombre de archivo, su clase IO_file es en realidad un objeto abstracto que se parece a un archivo concreto. Si está pasando el nombre del archivo cuando llama al método, también puede simplemente refactorizar todo el método en una función pura de flotación libre; no hay un beneficio real para mantenerlo atado a un objeto que necesitará instanciar solo para usarlo. Es sólo una función; La placa de referencia de creación de objetos es solo un paso adicional inconveniente.

Por otra parte, una vez que se le da al nombre de archivo, todos los métodos a los que llama en ese objeto son más bien consultas similares sobre una instancia específica de una cosa. Eso es mejor OO porque tus objetos tienen un significado real y, por lo tanto, utilidad.

    
respondido por el moberemk 10.11.2016 - 19:36
1

Vamos a traducir esto a C. Primero nosotros, nuestra clase, ahora es una estructura:

struct IO_file {
    char* m_file_name;
};

Para la simplicidad de los fragmentos, definamos una función de constructor (ignorando las fugas de memoria por ahora):

struct IO_file* IO_file(char* file_name) {
    struct IO_file* obj = malloc(sizeof(struct IO_file));
    obj.m_file_name = file_name;
    return obj;
}

La opción # 2 se ve así:

time_t get_mtime(struct IO_file*);

y usado de esta manera:

time_t mtime = get_mtime(IO_file("some-file"));

¿Qué hay de la opción # 1? Bueno, se parece a esto:

time_t get_mtime(struct IO_file* this, char* file_name);

¿Y cómo se usa? Esencialmente, estás pidiendo que pases el correo basura como primer parámetro:

time_t mtime = get_mtime((struct IO_file*)1245151325, "some-file");

No tiene mucho sentido, ¿verdad?

El sistema de objetos de C ++ lo oculta, pero el objeto también es un argumento del método, un argumento de puntero implícito llamado this . Esto es lo que la opción # 1 es mala: tiene un argumento que no se usa por definición (los argumentos que no se usan, pero se pueden usar en anulaciones están bien). Dichos argumentos no aportan nada al tiempo que complican la firma, la definición y el uso de la función y confunden a los lectores del código que se deja pensando en qué se supone que hace el argumento, por qué está bien pasarle la chatarra y si el hecho de que usted es o no pasar junk / NULL / empty a él es la causa del error que actualmente están tratando de resolver. Spoiler: no lo es, pero aún así van a perder tiempo explorando esa posibilidad.

El hecho de que el argumento de la chatarra sea implícito puede disminuir el efecto, pero sigue ahí, y puede evitarse fácilmente haciendo que el método static .

    
respondido por el Idan Arye 10.11.2016 - 21:28

Lea otras preguntas en las etiquetas