Código de ejemplo para explicar el problema de Banana Monkey Jungle por Joe Armstrong [cerrado]

7

En el libro Codificadores en el trabajo Joe Armstrong declaró que:

  

Creo que la falta de reutilización viene en objetos orientados   Idiomas, no en lenguajes funcionales. Debido a que el problema con el objeto orientado   idiomas es que tienen todo este entorno implícito que llevan   alrededor con ellos. Querías un plátano pero lo que conseguiste fue un gorila.   sosteniendo el plátano y toda la selva

No lo consigo del todo aquí. Si el problema es obtener una banana, podemos encapsular toda la lógica detrás de la función 'getBanana'. ¿Cómo se involucran el mono y la selva en este contexto? ¿Podría alguien escribir un fragmento de código que explique el problema de una manera más fácil de entender, por ejemplo, para demostrar el hecho de que el objeto Banana requiere que se inicien los objetos Monkey y Jungle , por favor?

    
pregunta Kha Nguyễn 04.04.2018 - 10:19

3 respuestas

9

Está insinuando un hecho, que la mayoría de los programas OOP reales no respetan la separación de preocupaciones. Por ejemplo, podrías tener clases:

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

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

public class Jungle
{
}

Si usas Banana , es necesario que dependa también de Monkey y Jungle .

Pero estrictamente no estoy de acuerdo en que esto sea un problema con la POO y que el estilo funcional de alguna manera no lo tenga. Esto puede solucionarse fácilmente en OOP con la introducción de la abstracción correcta.

El problema es más sobre los desarrolladores que no se preocupan por la separación de preocupaciones. Y no me atrevería a afirmar que la mayoría de los programadores OOP son novatos, mientras que los programadores funcionales tienen alguna experiencia, que los motiva a separar adecuadamente su código.

La posible abstracción podría ser:

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

public interface IBananaOwner
{
}

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

public class Jungle
{
}

De esta manera, sabe que Banana tiene propietario, pero no tiene que ser Monkey . Puede ser cualquier cosa. Y limita lo que Banana puede hacer con el propietario a solo las operaciones definidas por IBananaOwner , lo que simplifica el razonamiento.

    
respondido por el Euphoric 04.04.2018 - 12:00
9

¡Los gorilas no son monos!

Dejando eso de lado, respondes a tu propia pregunta con " podemos resumir toda la lógica detrás de la función 'getBanana' ". Todo lo que quiero es un plátano, pero para obtenerlo necesito llamar a getBanana en algún objeto, por ejemplo, una instancia de la clase Gorilla . Ese objeto de banana probablemente contiene una referencia al gorila al que pertenece y ese objeto de gorila a su vez tendrá una referencia al bosque al que pertenece. Así que pido un plátano, pero encapsulado detrás está toda la jungla.

Es un ejemplo extremo y no siempre será tan malo. Pero no es raro terminar con un sistema OO como este. Y así, para probar ese método getBanana , necesito crear una instancia, o simular, un bosque completo.

    
respondido por el David Arno 04.04.2018 - 10:32
-4

Vamos a deconstruir este aforismo a través de un ejemplo usando algún código existente (la función de Fibonacci es nuestra banana aquí). Voy a usar C # y ML aquí, ya que he cruzado espadas con estos en el pasado, pero lo mismo se aplica a casi cualquier OO y lenguaje funcional.

Cabe señalar que estos podrían haber sido seleccionados como prácticamente idénticos. Pero estoy buscando identificar las diferencias generales en el enfoque con cada paradigma. Los estilos de desarrollador pueden variar considerablemente (a pesar de los estándares de codificación internos).

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

Entonces, ¿qué está pasando aquí? Lo primero que hay que notar es que ML no requiere declaraciones de variables (o tipos, aunque las tenga). Esta es una característica de los lenguajes funcionales. La moneda de los lenguajes funcionales es una función que hace que el código sea muy conciso, altamente comprobable y reutilizable.

Lo segundo es que los lenguajes funcionales se prestan muy bien para la recursión, lo que nuevamente hace un código más conciso. Los desarrolladores de OO pueden estar más inclinados a usar un bucle aquí. Eso no quiere decir que los lenguajes OO no hagan recursión (ni mucho menos), sino que deben diseñarse con cuidado para detener los desbordamientos de pila y similares.

¿Qué hay de las pruebas entonces? Bueno, aquí el contraste es aún más marcado:

ML

fibonacci (10)

C#

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

Como la moneda de los lenguajes funcionales es la función, el resultado simplemente se devuelve (ya que puede incorporarse a una función). Con C #, la clase (o su instancia) debe ser referenciada (un gorila). También necesitamos algún código ( Console.WriteLine ) para informar el resultado al mundo exterior (un bosque).

Gorila y bosque también pueden ser otras cosas, como los objetos que necesitas construir para llegar al plátano, y los espacios de nombres, etc. El punto es que hay capas que deben ser navegadas para llegar al plátano.

    
respondido por el Robbie Dee 04.04.2018 - 11:02

Lea otras preguntas en las etiquetas