Domanda

Una volta per tutte voglio chiarire questa area di programmazione in qualche modo soggettiva e argomentativa.

Eredità multipla

Nel mio attuale ambiente di lavoro ho sviluppatori C ++ e sviluppatori C # che provengono da mondi totalmente diversi e quindi hanno opinioni diverse sul layout di programmazione.

Ora essendo uno sviluppatore C # e Java, non sono mai arrivato allo stato in cui avevo effettivamente bisogno di utilizzare l'ereditarietà multipla, ma gli sviluppatori C ++ intorno a me tendono a leggere commenti come " Sarebbe un modo perfetto per usare Multiple ereditarietà "

Certo che tendo a dissentire.

La mia domanda

In quale scenario l'ereditarietà multipla sarebbe un modo migliore o più semplice per risolvere un problema rispetto all'uso delle interfacce e alla semplice eredità?

E puoi sempre risolvere i molteplici vantaggi dell'ereditarietà usando invece le variabili membro?

È stato utile?

Soluzione

Tranne che sembra che tu stia proponendo di utilizzare l'ereditarietà multipla.

L'ereditarietà ha usi diversi. L'ereditarietà pubblica specifica una relazione IS-A e l'ereditarietà con qualsiasi modificatore di accesso può fornire un comportamento alla classe figlio, che può quindi essere ignorata. In C # e Java (con cui ho più familiarità), le interfacce forniscono l'interfaccia IS-A senza comportamento. (In C ++, puoi fare un'interfaccia definendo una classe senza membri di dati e tutte le funzioni pure virtuali.)

Quindi, se si fornisce un'interfaccia e si inserisce una variabile membro per fornire il comportamento, si sta eseguendo un'eredità completa in stile C ++. Lo stai solo dividendo in componenti e stai prendendo più problemi per farlo. Descrivere questo come risolvere il problema dell'ereditarietà multipla è disonesto, poiché farlo con due interfacce e due variabili membro è ereditarietà multipla. Diventa ancora più imbarazzante se hai intenzione di modificare il comportamento in modo polimorfico, dal momento che hai bisogno di gerarchie ereditarie parallele o di un invio più complicato alla variabile membro.

Ci sono problemi con l'ereditarietà multipla C ++, vero, ma ci sono anche usi che non presentano problemi. Il primo buon uso che ho visto è stato "mix-in": piccole classi per aggiungere determinati comportamenti. Ci sono molte interfacce per queste in Java, ma ci si aspetta che tu gestisca il comportamento da solo.

Se sei interessato a saperne di più sull'ereditarietà multipla, suggerirei di provare una lingua, come Common Lisp, in cui l'ereditarietà multipla viene abitualmente utilizzata e nessuno ha problemi con essa.

Altri suggerimenti

L'ereditarietà multipla è una funzionalità interessante. Coloro che erano soliti scrivere codice con MI, naturalmente attaccano il problema da un'angolazione diversa. Tuttavia, ciò che è "più facile" per un programmatore, potrebbe essere "più difficile" per un altro.
Per informazioni specifiche su C ++ sull'MI consultare l'articolo di Herb Sutter :

  

Combinazione di moduli / librerie

     

Molte classi sono progettate per essere di base   classi; cioè, per usarli sei   destinato a ereditare da loro. Il   conseguenza naturale: cosa succede se si desidera   per scrivere una classe che ne estende due   librerie, e sei tenuto a   ereditare da una classe in ciascuno? Perché   di solito non hai la possibilità di   cambiando il codice della biblioteca (se tu   acquistato la biblioteca da a   fornitore di terze parti o è un modulo   prodotto da un altro team di progetto   all'interno della tua azienda), MI è necessario.

     

Facilità d'uso (polimorfico)

     

Ci sono esempi in cui consentire l'MI   semplifica notevolmente l'utilizzo dello stesso   oggetto polimorficamente in diversi   modi. Un buon esempio si trova in   C ++ PL3 14.2.2 che dimostra un   Progettazione basata su MI per classi di eccezione,   dove una classe di eccezione più derivata   può avere un IS-A polimorfico   relazione con più basi dirette   classi.

Dai un'occhiata a questo argomento SO: C # dovrebbe includere l'eredità multipla?

Avendo ampiamente usato due lingue in cui una ha (Python) eredità multipla e una no (C #) posso onestamente dire che non ho mai usato mai o ho avuto bisogno di MI.

Tendo a preferire interfacce + composizione nella maggior parte dei casi rispetto sia all'eredità normale che all'ereditarietà multipla. Certo ci sono alcuni casi in cui hai davvero bisogno dell'eredità, specialmente in C # - ma MI? Mai.

Modifica : sarebbe più facile rispondere se potessi mostrare un esempio in cui i tuoi programmatori C ++ hanno sostenuto l'MI e tu no.

Modifica : pensandoci un po 'di più mi sono reso conto che la differenza tra un linguaggio tipizzato staticamente come C # o C ++ e un linguaggio tipizzato come anatra come Python potrebbe essere una buona ragione che non ho mai dovuto usare MI in Python, proprio per la sua natura dinamica. Tuttavia, non ne ho mai avuto bisogno in C # mai.

Non ho usato molto l'ereditarietà multipla, ma a volte è utile perché funziona bene e riduce i costi di manutenzione. Le risposte C ++ FAQ Lite hanno alcuni scenari positivi.

#include <afx.h>      
#include <afxtempl.h>
#include "StdAfx.h"  

class Employee
{
    char name[30];
public:
    Employee() {}
    Employee( const char* nm )
    {
        strcpy( name, nm ) ;
    }
    char* getName() const;
    virtual double computePay() const = 0;
    virtual ~Employee() 
    {
    }
};

class WageEmployee : public virtual Employee
{
    double wage;
    double hours;
public:
    WageEmployee( const char* nm );
    void setWage(  double wg  ){wage  = wg; }
    void setHours( double hrs ){hours = hrs;}

    double computePay() const       /* Implicitly virtual */
    {
        return wage * hours;
    }
};

class SalesPerson : public WageEmployee
{
      double commission; 
      double salesMade;
public:
      SalesPerson( const char* nm );
      void setCommission( double comm )
      {
          commission = comm;
      }
      void setSales( double sales )
      {
          salesMade = sales;
      }

      double computePay() const     /* Implicitly virtual */
      {
          return WageEmployee::computePay() + commission * salesMade;
      }
};

class Manager : public virtual Employee
{
    double weeklySalary; 
public:
    Manager( const char* nm );

    void setSalary( double salary ){weeklySalary = salary; }

    double computePay() const           /* Implicitly virtual */
    {
        return weeklySalary;
    }
};

class SalesManager : public SalesPerson, public Manager
{
public:
    SalesManager::SalesManager( const char* nm )
                :Employee(nm),Manager(nm),SalesPerson(nm)
    {
    }

    double computePay() const       /* Implicitly virtual */
    {
        return Manager::computePay() + SalesPerson::computePay();
    }
};

typedef CTypedPtrList < CPtrList, Employee* > CEmployeeList;

class EmployeeList
{
    CEmployeeList List;
public:
    EmployeeList() {}
    CEmployeeList& GetElements() { return List; }

    void Add( Employee* newEmp )
    {
        List.AddTail( newEmp ) ;        
    }

    virtual ~EmployeeList() 
    {
        POSITION pos = List.GetHeadPosition() ;
        while ( pos != NULL ) 
        {
            delete List.GetNext(pos) ;
        }
        List.RemoveAll() ;
    }
};


WageEmployee::WageEmployee( const char* nm )
            :Employee( nm )
{
    wage  = 0.0;
    hours = 0.0;
}

SalesPerson::SalesPerson( const char* nm )
           :WageEmployee( nm )
{
    commission = 0.0;
    salesMade  = 0.0;
}

Manager::Manager( const char* nm )
       :Employee( nm )
{
    weeklySalary = 0.0;
}

void main( int argc, char *argv[] )
{
    int ans = 0 ;

EmployeeList  myDept;
WageEmployee* wagePtr;
SalesPerson*  salePtr;
Manager*      mgrPtr;
SalesManager* smPtr;

wagePtr = new WageEmployee("Alan Wage");
salePtr = new SalesPerson("Brian Sale");
mgrPtr  = new Manager("Clive Manager");
smPtr   = new SalesManager("David SaleManager");

wagePtr->setWage( 10.0 );
wagePtr->setHours( 35.0 );

salePtr->setWage( 5.0 );
salePtr->setHours( 35.0 );
salePtr->setCommission( 0.05 );
salePtr->setSales( 100.0 );

mgrPtr->setSalary( 600.0 ) ;

smPtr->setSalary( 670.0 ) ;
smPtr->setCommission( 0.01 );
smPtr->setSales( 100.0 );


myDept.Add( wagePtr );
myDept.Add( salePtr );
myDept.Add( mgrPtr );
myDept.Add( smPtr );

double payroll = 0.0 ;
Employee* person ;
POSITION pos = myDept.GetElements().GetHeadPosition() ;
while ( pos != NULL )
{
    person = (Employee* )myDept.GetElements().GetNext(pos) ;
    payroll += person->computePay();
}

ExitProcess( ans ) ;
}

Sopra è Visual C ++ con un'eredità multipla.

using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;

namespace ConsolePoly
{
   interface Employee
   {
       string Name { get; set; }
       double computePay();
   }

class WageEmployee : Employee 
{
    private string iName = "";
    virtual public string Name { get { return iName; } set { iName = value; } }

    public double wage { get; set; }
    public double hours { get; set; }

    public WageEmployee(string nm)
    {
        Name = nm;
        wage = 0.0;
        hours = 0.0;
    }

    virtual public double computePay() { return wage * hours; }     /* Implicitly virtual c++ */
}

class SalesPerson : WageEmployee
{
    public double commission { get; set; }
    public double salesMade { get; set; }

    public SalesPerson(string nm)
        : base(nm)
    {
        commission = 0.0;
        salesMade = 0.0;
    }

    override public double computePay()
    {
        return base.computePay() + commission * salesMade;
    }   /* Implicitly virtual c++ */
}

class Manager : Employee
{
    private string iName = "";
    virtual public string Name { get { return iName; } set { iName = value; } }
    public double weeklySalary { get; set; }

    public Manager(string nm)
    {
        Name = nm;
        weeklySalary = 0.0;
    }
    public Manager()
    {
        weeklySalary = 0.0;
    }

    public double computePay() { return weeklySalary; }     /* Implicitly virtual c++ */
}

class SalesManager : Manager, /*SalesPerson,*/ Employee 
{            
    public SalesManager(string nm)
    {
        Name = nm;
        //SalesPerson(nm);
        //commission = 0.01;
        //salesMade = 100.0;
    }

    public double computePay() { return base.computePay();/*Manager.computePay()+SalesPerson.computePay();*/ }       
}

class EmployeeList
{
    private List<Employee> list = new List<Employee>();
    public List<Employee> GetElements() { return list; }
    public void Add(Employee newEmp) { list.Add(newEmp);}
    public EmployeeList() {}
}


class poly
{
    public poly()
    {

    }

    public virtual void generate()
    {
        EmployeeList  myDept = new EmployeeList();

        WageEmployee wagePtr = new WageEmployee("Alan Wage");
        SalesPerson salePtr = new SalesPerson("Brian Sale");
        Manager mgrPtr  = new Manager("Clive Manager");
        SalesManager salemanPtr = new SalesManager("David SaleMan");

        wagePtr.wage=10.0;
        wagePtr.hours=35; 

        salePtr.wage= 5.0 ;
        salePtr.hours= 35.0 ;
        salePtr.commission= 0.05 ;
        salePtr.salesMade= 100.0 ;

        mgrPtr.weeklySalary=600.0;
        salemanPtr.weeklySalary = 670;

        myDept.Add( wagePtr );
        myDept.Add( salePtr );
        myDept.Add( mgrPtr );
        myDept.Add( salemanPtr );

        double payroll = 0.0 ;

        List<Employee> list = myDept.GetElements();

        foreach (Employee person in list)
        {
            Console.WriteLine("personName( \"{0}\" )\tcomputePay( \"{1}\" )", person.Name, person.computePay());
            payroll += person.computePay();
        }
        Console.WriteLine("computePay( \"{0}\" )\n", payroll.ToString() );

    }

    static void Main(string[] args)
    {
        try
        {
            new poly().generate(); //new poly(args[0], args[1]).generate();  
        }
        catch (IndexOutOfRangeException ioe)
        {
            System.Console.Error.WriteLine(ioe);
        }
    }
}

}

Ma in C # l'ereditarietà multipla di SalesManager non è supportata dalla lingua.

Personalmente sono felice di perdere l'eredità multipla.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top