Vieta qualsiasi principio OOP se una funzione membro non usa nessuna delle proprietà di classe / delle variabili membro?

7

Ho una classe esistente che interagisce che può aprire, leggere o scrivere su un file. Ho bisogno di recuperare una modifica di file per questo scopo, devo aggiungere un nuovo metodo

Supponiamo che questa sia la mia definizione di classe in cui voglio aggiungere un nuovo metodo.

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();
};

Ho due opzioni -

  1. Crea un oggetto vuoto e poi passa il nome_file nell'argomento metodo per il quale desidero recuperare il tempo di modifica del file.

  2. Passa il nome del file alla costruzione dell'oggetto e chiama semplicemente la funzione membro che opererà sulla variabile membro.

Entrambe le opzioni serviranno allo scopo. Credo anche che il secondo approccio sia migliore del primo. Ma quello che non capisco è come ? Il primo approccio è un cattivo design in quanto non utilizza una variabile membro? Quale principio del design orientato agli oggetti viola? Se una funzione membro non usa la variabile membro, questa funzione membro dovrebbe sempre essere statica?

    
posta Rahul 10.11.2016 - 19:19
fonte

4 risposte

7

Does it violate any OOP principal if a member function does not use any of class properties/member variables?

No.

A OOP non interessa se la funzione membro usa, o non usa, proprietà di classe o variabili membro. OOP si preoccupa del polimorfismo e non dell'implementazione di hard coding. Le funzioni statiche hanno i loro usi ma una funzione non dovrebbe essere statica semplicemente perché non dipende dallo stato dell'oggetto. Se questo è il tuo modo di pensare bene, ma non dare la colpa a OOP perché quell'idea non è venuta da OOP.

Is [it] bad design [to not] make use of member variables?

Se non hai bisogno di ricordare lo stato da una chiamata all'altra, non c'è una buona ragione per usare lo stato.

Which principle of Object oriented design [does] it violate?

Nessuno.

If a member function does not use the member variable then that member function should always be made static?

No. Questo pensiero ha la freccia implicita che va nella direzione sbagliata.

  • Una funzione statica non può accedere allo stato dell'istanza

  • Se la funzione non ha bisogno di accedere allo stato dell'istanza, la funzione può essere statica o non statica

Rendere la funzione statica qui dipende completamente da te. Ma lo renderà più come un globale se lo fai. Prima di andare statico, considera l'hosting della funzione in una classe senza stato. È più flessibile.

Ho qui un esempio OOP di una funzione membro che non usa le proprietà di classe o le variabili membro.

La funzione membro (ed è una classe stateless) :

#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.
};


Tre diverse implementazioni:

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 luogo in cui memorizzare la scelta dell'implementazione:

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 esempio di utilizzo

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";
}

Uscite:

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

resultA: 7       
resultB: -1       
resultC: 12

E tutto senza execute() attento allo stato di qualsiasi oggetto. La classe Strategy è in realtà senza stato. Lo stato solo è in Context . Gli oggetti apolidi stanno perfettamente bene in OOP.

Trovato questo codice qui .

    
risposta data 10.11.2016 - 23:19
fonte
5

get_mtime avrebbe più senso qui come funzione autonoma o come funzione static , piuttosto che come hai mostrato qui.

Il mtime di un file viene letto, sulla maggior parte dei sistemi, da una chiamata a lstat o simili, e non richiede un descrittore di file aperto, quindi non ha senso averlo come membro funzione di una classe istanziata.

    
risposta data 10.11.2016 - 19:38
fonte
3

Il motivo per cui la seconda opzione sembra istintivamente migliore (e, IMO, IS meglio) è perché la tua prima opzione ti dà un oggetto che in realtà non rappresenta nulla.

Non specificando il nome del file, la tua classe IO_file è in realtà solo un oggetto astratto che sembra assomigliare a un file concreto. Se stai passando il nome del file quando chiami il metodo, puoi semplicemente rifattare l'intero metodo in una pura funzione free-floating; non c'è alcun vantaggio reale nel tenerlo legato a un oggetto che dovrai istanziare solo per usarlo. È solo una funzione; l'istanziazione di oggetti boilerplate è solo un passaggio aggiunto scomodo.

D'altro canto, una volta che il nome del file viene dato a tutti i metodi su cui si fa riferimento a quell'oggetto, si assomigliano più a una specifica istanza di una cosa. È meglio OO perché i tuoi oggetti hanno un significato reale e, quindi, utilità.

    
risposta data 10.11.2016 - 19:36
fonte
1

Trasformiamo questo in C. Prima abbiamo la nostra classe - ora è una struttura:

struct IO_file {
    char* m_file_name;
};

Consideriamo, per la semplicità dei frammenti, una funzione di costruzione (ignorando le perdite di memoria per ora):

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;
}

L'opzione n. 2 ha questo aspetto:

time_t get_mtime(struct IO_file*);

e usato in questo modo:

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

Che ne dici dell'opzione n. 1? Bene, sembra così:

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

E come viene utilizzato? In sostanza, stai chiedendo di passare la posta indesiderata come primo parametro:

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

Non ha molto senso, vero?

Il sistema di oggetti di C ++ lo nasconde, ma l'oggetto è anche un argomento del metodo - un argomento di puntatore implicito chiamato this . Questo è l'opzione 1 non valida: ha un argomento che non è utilizzato per definizione (gli argomenti che sono inutilizzati, ma che possono essere usati nelle sostituzioni sono OK). Tali argomenti non contribuiscono a nulla, mentre complicano la firma, la definizione e l'uso della funzione e confondono i lettori del codice che sono rimasti a riflettere su cosa dovrebbe fare l'argomento, perché è corretto passare la spazzatura ad esso e se il fatto che tu sia o meno passare junk / NULL / vuoto ad esso è la causa del bug che stanno attualmente cercando di risolvere. Spoiler - non lo è, ma stanno ancora per perdere tempo a esplorare questa possibilità.

Il fatto che l'argomento della posta indesiderata sia implicito può ridurre l'effetto, ma è ancora lì ed è facilmente evitabile rendendo il metodo static .

    
risposta data 10.11.2016 - 21:28
fonte

Leggi altre domande sui tag