多重継承、ポリモーフィズム、およびプログラミングの新しい方法
-
07-07-2019 - |
質問
一度、すべての人のために、プログラミングのこのやや主観的で議論的な分野を明確にしたい。
複数の継承
現在の作業環境には、まったく異なる世界から来たC ++開発者とC#開発者がいます。したがって、プログラミングレイアウトについて異なる意見があります。
C#とJavaの開発者である私は、多重継承を実際に使用する必要がある状態には至りませんでしたが、私の周りのC ++開発者は、"継承"
もちろん、私は反対する傾向があります。
私の質問
インターフェースを使用して単純な継承を使用するよりも、どのシナリオで多重継承が問題を解決するための優れたまたは簡単な方法でしょうか?
そして、代わりに、つまりメンバー変数を使用することで、多重継承の利点を常に解決できますか?
解決
多重継承の使用を提案していると思われる場合を除きます。
継承にはさまざまな用途があります。パブリック継承はIS-A関係を指定します。アクセス修飾子を使用した継承は、子クラスに動作を提供できます。この動作はオーバーライドできます。 C#とJava(私はよく知っている)では、インターフェイスはIS-Aインターフェイスを動作なしで提供します。 (C ++では、データメンバーとすべての関数を純粋仮想でクラスを定義することにより、インターフェイスを実行できます。)
したがって、インターフェイスを提供し、動作を提供するためにメンバー変数に入れると、完全なC ++スタイルの継承を行うことになります。あなたはそれをコンポーネントに分割し、それを行うのにより多くの手間をかけているだけです。これを多重継承の問題を解決すると説明することは不誠実です。なぜなら、2つのインターフェイスと2つのメンバー変数でそれを行うことは多重継承だからです。並列継承階層またはメンバー変数へのより複雑なディスパッチが必要なため、ポリモーフィックな方法で動作を変更しようとすると、さらに厄介になります。
C ++の多重継承には問題がありますが、問題はない用途もあります。私が最初に見た良い使用法は、「ミックスイン」:特定の動作を追加する小さなクラスです。 Javaにはこれらのインターフェイスがたくさんありますが、自分で動作を処理することが期待されています。
多重継承の詳細に興味がある場合は、多重継承が日常的に使用されており、誰も問題を抱えていないCommon Lispなどの言語を試すことをお勧めします。
他のヒント
複数の継承は優れた機能です。 MIを使用してコードを記述するのに使用した人たちは、自然と異なる角度から問題を攻撃します。ただし、「簡単」なのはあるプログラマにとっては、「より難しい」かもしれません。別の。
MIに関するC ++固有の情報については、 Herb Sutterの記事をご覧ください。
モジュール/ライブラリの組み合わせ
多くのクラスはベースになるように設計されています クラス;つまり、それらを使用するには それらから継承することを目的としています。の 自然な結果:あなたが望むなら 2を拡張するクラスを書く ライブラリ、およびする必要があります それぞれのクラスから継承しますか?なぜなら 通常、次のオプションはありません ライブラリコードの変更( からライブラリを購入した サードパーティベンダー、またはモジュール 別のプロジェクトチームが作成 社内で)、MIが必要です。
使いやすさ(多態性)の使用
MIを許可する例があります 同じものを使用して大幅に簡素化 異なる多形のオブジェクト 方法。 1つの良い例は C ++ PL3 14.2.2は、 例外クラスのMIベースの設計、 最も派生した例外クラス 多態性IS-Aを持っている可能性があります 複数の直接ベースとの関係 クラス。
このSOトピックをご覧ください: C#に複数の継承を含めるべきですか?
2つの言語を広範囲に使用し、一方は(Python)多重継承を使用し、もう一方は使用しない(C#)正直に言って、私は決して使用していないか、MIを必要としていると言えます。
ほとんどの場合、通常の継承および/または多重継承の両方よりもインターフェイス+コンポジションを好む傾向があります。確かに、特にC#では継承が本当に必要な場合がありますが、MIですか?決して。
編集:C ++プログラマーがMIを提唱し、そうしなかった例を示すことができれば、答えるのは簡単です。
編集:少し考えてみると、C#やC ++などの静的に型付けされた言語とPythonのようなアヒル型言語との違いが、使用する必要がなかった正当な理由であることがわかりましたPythonのMIは、その動的な性質のためです。しかし、それでも、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の多重継承では言語はサポートされていません。
個人的に、多重継承を失うことを嬉しく思います。