多态
1什么是多态?
1.1概念
多态的概念众说纷纭,下面是几种代表性的说法:
“This ability to manipulate more than one type with a pointer or a reference to a base classis spoken of as polymorphism” (《C++ Primer》第838页)。即用基类的指针/引用来操作多种类(基类和其派生类)的对象的能力称之为多态。它是从语言实现的角度来考虑的。
“polymorphism provides another dimension of separation of interface from implementation, to decouple what from how”(《Think in Java》3rd edtion),即多态提供了另外一种分离接口和实现(即把“做什么”与“怎么做”分开)的一种尺度。它是从设计的角度考虑的。
“The ability to use the same expression to denote different operations is refered to as Polymorphism”,(《Object-Oriented Methods Principles & Practice》3rd Edition,第16页)。简单的说,多态就是“相同的表达式,不同的操作”,也可以说成“相同的命令,不同的操作”。这是从面向对象的语义的角度来看的。
三种说法分别从不同的角度来阐述了多态的实质。其中第三种说法尤为确切,下面着重分析第三种说法。
先解释这句话的含义:
相同的表达式—函数调用
不同的操作 —根据不同的对象就有不同的操作。
举个例子来说明,比如在公司中有各种职责不同的员工(程序员,业务员,文管等),他们“上班”时,做不同的事情(也可以看作是一种业务逻辑),我们把他们各自的工作都抽象为"上班",关系如下:
员工
/ | \ ——继承关系
程序员业务员文管
每天上班时间一到,相当于发了一条这样的命令:
“员工们.开始上班”(同一条表达式)
每个员工接到这条命令(同样的命令)后,就“开始上班”,但是他们做的是各自的工作,程序员就开始“Coding”,业务员就开始“联系业务”,文管员就开始“整理文档”。即“相同的表达式(函数调用),(在运行期根据不同的对象来执行)不同的操作
下面我们来用Delphi实现这个实例
Type
Employee=Class(Tobject)//员工基类
public
procedure Work;Virtual;abstract;//开始工作的过程
end;
Programmor=Class(Employee)//程序员类--继承自员工类
public
procedure Work;override;//程序员的工作过程
end;
BusinessMan=Class(Employee)//业务员类--继承自员工类
public
procedure Work;override;//业务员的工作过程
end;
DocManager=Class(Employee)//文档管理员类-----继承自员工类
public
procedure Work;override;//文档管理员的工作过程
end;
procedure Programmor.Work;//程序员的具体工作过程实现
begin
Showmessage('程序开始Coding');
end;
procedure BusinessMan.Work;//业务员的具体工作过程实现
begin
Showmessage('业务员开始联系业务');
end;
procedure DocManager.Work;//文档管理员的具体工作过程实现
begin
Showmessage('文档管理员开始整理文档');
end;
//上班了,各位员工开始工作
procedure TForm1.btn2Click(Sender: TObject);
const
Num=3;
var
emp:arrayof Employee;
i:Integer;
begin
//实现多态
setLength(emp,Num);
emp[0]:=Programmor.Create;
emp[1]:=BusinessMan.Create;
emp[2]:=DocManager.Create;
for i:=0to length(emp)-1do
emp[i].Work;//分别调用各员工的具体的工作过程
end;
1.2多态的意义
封装和继承的意义是它们实现了代码重用,而多态的意义在于,它实现了接口重用(同一的表达式),接口重用带来的好处是程序更易于扩展,代码重用更加方便,更具有灵活性,也就能真实地反映现实世界。
比如为了更好地管理,把程序员分为C++程序员,Delphi程序员。…
员工
/ | \ ——继承关系
程序员业务员文管
/ \ ——继承关系
C++程序员 Delphi程序员
代码如下:
Type
CppProgarmmer=class(Progarmmer)//c++程序员,继承自程序员类
public
procedure Work;override;//c++程序员的工作过程
end;
DelphiProgarmmer=class(Progarmmer)//delphi程序员,继承自程序员类
public
procedure Work;override;//delphi程序员的工作过程
end;
procedure CppProgarmmer.Work;
begin
Showmessage('C++ 程序员开始高度Bug!');
end;
procedure DelphiProgarmmer.Work;
begin
Showmessage('Delphi 程序员开始Coding!');
end;
//现在重新调用
//上班了,各位员工开始工作
procedure TForm1.btn2Click(Sender: TObject);
const
Num=5;
var
emp:arrayof Employee;
i:Integer;
begin
//实现多态
setLength(emp,Num);
emp[0]:=Programmor.Create;
emp[1]:=BusinessMan.Create;
emp[2]:=DocManager.Create;
emp[3]:=CppProgarmmer.Create;
emp[4]:=DelphiProgarmmer.Create;
for i:=0to length(emp)-1do
emp[i].Work;//分别调用各员工的具体的工作过程
end;
多态的实质是:相同的表达式,不同的操作(就这么简单),
从OOP语言的实现来讲,多态就是使用基类的指针/引用来操作(派生类)对象,在运行期根据实际的对象,来执行不同的操作方法;
或者换一种更形象的说法:由对象自己来决定自己操作方式,编译器只需下达做什么的命令(做什么what),而不要管怎么做(how),"怎么做"由为对象自己负责。
这样就实现了接口和实现的分离,使接口重用变得可能
其实多态也简单!那么使用多态应该注意什么呢?下面是一些建议:
分析业务逻辑,然后把相关的事物抽象为“对象”,再用对象方法封装业务逻辑。把一些具有多态性的操作,在基类中声明为虚方法(virtual Method),
对于在基类没有必要实现的就声明为抽象方法(virtual Abstract Method),然后在其派生类中再覆载它(Override),在使用的时候用基类的引用/指针来调用,
这样顺理成章地实现了现实世界中的多态性。记住千万不要为了多态,而去实现多态,这是一种走形式化的做法,是没有意义的。
由于基类与派生类有一种天然“耦合”关系,修改基类就会导致“牵一发而动全身”,这将是非常麻烦的事情!因此要尽量弱化基类的功能实现,
必要时把它设计为“抽象类”,并保证稳定的接口,这可以通过预留一些冗余的虚函数(或抽象函数)来实现