请教一个关于类设计方面的问题 (100分)

  • 主题发起人 Jonix_fu
  • 开始时间
J

Jonix_fu

Unregistered / Unconfirmed
GUEST, unregistred user!
VCL 是一类单继承的类,不能以两类以上的类作为父类继承一个新的子类。
那么大家是用什么办法来弥补这个不足的呢?
 
可以通过com的接口聚合来实现多类的继承,或者通过把一个类作为另一个类的域成员。
 
强烈推荐采用接口来实现多重继承的功能。
建议看看电子工业出版社的《Java与模式》——非常好!
 
Java 与 VCL ?
楼上俩者都是答非所问呀,
我问的是 C++ 和 Delphi的问题。
 
例如有两个类class1,class2;
定义一个新类class3,
class3=class (class1)
private
FClass2:TClass2;
.....
...
 
Object Pascal不支持多继承。
但可以用接口来弥补。
据说Delphi.net支持多继承了。
 
如果能多继承
class Keyboard
{
public:
int get();
// 数据读取
};
class Mouse
{
public:
int read();
// 数据读取
};
class IO : public Keyboard, public Mouse
{
public:

};
void main()
{
IO io;
io.get();
// 都可以用
io.read();
// 都可以用
}
如果不能多继承
class IO
{
public:
Keyboard kb;
Mouse ms;
};
void main()
{
IO io;
io.kb.get();
// 多加了 kb // 这样的话, IO 就变成与具体设置有关的方法了
io.ms.read();
// 多加了 ms // 这样的话, IO 就变成与具体设置有关的方法了
}
变通的办法要多写几行代码了:
class IO
{
private:
Keyboard kb;
Mouse ms;
public:
    int get() { return kb->get();
}
    int read() { return ms->read();
}
};
 
>>楼上俩者都是答非所问呀,
>>我问的是 C++ 和 Delphi的问题。
晕倒!我又不是让你看Java,而是看思路。Java有模式,难道Delphi就没有了??
http://www.delphibbs.com/delphibbs/dispq.asp?lid=0596317
Interface 是从 D3/D4 就有的,但是到了 D6 已经发展得如火纯青。
在 MS 方案中,Interface 是 COM 对象的基石,
在 Java 中,Interface 是 多重继承的替代品。
在 Delphi 中,Interface 是上述两者的结合。是用来开发和调用 COM 的手段,
也可以用来增强 Object Pascal 的语言功能。使得 Object Pascal 如 Java 一样可以
用 Interface 来实现多重继承的功能。
please look at "来自:yysun, 时间:2001-9-14 3:58:00, ID:624988"

下面的例子演示了多接口继承以及缺省实现:
type
IMyInterfaceA = interface
function GetNum:Integer;
end;
IMyInterfaceB = interface
function CalNum(const N:Integer):Integer;
end;
IMyInterfaceC = interface
function Big(const N1,N2:Integer):Boolean;
end;
TMyBaseClass=class(TObject,IMyInterfaceB)
function QueryInterface(const IID: TGUID;
out Obj): HResult;
stdcall;
function _AddRef: Integer;
stdcall;
function _Release: Integer;
stdcall;
function CalNum(const N:Integer):Integer;
end;
TMyClass=class(TMyBaseClass,IMyInterfaceA,IMyInterfaceC)
function GetNum:Integer;
function Big(const N1,N2:Integer):Boolean;
constructor Create;
destructor Destroy;
override;
end;

{ TMyBaseClass }
function TMyBaseClass.QueryInterface(const IID: TGUID;
out Obj): HResult;
begin
Result:=0;
end;

function TMyBaseClass._AddRef: Integer;
begin
Result:=0;
end;

function TMyBaseClass._Release: Integer;
begin
Result:=0;
end;

function TMyBaseClass.CalNum(const N: Integer): Integer;
begin
Result:=N*2;
end;

{ TMyClass }
function TMyClass.Big(const N1, N2: Integer): Boolean;
begin
Result:=N1>N2;
end;

constructor TMyClass.Create;
begin

end;

destructor TMyClass.Destroy;
begin

