Quali sono i Mixins (come concetto)

Sto cercando di ottenere la mia testa intorno al Mixin concetto, ma non riesco a capire di cosa si tratta.
A mio modo di vedere è che è un modo per espandere le funzionalità di una classe utilizzando l’ereditarietà.
Ho letto che le persone si riferiscono a loro come “astratto sottoclassi”. Qualcuno può spiegare perché?

Ti sarei grato se devi spiegare la tua risposta in base al seguente esempio (Da uno dei miei conferenza presentazioni):
Quali sono i Mixins (come concetto)

  • Un quadro che fa un uso pesante di mixins è Apache Arazzo per le applicazioni web Java. Leggere la documentazione e guardare alcuni esempi di Arazzi e forse si sarà in grado di vedere i paralleli/modelli di ciò che si sta vedendo in C++ esempio. Ecco un link: tapestry.apache.org/component-mixins.html
  • Avevo completamente pensato che si stava parlando di Ruby basta uno sguardo del titolo…
InformationsquelleAutor Shookie | 2013-09-12



6 Replies
  1. 110

    Prima di andare in quello che un mix-in, è utile descrivere i problemi che cerca di risolvere. Diciamo che avete un sacco di idee o concetti che si sta tentando di modello. Essi possono essere in qualche modo correlati, ma sono ortogonali per la maggior parte-il che significa che si può stare da soli in modo indipendente. Ora si potrebbe modello di questo attraverso l’ereditarietà e ognuno di quei concetti che derivano da alcuni comuni classe di interfaccia. Quindi è fornire strumenti concreti in derivati di classe che implementa l’interfaccia.

    Il problema con questo approccio è che questa struttura non offre alcuna chiara e intuitiva per prendere ciascuna di tali classi concrete e combinarli insieme.

    L’idea con mix-ins è quello di fornire un sacco di classi primitive, dove ognuno di essi i modelli di una base ortogonale concetto, e di essere in grado di attaccare insieme per comporre più complesse classi con solo le funzionalità che si desidera-un po ‘ come i lego. Le classi primitive stessi sono destinati ad essere utilizzati come blocchi di costruzione. Questo è estensibile, dal momento che in seguito si possono aggiungere altre classi primitive per la raccolta senza modificare quelli esistenti.

    Tornando al C++, una tecnica per farlo è con l’utilizzo di modelli e di eredità. L’idea di base è che si collega questi blocchi di costruzione, offrendo loro tramite il parametro del modello. Quindi catena insieme, ad esempio. via typedef, per formare un nuovo tipo contenente le funzionalità che si desidera.

    Prendendo il tuo esempio, diciamo che si desidera aggiungere un ripristino funzionalità. Ecco come potrebbe sembrare:

    #include <iostream>
    using namespace std;
    
    struct Number
    {
      typedef int value_type;
      int n;
      void set(int v) { n = v; }
      int get() const { return n; }
    };
    
    template <typename BASE, typename T = typename BASE::value_type>
    struct Undoable : public BASE
    {
      typedef T value_type;
      T before;
      void set(T v) { before = BASE::get(); BASE::set(v); }
      void undo() { BASE::set(before); }
    };
    
    template <typename BASE, typename T = typename BASE::value_type>
    struct Redoable : public BASE
    {
      typedef T value_type;
      T after;
      void set(T v) { after = v; BASE::set(v); }
      void redo() { BASE::set(after); }
    };
    
    typedef Redoable< Undoable<Number> > ReUndoableNumber;
    
    int main()
    {
      ReUndoableNumber mynum;
      mynum.set(42); mynum.set(84);
      cout << mynum.get() << '\n';  //84
      mynum.undo();
      cout << mynum.get() << '\n';  //42
      mynum.redo();
      cout << mynum.get() << '\n';  //back to 84
    }

    Si noterà che ho fatto un paio di modifiche dall’originale:

    • Le funzioni virtuali, in verità, non necessaria, perché sappiamo esattamente che cosa il nostro composto per il tipo di classe è a tempo di compilazione.
    • Ho aggiunto un default value_type per il secondo modello param per rendere il suo utilizzo meno ingombrante. In questo modo non è necessario digitare <foobar, int> ogni volta che ti infili un pezzo insieme.
    • Invece di creare una nuova classe che eredita da i pezzi, un semplice typedef è utilizzato.

    Di notare che questo è destinato ad essere un semplice esempio per illustrare il mix-in idea. In modo da non prendere in considerazione casi particolari e divertenti usi. Per esempio, l’esecuzione di un undo senza mai segnare un numero probabilmente non si comportano come ci si potrebbe aspettare.

    Come una nota a margine, si potrebbe anche trovare in questo articolo utile.

    • Questo esempio è in realtà molto buono, ho letto e sono rimasto sorpreso di trovare che fatto un sacco di senso lolol. Ben fatto mate. Grazie.
    • Una nota per i lettori, void Number::set(int) e int Number::get() const dovrebbe essere virtual per ottenere il mixin comportamento quando si utilizza un Number* puntatore.
    • Mantenere la classe di base set e get virtuale ha un senso, perché si può quindi definire void doubler(Number &n){ n.set(n.get()*2); } e di essere in grado di utilizzare con annullabili e reundoable classi
    • Un suggerimento: quando Number::value_type è già definito, potrebbe (e dovrebbe) essere utilizzato anche per Number::n, Number::get e Number::set.
    • Sulla base della mia comprensione del ragionamento in questo post, sembra lo stesso ragionamento può essere usato per dire che i nativi/tipi di base di qualsiasi linguaggio di programmazione (ad es. int, std::string, char, ecc in C++) sono essi stessi mixins, giusto?
  2. 7

    Mixin è una classe dessigned per fornire funzionalità di un’altra classe, di norma, attraverso una specifica classe che fornisce le funzionalità di base che le esigenze di funzionalità. Per esempio, si consideri il tuo esempio:

    Il mixin in questo caso fornisce la funzionalità di annullamento dell’operazione di impostazione di un valore di classe. Questo maggior traspirabilità è basato sul get/set funzionalità fornite da una parametrizzazione di classe (Il Number classe, nel tuo esempio).

    Un altro esempio (Estratta da Mixin a base di programmazione in C++):

    template <class Graph>
    class Counting: public Graph {
      int nodes_visited, edges_visited;
    public:
      Counting() : nodes_visited(0), edges_visited(0), Graph() { }
      node succ_node (node v) {
        nodes_visited++;
        return Graph::succ_node(v);
      }
      edge succ_edge (edge e) {
        edges_visited++;
        return Graph::succ_edge(e);
      }
    ... 
    };

    In questo esempio, il mixin fornisce le funzionalità di contare i vertici, dato un grafo classe che esegue operazioni trasversali.

    Comunemente, in C++ mixins sono attuate attraverso la CRTP idioma. Questo thread potrebbe essere una buona lettura su un mixin implementazione in C++: Che cosa è il C++, Mescolandosi Stile?

    Qui è un esempio di un mixin che sfrutta il CRTP idioma (Grazie a @Semplici):

    #include <cassert>
    #ifndef NDEBUG
    #include <typeinfo>
    #endif
    
    class shape
    {
    public:
        shape* clone() const
        {
            shape* const p = do_clone();
            assert(p && "do_clone must not return a null pointer");
            assert(
                typeid(*p) == typeid(*this)
                && "do_clone must return a pointer to an object of the same type"
            );
            return p;
        }
    
    private:
        virtual shape* do_clone() const = 0;
    };
    
    template<class D>
    class cloneable_shape : public shape
    {
    private:
        virtual shape* do_clone() const
        {
            return new D(static_cast<D&>(*this));
        }
    };
    
    class triangle : public cloneable_shape<triangle>
    {
    };
    
    class square : public cloneable_shape<square>
    {
    };

    Questo mixin fornisce le funzionalità di eterogenei copia per un set (gerarchia) di forma le classi.

    • L’esempio non è CRTP a tutti.
    • oops, è vero 🙂
    • Ma mi è piaciuto il tuo commento riferimento a CRTP. Perché mixins sono approssimati con CRTP in C++.
    • sì, è per questo che ho postato il commento. Ma come avete notato, OP esempio non uso CRTP a tutti. Ecco perché ho rimosso il commento più tardi.
    • puoi spiegare perché la gente lo chiama “eredità senza sub-tipo polimorfismo”? Io non sono ancora del tutto sicuro di aver capito come questo sia diverso da una classe astratta che aggiunge la funzionalità di tutti le classi derivate.
    • In CRTP la classe base è un modello che assomiglia a base<derived>, quindi non si può avere un contenitore di base* oggetti. Inoltre, una classe astratta utilizza virtual funzione che comportano sovraccarico di runtime; con CRTP si utilizza static_cast chiamare le funzioni membro in classe derivata che vengono risolti in modo statico a tempo di compilazione.
    • Qualcuno ha un bel esempio di vita reale che è di facile comprensione, il che sarebbe finita questa risposta e una serie di commenti fuori bene, credo.
    • ok, un esempio
    • Esempio di un CRTP mixin: ideone.com/V7MsAS
    • grazie, ho aggiunto che per la risposta. Il tuo esempio fornisce la maggior traspirabilità di “essere clonabile” di una classe, giusto? Eterogenei copia è il termine corretto? Non ne sono sicuro.
    • Esso consente di definire classi con un -bis rapporto con il shape classe, ma derivanti da cloneable_shape implementa automaticamente il clone funzione membro per voi, in modo da non dovete scrivere voi stessi per ogni classe si deriva.
    • questo è quello che ho capito io, ma non ero del tutto sicuro circa il termine. Grazie comunque

  3. 5

    Mi piace la risposta da greatwolf, ma di offrire un punto di attenzione.

    greatwolf ha dichiarato, “Le funzioni virtuali, in verità, non necessaria, perché sappiamo esattamente che cosa il nostro composto per il tipo di classe è a tempo di compilazione.” Purtroppo, si può incorrere in qualche comportamento incoerente se si utilizza l’oggetto polymorphically.

    Mi permetta di modificare la funzione principale dal suo esempio:

    int main()
    {
      ReUndoableNumber mynum;
      Undoable<Number>* myUndoableNumPtr = &mynum;
    
      mynum.set(42);                //Uses ReUndoableNumber::set
      myUndoableNumPtr->set(84);    //Uses Undoable<Number>::set (ReUndoableNumber::after not set!)
      cout << mynum.get() << '\n';  //84
      mynum.undo();
      cout << mynum.get() << '\n';  //42
      mynum.redo();
      cout << mynum.get() << '\n';  //OOPS! Still 42!
    }  

    Facendo il “set” funzione virtuale, il corretto ignorare verrà chiamato e il comportamento incoerente di cui sopra non si verifica.

  4. 0

    Questo funziona come un’interfaccia e forse di più, in modo astratto, ma le interfacce sono più facili da ottenere prima volta.

    Risolve molti problemi, ma quello che ho trovare in via di sviluppo che viene su un lotto api esterne. immaginate questo.

    Si dispone di un database di utenti, il database ha un certo modo di ottenere l’accesso ai suoi dati.
    ora immaginate di avere facebook, che ha anche un certo modo di ottenere l’accesso ai dati (api).

    in qualsiasi momento la tua applicazione potrebbe essere necessario eseguire utilizzando i dati di facebook o il tuo database. così che cosa dovete fare è creare un’interfaccia che dice “tutto ciò che implementa mi sta per sicuramente hanno i seguenti metodi” ora, è possibile implementare l’interfaccia nelle applicazioni…

    perché l’interfaccia promesse che l’implementazione di archivi avrà i metodi dichiarati in loro, sai che ovunque o ogni volta che si utilizza l’interfaccia dell’applicazione, se si passa i dati, si sta andando sempre di avere i metodi per la definizione e quindi avere i dati per lavorare fuori di.

    Ci sono molti più livelli di questo modello di lavoro, ma la sostanza è che è un bene perché, dati o altri tali persistenti elementi di diventare una grande parte della vostra applicazione, e se cambiano senza che tu lo sappia, l’applicazione può rompere 🙂

    Ecco alcuni pseudo-codice.

    interface IUserRepository
    {
        User GetUser();
    }
    
    class DatabaseUserRepository : IUserRepository
    {
        public User GetUser()
        {
            //Implement code for database
        }
    }
    
    class FacebookUserRepository : IUserRepository
    {
        public User GetUser()
        {
            //Implement code for facebook
        }
    }
    
    class MyApplication
    {
        private User user;
    
        MyApplication( IUserRepository repo )
        {
            user = repo;
        }
    }
    
    //your application can now trust that user declared in private scope to your application, will have access to a GetUser method, because if it isn't the interface will flag an error.
    • Il codice simile a java, e non come il c++. Inoltre, come sono mixins diverso dal normale classi astratte?
    • ah beh, io C#, PHP e C++… mi hanno detto che sono molto simili all’astratto, ma trovo difficile spiegare il loro utilizzo… considerando che ho ancora li uso un sacco e un sacco :). Ogni volta che cerco di venire con un buon esempio di ereditarietà, mi fanno piangere. Il codice è stato quello di dimostrare più facile con prolisse, sintassi, ma non un vero e proprio codice, pseudo ho detto 🙂 spero che qualcuno mi risponda è molto più chiara, ma a me personalmente piace di più prolisso risposte, trovo più utile e un buon esempio di vita reale troppo ^_^
    • Ok, mi chiedevo. Dopo la lettura di questo, ho cercato in rete, e non riuscivo a trovare una spiegazione decente. Io ancora non capisco cosa sono i mixins :/
    • Capisco che tu abbia buone intenzioni, ma la tua spiegazione non rende l’immagine più sfocata di quello che già era. Per non parlare del fatto, che ti sei perso il “C++” di parte.
  5. 0

    Per capire il concetto dimenticare classi per un momento. Pensare (più popolare) di JavaScript. Dove gli oggetti sono matrici dinamiche di metodi e proprietà. Callable con il loro nome come simbolo o come una stringa letterale. Come implementare in C++ standard in un anno 2018? Non facilmente. Ma che è il nucleo del concetto. In JavaScript si possono aggiungere e rimuovere (aka mix-in) ogni volta che è e ciò che si desidera. Molto importante: Non ereditarietà di classe.

    Ora su C++. In C++ Standard e dispone di tutto il necessario, non serve come una dichiarazione di qui. Ovviamente non voglio scrivere un linguaggio di scripting per attuare mix-in, utilizzando il C++.

    Sì, questo è un buon articolo , ma per l’ispirazione. CRTP non è una panacea. E anche il cosiddetto approccio accademico è qui, anche (in sostanza) CRTP base.

    Prima del down-voto questa risposta forse prendere in considerazione il mio p.o.c. codice bacchetta box 🙂

Lascia un commento