Esempio di codice per spiegare il problema di Banana Monkey Jungle di Joe Armstrong [chiuso]

7

Nel libro Coders at work Joe Armstrong ha dichiarato che:

I think the lack of reusability comes in object oriented languages, not in functional languages. Because the problem with object oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle

Non lo capisco proprio qui. Se il problema è ottenere una banana, possiamo racchiudere tutta la logica dietro la funzione 'getBanana'. In che modo la scimmia e la giungla sono coinvolte in questo contesto. Qualcuno potrebbe scrivere uno snippet di codice che spieghi il problema in un modo più semplice da capire, ad esempio, dimostra il fatto che l'oggetto Banana richiede gli oggetti Monkey e Jungle da avviare, per favore?

    
posta Kha Nguyễn 04.04.2018 - 10:19
fonte

3 risposte

9

Sta facendo intuire che la maggior parte dei programmi OOP reali non rispettano la separazione delle preoccupazioni. Ad esempio, potresti avere classi:

public class Banana
{
    public Monkey Owner {get;}
}

public class Monkey
{
    public Jungle Habitat {get;}
}

public class Jungle
{
}

Se utilizzi Banana , è necessario in transito anche dipendere da Monkey e Jungle .

Ma non sono d'accordo sul fatto che questo è un problema con OOP e che lo stile funzionale in qualche modo non ce l'ha. Questo può essere facilmente risolto in OOP con l'introduzione dell'astrazione giusta.

Il problema riguarda maggiormente gli sviluppatori che non si preoccupano della separazione delle preoccupazioni. E non avrei paura di affermare che la maggioranza dei programmatori OOP sono novizi, mentre i programmatori funzionali hanno una certa esperienza, che li motiva a separare correttamente il loro codice.

Possibile astrazione potrebbe essere:

public class Banana
{
    public IBananaOwner Owner {get;}
}

public interface IBananaOwner
{
}

public class Monkey : IBananaOwner
{
    public Jungle Habitat {get;}
}

public class Jungle
{
}

In questo modo, sai che Banana ha il proprietario, ma non deve essere Monkey . Può essere qualsiasi cosa. E limita cosa può fare Banana con il proprietario alle sole operazioni definite da IBananaOwner , il che semplifica il ragionamento.

    
risposta data 04.04.2018 - 12:00
fonte
9

I gorilla non sono scimmie!

Lasciando da parte questo, rispondi alla tua stessa domanda con " possiamo incapsulare tutta la logica dietro la funzione 'getBanana' ". Tutto quello che voglio è una banana, ma per ottenerlo ho bisogno di chiamare getBanana su qualche oggetto, ad esempio un'istanza della classe Gorilla . Questo oggetto banale probabilmente contiene un riferimento al gorilla a cui appartiene e quell'oggetto gorilla avrà a sua volta un riferimento alla foresta a cui appartiene. Quindi chiedo una banana, ma dietro a questa è racchiusa l'intera giungla.

È un esempio estremo e non sarà sempre così male. Ma non è raro finire con un sistema OO come questo. Quindi, per testare il metodo getBanana , ho bisogno di creare un'istanza, o di deridere, un'intera foresta.

    
risposta data 04.04.2018 - 10:32
fonte
-4

Decostruiamo questo aforisma con un esempio usando un codice esistente (la funzione di Fibonacci è la nostra banana qui). Userò qui C # e ML visto che ho incrociato le spade con questi in passato, ma lo stesso vale per praticamente qualsiasi OO e linguaggio funzionale.

Va notato che questi potrebbero essere stati raccolti in modo da essere praticamente identici. Ma sto cercando di identificare le differenze generali di approccio con ogni paradigma. Gli stili di sviluppo possono variare considerevolmente (nonostante gli standard di codifica della casa).

ML

fun fibonacci n =
  if n < 3 then
    1
  else
    fibonacci (n-1) + fibonacci (n-2)

C #

 public static int Fibonacci(int n)
    {
        int a = 0;
        int b = 1;

        for (int i = 0; i < n; i++)
        {
            int temp = a;
            a = b;
            b = temp + b;
        }
        return a;
    }

Quindi cosa sta succedendo qui. La prima cosa da notare è che ML non richiede dichiarazioni variabili (o tipi, sebbene li abbia). Questa è una caratteristica dei linguaggi funzionali. La valuta dei linguaggi funzionali è funzioni che rendono il codice molto conciso, altamente verificabile e riutilizzabile.

La seconda cosa è che i linguaggi funzionali si prestano molto bene alla ricorsione, facendo di nuovo codice più conciso. Gli sviluppatori OO potrebbero essere più inclini a utilizzare un loop qui. Questo non vuol dire che i linguaggi OO non facciano ricorsione (lontano da esso), ma questi devono essere creati con cura per fermare lo stack overflow e simili.

Che ne dici di test allora? Bene, qui il contrasto è ancora più strong:

ML

fibonacci (10)

C #

Console.WriteLine(MyClass.Fibonacci(10));

Poiché la valuta dei linguaggi funzionali è la funzione, il risultato viene semplicemente restituito (in quanto può esso stesso inserirsi in una funzione). Con C # la classe (o la sua istanza) deve essere referenziata (un gorilla). Abbiamo anche bisogno di un codice ( Console.WriteLine ) per segnalare il risultato al mondo esterno (una foresta).

Gorilla e foresta potrebbero ugualmente essere altre cose come oggetti che devi costruire per arrivare alla banana, e spazi dei nomi ecc. Il punto è che ci sono degli strati che devono essere navigati per arrivare alla banana.

    
risposta data 04.04.2018 - 11:02
fonte

Leggi altre domande sui tag