Множественное наследование, полиморфизм и новые способы программирования

StackOverflow https://stackoverflow.com/questions/1637347

  •  07-07-2019
  •  | 
  •  

Вопрос

Раз и навсегда я хочу прояснить эту несколько субъективную и спорную область программирования.

Множественное наследование

В моей нынешней рабочей среде у меня есть разработчики на C ++ и C #, которые происходят из совершенно разных миров и, следовательно, имеют разные мнения о макете программирования.

Будучи самим разработчиком на C # и Java, я никогда не приходил в такое состояние, когда мне действительно нужно было использовать множественное наследование, но разработчики C ++ вокруг меня склонны выдавать комментарии типа " Это был бы идеальный способ использовать множественное наследование. наследование & Quot;

Конечно, я склонен не соглашаться.

Мой вопрос

В каком сценарии множественное наследование будет лучшим или более простым способом решения проблемы, чем использование интерфейсов и простое наследование?

И всегда ли вы можете решить преимущества множественного наследования, используя вместо этого переменные-члены?

Это было полезно?

Решение

За исключением того, что вы, похоже, предлагаете использовать множественное наследование.

Наследование используется по-разному. Открытое наследование определяет отношение IS-A, а наследование с любыми модификаторами доступа может обеспечить поведение дочернего класса, которое затем может быть переопределено. В C # и Java (с которыми я более знаком) интерфейсы предоставляют интерфейс IS-A без поведения. (В C ++ вы можете создать интерфейс, определив класс без членов данных и все функции чисто виртуальные.)

Итак, если вы предоставляете интерфейс и помещаете переменную-член для обеспечения поведения, вы делаете полное наследование в стиле C ++. Вы просто разбиваете его на компоненты и делаете больше хлопот, чтобы это сделать. Описывать это как решение проблемы множественного наследования является неискренним, поскольку выполнение этого с двумя интерфейсами и двумя переменными-членами является множественным наследованием. Это становится еще более неловким, если вы собираетесь изменить поведение полиморфным способом, поскольку вам нужны параллельные иерархии наследования или более сложная диспетчеризация переменной-члена.

Есть проблемы с множественным наследованием в C ++, правда, но есть и такие применения, которые не представляют проблем. Первое хорошее применение, которое я увидел, было «mix-in»: маленькие классы, чтобы добавить определенное поведение. В Java есть множество интерфейсов для них, но вы, как ожидается, сами справитесь с поведением.

Если вы хотите узнать больше о множественном наследовании, я бы предложил попробовать язык, такой как Common Lisp, где множественное наследование обычно используется, и с ним никто не сталкивается.

Другие советы

Многократное наследование - это хорошая возможность. Те, кто писал код с помощью MI, естественно, атакуют проблему под другим углом. Однако то, что «проще» для одного программиста это может быть «сложнее»; для другого.
Информацию о MI для C ++ см. В статье Херба Саттера :

  

Объединение модулей / библиотек

     

Многие классы разработаны, чтобы быть базовыми   классы; то есть использовать их вы   намеревался унаследовать от них.   естественное следствие: что если вы хотите   написать класс, который расширяет два   библиотеки, и вы обязаны   наследовать от класса в каждом? Так как   у вас обычно нет возможности   изменение кода библиотеки (если вы   купил библиотеку у   сторонний поставщик, или это модуль   производится другой командой проекта   внутри вашей компании), MI необходимо.

     

Простота (полиморфного) использования

     

Есть примеры, когда разрешается МИ   значительно упрощает использование того же   объект полиморфно в разных   пути. Один хороший пример можно найти в   C ++ PL3 14.2.2, который демонстрирует   MI-дизайн для классов исключений,   где наиболее производный класс исключений   может иметь полиморфный IS-A   отношения с множеством прямой базы   классы.

При интенсивном использовании двух языков, где один имеет (Python) множественное наследование, а другой - нет (C #), я могу честно сказать, что я никогда не использовал или имел потребность в MI.

В большинстве случаев я предпочитаю интерфейсы + композицию как обычному, так и / или множественному наследованию. Конечно, в некоторых случаях вам действительно нужно наследование, особенно в C #, но MI? Никогда.

Изменить . Было бы проще ответить, если бы вы показали пример, когда ваши программисты на C ++ защищали MI, а вы - нет.

Правка . Подумав немного об этом, я понял, что разница между статически типизированным языком, таким как C # или C ++, и языком с утиной типизацией, таким как Python, может быть хорошей причиной, которую мне никогда не приходилось использовать MI в Python, просто из-за его динамического характера. Но, тем не менее, я никогда не нуждался в этом в C #.

Я не часто использовал множественное наследование, но иногда это бывает удобно, потому что оно просто хорошо работает и снижает затраты на обслуживание. C ++ FAQ Lite имеют несколько хороших сценариев.

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

Выше приведен Visual C ++ с множественным наследованием.

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

}

Но в C # SalesManager множественное наследование не поддерживается языком.

Лично я счастлив потерять множественное наследство.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top