Che cosa sono i messaggi polimorfici? [duplicare]

3

Sto leggendo " Rifattorizzazione: migliorare il design del codice esistente di Martin Fowler. Non ho capito una sezione del secondo capitolo in cui Kent Beck descrive i pro di riferimento indiretto.

Uno dei professionisti elencati è Codifica Logica condizionale ed è descritto come:

Objects have a fabulous mechanism, polymorphic messages, to flexibly but clearly express conditional logic. By changing explicit conditionals to messages, you can often reduce duplication, add clarity, and increase flexibility all at the same time.

Che cosa sono i messaggi polimorfici? Cosa intende per "cambiare condizionali espliciti nei messaggi"?

    
posta mridula 26.01.2016 - 12:28
fonte

2 risposte

2

Un altro termine potrebbe essere " invio dinamico ". Di seguito è riportato un esempio che utilizza C # (un po 'non idiomatico)

Supponiamo di avere il seguente tipo di elenco:

public abstract class List<T> {
    public abstract bool isEmpty();
    public abstract T head();
    public abstract List<T> tail();
}

class Empty<T> : List<T> {
    public Empty() { }
    public override bool isEmpty() { return true; }
    public override T head() { 
        throw new ApplicationException("head of empty list"); 
    }
    public override List<T> tail() { 
        throw new ApplicationException("tail of empty list"); 
    }
}

class Cons<T> : List<T> {
    private T head;
    private List<T> tail;
    public Cons(T head, List<T> tail) {
        this.head = head;
        this.tail = tail;
    }
    public override bool isEmpty() { return false; }
    public override T head() { return head;  }
    public override List<T> tail() { return tail; }
}

Potresti implementare una funzione di lunghezza come segue:

int length(List<T> xs) {
    if(xs.isEmpty()) { 
        return 0; 
    } else {
        return 1 + length(xs.tail());
    }
}

In alternativa, utilizzando l'invio dinamico come suggerisce Kent Beck, potremmo eliminare il condizionale aggiungendo length come metodo di List<T> .

public abstract class List<T> {
    /* ... */
    public abstract int length();
}

class Empty<T> : List<T> {
    /* ... */
    public override int length() {
        return 0;
    }
}

class Cons<T> : List<T> {
    /* ... */
    public override int length() {
         return 1 + tail.length();
    }
}

Ciò aumenta la flessibilità perché ora possiamo aggiungere un'altra classe con la sua implementazione di length che può essere più intelligente della nostra funzione length .

class Concat<T> : List<T> {
    private List<T> left, right;
    public Concat(List<T> left, List<T> right) {
        this.left = left;
        this.right = right;
    }
    /* ... */
    public override int length() {
        return left.length() + right.length();
    }
}

Per quanto riguarda un po ', ci sono problemi con la definizione di length in Cons<T> che causerà l'utilizzo di molto spazio nello stack e probabilmente produrrà un overflow dello stack in uso reale. Il problema è che non è scritto in un modulo ricorsivo di coda . Questo è facilmente corretto, ma anche se corretto, dal momento che praticamente nessun linguaggio OO garantisce l'eliminazione delle chiamate tail, i benefici che Kent Beck menziona non possono essere raggiunti in questo esempio. In altre parole, la mancanza di un'eliminazione di chiamata di coda ostacola in modo significativo uno dei principali vantaggi dello stile OO.

    
risposta data 26.01.2016 - 13:36
fonte
1

Quando si invia un messaggio a un oggetto, l'oggetto è libero di rispondere al messaggio come desidera. In particolare, oggetti diversi possono rispondere allo stesso messaggio in modo diverso: questa è l'essenza del polimorfismo ad-hoc.

Fare cose diverse è ovviamente anche ciò che riguarda i condizionali. Quindi, i messaggi polimorfici sono una forma di condizionale e in quanto risulta molto potente. Ecco un esempio di implementazione di condizionali tradizionali (ad esempio if / then / else ) utilizzando i messaggi polimorfi in Ruby , il il suo senso si riduce a questo:

class TrueClass
  def iff(thn: ->{}, els: ->{})
    thn.()
  end
end

class FalseClass
  def iff(thn: ->{}, els: ->{})
    els.()
  end
end

(2 < 3).iff(thn: -> { puts '2 is less than 3' }, els: -> { puts '2 is greater than 3' })
# 2 is less than 3

E un esempio leggermente più completo in Scala:

sealed abstract trait Buul {
  def apply[T, U <: T, V <: T](thn: => U)(els: => V): T
  def &&&(other: => Buul): Buul
  def |||(other: => Buul): Buul
  def ntt: Buul
}

case object Tru extends Buul {
  override def apply[T, U <: T, V <: T](thn: => U)(els: => V): U = thn
  override def &&&(other: => Buul) = other
  override def |||(other: => Buul): this.type = this
  override def ntt = Fls
}

case object Fls extends Buul {
  override def apply[T, U <: T, V <: T](thn: => U)(els: => V): V = els
  override def &&&(other: => Buul): this.type = this
  override def |||(other: => Buul) = other
  override def ntt = Tru
}

object BuulExtension {
  import scala.language.implicitConversions
  implicit def boolean2Buul(b: => Boolean) = if (b) Tru else Fls
}

import BuulExtension._

(2 < 3) { println("2 is less than 3") } { println("2 is greater than 3") }
// 2 is less than 3

Noterai che questa codifica orientata agli oggetti di condizionali e booleani è esattamente la stessa della Codifica della Chiesa dei condizionali e dei booleani nel calcolo λ. Non è una coincidenza: OO riguarda l'astrazione comportamentale, e il calcolo λ non supporta l'astrazione della funzione ma (cioè il comportamento), inducendo Cook a chiamarlo "il primo linguaggio OO", solo a metà scherzando.

    
risposta data 26.01.2016 - 14:32
fonte

Leggi altre domande sui tag