inherited;
end;

function TMyClass.GetNum: Integer;
begin
Result:=123;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
A:TMyClass;
B:IMyInterfaceB;
begin
A:=TMyClass.Create;
B:=A;
ShowMessage('A.GetNum: '+IntToStr(A.GetNum)
+#13#10'A.Big: '+BoolToStr(A.Big(12,34))
+#13#10'B.CalNum: '+IntToStr(B.CalNum(12)));
end;
 
god ,你说的真的是两回事,Windows是以大量的C的少量的C++写的,当然许多地方借鉴C++,
包括了COM,可是Delphi, java, 以及其它许多语言与C++不同 ,它们不能多继承。
但COM不是,COM在写法上就是一种多继承,所以你用COM的思路就没办法回答我的问题。
相形之下,cjc861是明白我的意思的。他的方案也是我很了解的一种解决方案,但是我觉这
种方式让我多写了不少代码,特别是接口比较多时,更是如此
 
我认为楼主的问题主要还是设计思路的问题。比如汽车有发动机和轮子,那么,作为一个
类,TCar需不需要继承TEngine和TWheel两个对象呢?——答案是不应该这样继承,而应该
进行“聚合”——将1个发动机对象和N个轮子对象做为汽车的组成部分,在构造汽车对象的
时候生成。汽车类继承交通工具类,交通工具类继承物体类以及可移动接口,可移动接口中
规定 启动、移动、停止 三个方法。
具体问题具体分析,我认为楼主提出的问题就可以用聚合来处理。您提到的“让我多写了
不少代码”——这根本就是一件微不足道的事情,因为只要Copy+Paste即可,没有任何需要
思考的成分——你完全可以让初中生写,或者自己写一个小工具自动实现聚合中public方法
的声明。
还有一点,如果运气不好,楼主写的两个父类的public方法都是 int get();
——那么在
具体使用中同样还是要加上父类前缀——可见C++只不过在这一步自动完成了而已。
>>public:
>> Keyboard kb;
>> Mouse ms;
这样做比较危险,建议用只读属性。
我以为下面的实现较为妥当:
ICharInput=interface
function getChar:Integer;
end;
IXYInput=interface
function getXY:TPoint;
end;
TKeyboard=class(TObject,ICharInput)
function getChar:Integer;
end;
TMouse=class(TObject,IXYInput)
function getXY:TPoint;
end;
TInput=class(TObject,ICharInput,IXYInput)
private
Keyboard:TKeyboard;
Mouse:TMouse;
public
function getChar:Integer;
function getXY:TPoint;
end;
 
楼上的回答我能理解,但是在这里有了一个小小的误区,那就是如果你用
一个发动机和四个轮子组合一部车,那自然就是一种组合,而不应该是一
种继承。
原因是车子不是发动机,发动机不是车子,车子同样不是轮子,轮子同样
不是车子。而继承这个概念本身就是父和子是同一种,同一类东西。
所以最早的OO书上会常用的例子是:父类好比是一种鸟,子类好比鸽子。
它们都是鸟,但可能是有差异的,再继承下去可能是鸵鸟,追加了一个不
能飞的特性。对吗?
我以前的例子可能不太好,重新举一个:我要造一辆水陆两用车,我就会
考虑同时从车子和船继承一下。
你还会认为应该是组合吗?
 
对于你提出的具体问题,我认为可以用下面的思路解决:
TCar和TShip实际上非常相似——它们都属于交通工具抽象类,有一个油箱、一个发动机、
有移动部件以及载货量、Move能力、速度属性。它们的区别在于:TCar的移动部件是若干个
轮子,而TShip的移动部件是一个或多个螺旋桨;前者只能在陆地上Move,后者只能在水中
Move。如果要构造一个具有它们的综合能力的类,并不需要从TCar和TShip继承(在Delphi
和Java中也不可能实现这种要求——除非你用C++),而只需要从交通工具抽象类进行继承,
和TCar,TShip类似,只要将移动部件变成螺旋桨+轮子,重载Move过程,使之可以在陆地和
水中行驶即可。
《Java与模式》中提到——除非万不得已,不要从具体类继承,而应该从抽象类继承——
我觉得非常正确。
 
Delphi和Java的多继承都是通过接口来实现的
 
精采,很久沒有看到這種討論了.希望各位高手繼續.
受益良多了.繼續關注中.
 
有谁在实际项目中,真正觉得单根继承体系不够用,
而且这种缺撼是Interface的扩充方式也不能弥补的?
OO设计中的一个重要原则就是“组合优于继承”。
这是因为使用“组合技术”只需要知道接口属于黑箱复用,
而且这种关系,是在运行时可以动态替换的。
而继承技术是白箱复用,继承一个类,不仅需要知道
知道父类的public的接口,还需要知道protect的借口,
并且这种关系,属于编译时确定,在运行时无法改变。
通过以上分析可以知道。基于组合的代码复用技术,
比基于继承的代码复用技术,更符合“高具能,低偶合”的特点。
组合技术一方面功能强大,一方面有容易实现。但真是因为容易
实现,在讲述面向对象概念的书中,往往被忽略。大多数书都
热衷于讲解 水果,苹果,桔子之类的例子。让大家产生一种感觉,
只有基于继承的才是面向对象。而构造继承树的设计,才是面向
对象的设计。于是就是希望继承技术本身变得越来越强大,例如
单继承不够强,希望多继承等等。其实水果类这样的例子,只适合
讲解 一些具体的技术实现方式,而并不适合作为OO设计方面的例子。
面向对象的设计方法,给了我们一系列设计工具。
一个优秀的工匠知道用什么样的工具干哪一样工作,
而且知道该如何使用它们。如果你拥有的唯一工具就是一把锤子,
那么你就会把整个世界都当作一个钉子。
 
TO: creation-zy,
我的观点是建立在尽量少写代码的基础上,所以从你的继承方式来看,可行性方在是没有
问题的,但是从代码量上看就有问题了。
我从TCar和TShip继承的话,就可以不用再写轮子与螺旋桨的具体实现方式的代码了,而
你的方式应该要重写一下最终的具体实现方式。
另外还有一种可能:假如原先的 TCar 和 TShip设计的不好,它们是完全独立的,并不是
从一个交通工具类来继承的,或者是由于这两个类是我分别从两家公司买来的,所以它们
完全不同,那你的解决方案就完全不可行了,是不是?
TO tuti,
我认为你的说法片面了些,因为组合这个概念在现实生活中是可以很容易被描述和举例的,
然而继承并不是,它就象魔法一样,把一件东西变成了另一件东西。
其实组合与继承最大的差异在于它们面向的用途不大一样,如果你自己开发一个大的组件
包,那其中大部分都是继承的,而发布用户后,用户一般是用组合的方式使用的,所以它
们在用途上是不一样的。
为了让类采用组合方式,那么接口部分的要求会比继承的要求还要高一些,代码量也会增
多不少。
另外,你的回答有些偏离我的问题原意了。
 
我认为讨论归讨论,现实归现实——如果您无法控制整体的设计,或者不想改变不是很好
的现有设计,那么,在Delphi或者Java中,只能采用聚合来实现(MS的C#中都只能采用单根
继承模式,似乎MS并不把多重继承作为C++的亮点而让C#继承之哟)。
至于您提到的代码量问题,我的建议是,写一个小工具,自动完成,或者让廉价劳动力用
C&P大法完成。
 
我就是因为发现许多代码都是重复的,所以才觉得奇怪。其实我自己也没用过多继承方式,
但是我认为多继承除了引起的多义性外,好象找不到它还有什么不好的地方。
 
我不认为组合优于继承。
现代语言都是单根继承的,不约而同的全部放弃多重继承。
例如Object Pascal,Java,C#等等。
多重继承注定要被抛弃到历史的垃圾桶中。
多重继承不符合人们对自然的认识。
老虎就是哺乳动物,天鹅就是鸟。狗和天鹅组合起来成了什么?
而且OOP并没有规定一定要多重继承。
不多说了,不然今晚上要被某些人暗算了
 
顶部