Come simulare il sovraccarico del metodo in langs senza tale funzione? [chiuso]

0

Langs come Java conosce l'overloading del metodo true :

class Overload {
  void demo (int a) {
   System.out.println ("a: " + a);
  }
  void demo (int a, int b) {
   System.out.println ("a and b: " + a + "," + b);
  }
}

Se langs come PHP - no. Quindi, quando ti trovi di fronte a un problema di elaborazione di qualcosa che dipende dal contesto, a quali vie penserai e quali di solito sceglieranno?

Il tipico problema potrebbe essere un sistema di pagamento astratto, in cui l'utente esterno non dovrebbe pensare a dettagli concreti del fornitore di pagamenti (che potrebbero essere diversi) e chiamare semplicemente Provider.pay(PayDetails) . Nell'interfaccia di PaymentProvider dobbiamo correggere che funziona con PayDetails. Ma nelle implementazioni concrete il fornitore concreto potrebbe lavorare, ad esempio, solo con il proprio ConcretePayDetails.

Vale la pena di aggiungere polifrati come il polimorfismo ad-hoc nei lang che non lo supportano?

Come risolvere meglio i metodi relativi al contesto che vengono inviati in tali lingue?

Esempio di implementazione possibile disponibile su link

    
posta gaRex 02.01.2016 - 00:51
fonte

2 risposte

8

Il polimorfismo ad-hoc (metodo "sovraccarico") è una caratteristica abbastanza insignificante di un linguaggio e non lo rende più espressivo. Cioè non c'è letteralmente nulla di sovraccarico che ti permetta di fare ciò di cui non puoi fare a meno (ignorando la programmazione generica). Tutto ciò che fa è fornire una sintassi di scelta rapida.

Quando una lingua con polimorfismo ad-hoc (come Java) compila una chiamata a Overload#demo(…) , vede i diversi overload void demo(int) e void demo(int, int) come funzioni completamente differenti . Per risolvere la chiamata, il compilatore associa la firma della chiamata agli overload disponibili e seleziona la corrispondenza migliore. Questo può essere re-implementato in un linguaggio dinamico controllando il numero e il tipo di argomenti. Quando il polimorfismo ad-hoc viene risolto in base ai tipi di argomento durante il runtime, questo è in realtà più espressivo del polimorfismo ad-hoc e potrebbe essere chiamato multi -methods invece.

Ma non abbiamo bisogno di metaprogrammi elaborati per ottenere l'equivalente del polimorfismo ad-hoc. Invece, possiamo semplicemente dare un nome diverso a ciascun overload e richiedere al programmatore di selezionare il sovraccarico corretto durante la scrittura del codice. Questo è esattamente equivalente alle cose che il compilatore Java farebbe, anche se chiaramente più incline agli errori. A volte lo vedi nelle API C dove potrebbe esserci una funzione something(int) e something2(int, int) dove abbiamo diversi numeri di argomenti, o int atoi(const char*) e double atof(const char*) dove abbiamo tipi diversi. Nel tuo esempio, la tua interfaccia potrebbe essere implementata come Provider.payWithPayDetails(PayDetails) anziché Provider.pay(PayDetails) .

Quindi, se il polimorfismo ad-hoc è praticamente inutile, perché le lingue hanno questa caratteristica? Ci sono due ragioni principali:

  • In Java, il polimorfismo ad-hoc ci consente di simulare argomenti facoltativi e di fornire più costruttori con diverse firme. Per esempio. potremmo inizializzare un punto da tre numeri Point(double x, double y, double z) , o come costruttore di copie da un altro punto Point(Point orig) . Nella mia esperienza, sarebbe stato meglio per il linguaggio fornire argomenti opzionali effettivi e suggerire l'uso di funzioni factory statiche opportunamente denominate invece di sovraccaricare i costruttori. Nota che PHP ha veri argomenti opzionali, e non c'è bisogno di simularli con il polimorfismo ad-hoc.

  • Solitamente l'overloading dell'operatore viene implementato utilizzando il polimorfismo ad-hoc, poiché non è possibile assegnare un nome diverso allo stesso operatore solo per utilizzarlo per tipi diversi. Non vorrai avere +int e +double (sebbene OCaml lo faccia effettivamente con + e +. operatori, penso). In un senso più ampio, qualsiasi funzione può essere vista come un operatore (o almeno un'operazione) su un certo valore. Questo è abbastanza utile quando si scrive codice generico, ad es. con i modelli C ++ ("argument-dependent lookup") o i generici di Haskell. Soprattutto con Haskell, potremmo avere dozzine di definizioni per una funzione globale come fmap , ma questo può essere risolto durante la compilazione in una singola istanza. Tuttavia, questo caso d'uso non si applica a Java, dove le interfacce sono l'unico meccanismo per definire i tipi astratti.

risposta data 02.01.2016 - 01:37
fonte
0

Penso che tu abbia problemi di concepimento qui. Stai dicendo che vuoi avere un metodo generico di "paga". Il problema è che questo metodo sarà sempre specifico per il fornitore di servizi di pagamento. Ad esempio, il fornitore di PayPal non può accettare CreditCardDetails come argomento per "pagare". E viceversa.

Suggerirei un approccio "fabbrica". Potrebbe non essere il migliore in questo caso, ma risolve il "problema" di sovraccarico. Questa è solo una bozza grezza che ha bisogno di più lavoro ma mostra uno dei possibili approcci.

interface PaymentProvider
{
    public function pay();
}

class PayPalProvider implements PaymentProvider
{
    private $details;

    public function __construct(PayPalPayDetails $details)
    {
        $this->details = $details;
    }

    public function pay()
    {
        //Do something with $this->details
    }
}

class ProviderFactoriesCollection
{

}

interface ProviderFactory
{
    /**
     * @returns PaymentProvider
     */
    public function create(array $data);
}

class PayPalProviderFactory implements ProviderFactory
{
    public function create(array $data)
    {
        // Do the magic here
    }
}

class PaymentProviderFactory
{
    private $providerFactoriesCollection;

    public function __construct(ProviderFactoriesCollection $providerFactoriesCollection)
    {
        $this->providerFactoriesCollection= $providerFactoriesCollection;
    }

    /**
     * @returns PaymentProvider
     */
    public createFromInput($type, array $details)
    {
        return $this->getFactoryForType($type)->create($details);
    }

    private function getFactoryForType($type)
    {
        // Find in collection or throw exception if not supported.
    }
}

Tieni anche presente che esistono linguaggi che ignorano completamente l'overloading dei metodi. C, Objective-C, Rust, Ruby, Go e così via. E non è necessariamente una brutta cosa.

    
risposta data 03.01.2016 - 13:33
fonte

Leggi altre domande sui tag