Utilizzando tutti i parametri di una funzione

4

Codice completo (2 ° ed.) dice:

"Se passi un parametro a una routine, usalo. Se non lo stai usando, rimuovi i parametri dall'interfaccia di routine."

Tuttavia, in passato, a volte dovevo farlo. Considera il seguente esempio:

Supponiamo di avere una classe base astratta chiamata Food , contenente una pura funzione virtuale chiamata getEaten . Deriviamo da due classi separate:

class Apple : public Food
{
    void getEaten(const Utensil &utensil);
    // otherstuff
};

e

class Soup : public Food
{
    void getEaten(const Utensil &utensil);
    // otherstuff
};

Quando chiamiamo mySoup.getEaten(spoon) , la funzione utilizza il parametro spoon per eseguire l'azione. Tuttavia, una mela non ha bisogno di un utensile, e in quei casi devo inviare un utensile fittizio a myApple.getEaten(emptyUtensil) .

Sì, è brutto e ha il sovraccarico di creare e passare inutilmente un oggetto fittizio. Posso generalizzare il problema in questo modo:

"Quando abbiamo due concetti, che sono rappresentati come due classi, si applica la stessa azione su uno di essi, e uno ha bisogno di più informazioni rispetto all'altro quando si richiede di eseguire tali azioni, io tendo a passare informazioni fittizie al meno informazioni richiedendo oggetti. "

Quindi le mie domande sono:

1) Come possiamo cambiare il design di questo esempio un po 'sciocco, in modo da non dover passare l'utensile fittizio?

2) Se c'è una semplice soluzione alla prima domanda, ci sarebbe un modo per generalizzarlo al problema generalizzato sopra (come le soluzioni come "in quei casi creare una nuova x in modo che interagisca con la meno informante classe esigente come questa "ecc.)

    
posta loudandclear 21.07.2011 - 09:56
fonte

6 risposte

4

L'esempio è un po 'sciocco, ma se si segue, si potrebbe discutere di uno dei tre casi:

  1. L'azione "mangia" è in realtà un'azione composta che deve essere suddivisa in passaggi (tagliare, raccogliere, mettere in bocca, masticare, deglutire). I passaggi richiesti differiscono per tipo di cibo; a seconda che tu sia un food.beEaten(eater) o un eater.eat(food) type. Il mangiatore dovrebbe sapere quali misure adottare per ogni tipo di cibo (probabilmente usando un EatingProcedureFactory per determinare il IEatingProcedure corretto per il cibo in questione). La procedura di alimentazione a sua volta avrebbe metodi per determinare quali utensili sono richiesti (o, più in generale, un metodo per verificare se i suoi requisiti sono soddisfatti).
  2. In food.beEaten(eater) , controlla se il mangiatore ha tutti gli utensili necessari.
  3. Sii pragmatico e lascia perdere. Non vale la pena di andare al 100% -corrente-OOP, il vantaggio è piccolo e non supera la complessità aggiunta.
risposta data 21.07.2011 - 10:14
fonte
6

Questo esempio ha comunque una soluzione ovvia. Considera di mangiare una bistecca: probabilmente useresti un coltello e una forchetta. Cucchiaio da zuppa. Apple: niente. Quindi, nel caso più generale di pasti, hai bisogno di un elenco di utensili, non di un singolo utensile.

L'elenco, quindi, potrebbe essere vuoto nel caso di una Apple.

    
risposta data 21.07.2011 - 10:36
fonte
1

La classe Soup potrebbe avere un sovraccarico di getEaten che accetta il parametro, mentre la classe Food di base ha la firma del metodo senza il parametro?

interface IFood
{
    void getEaten();
}

class Apple : IFood
{
    void getEaten()
    { /*Stuff */ }
}

class Soup : IFood
{
    void getEaten()
    {
        throw NotImplementedException();
    }

    void getEaten(Utensil utensil)
    { /* Stuff */ }
}
    
risposta data 21.07.2011 - 10:06
fonte
1

In risposta alla tua prima domanda, ciò che puoi fare dipenderà dalla lingua che stai utilizzando. Come soluzione OOP generale, è possibile utilizzare il polimorfismo per fornire una seconda implementazione di getEaten che non richiede un utensile.

//Examples are converted to c#
class Food
{
    public virtual void getEaten(Utensil utensil);
    public virtual void getEaten();
}

class Soup :  Food
{
    public void getEaten(Utensil utensil)
    {
        // consume the soup
    }
    public void getEaten()
    {
        throw new Exception("A spoon is required"); 
    }  

}

A seconda della lingua che stai utilizzando e del tipo che stai trasmettendo nella funzione, un'altra opzione sarebbe un parametro facoltativo con un valore predefinito.

class Soup : Food
{
    // C# only supports compile-time constants as default values
    // so the above example using a Utensil class wouldn't work.
    void getEaten(string utensilType = "Spoon");
    {
        // consume the soup
    }
}

Come opzione finale si potrebbe semplicemente avere il supporto della funzione passato come null, in quanto non è richiesto alcun utensile.

Off di questi suggerirei il primo. Ha anche il vantaggio di supportare ulteriori metodi getEaten possono richiedere due o più utensili.

    
risposta data 21.07.2011 - 10:28
fonte
1

Perché non usare il modello di strategia?

L'esempio fornito è scritto in Java (praticamente succhio al C ++), ma dovrei darti un'idea:

public interface Utensil {
  void apply();
}

public class Spoon implements Utensil {
   public void apply() {
       System.out.println("Using spoon...");
   }
}

public class Food {
    private Utensil utensil;

    public Food(Utensil utensil) {
        this.utensil = utensil;
    }

    public void getEaten() {
        utensil.apply();
    }
}

public class Apple extends Food {
    public Apple() {
        super(new Spoon());
    }
}
    
risposta data 21.07.2011 - 11:41
fonte
1

IMHO, stai prendendo la regola

If you pass a parameter to a routine, use it. If you aren't using it, remove the parameters from the routine interface

semplicemente troppo lontano. L'interfaccia è comune sia a Apple che a Soup , quindi richiede Utensil e non c'è niente di sensato da fare al riguardo. Il fatto che Apple::getEaten non ne abbia effettivamente bisogno è un dettaglio di implementazione.

Quando chiami Food::getEaten devi semplicemente fornire un Utensil e va bene. Puoi fornire un metodo parametrico Apple::getEaten() nel caso in cui sia necessario chiamarlo direttamente e non avere Utensil . Molto probabilmente non dovresti promuovere questo metodo nella superclasse.

Ovviamente, l'utilizzo di List<Utensil> è la soluzione giusta nel caso in cui siano necessari più utensili.

    
risposta data 06.08.2011 - 16:57
fonte

Leggi altre domande sui tag