如果我在基类上实现一个接口它会被它的子类继承,我知道函数/过程会是,但我更感兴趣的是我是否能够将子类强制转换为接口然后回到原来的班级。
我希望能做的是将不同基类的对象传递给函数,然后在函数确定中输入并根据需要使用它们。
这是可能的,这是正确的方法吗?
更新
帮助消除任何混乱(或创造更多)这是我想做的事情(条纹下到核心)。
接口
IMyInterFace = interface
['{B7203E50-F5CB-4755-9DB1-FB41B7B192C5}']
function MyFunction: Boolean;
end;
基类
type TMyObject = class(TInterfacedObject,IMyInterFace)
子类
type TSubMyObject = class(TMyObject)
另一个班级
type TMyOtherObject = class(TInterfacedObject,IMyInterFace)
然后用法
procedure foo();
var obj:TSubMyObject;
begin
obj := TSubMyObject.Create();
SendInterfaceObject(obj);
end;
procedure bar();
var obj:TMyOtherObject;
begin
obj := TMyOtherObject.Create();
SendInterfaceObject(obj);
end;
procedure SendInterfaceObject(iobj:IMyInterFace);
begin
if (iobj is TSubMyObject) then
begin
//code here
end
else if (iobj is TMyOtherObject) then
begin
//code here
end;
end;
更新2
我更新了代码abit,以便更好地展示我的目标。
这里的 //代码部分与传递给它的对象几乎没有关系,例如,如果这个类是TAccounts并且它被传递了一个TEmployee对象,它可能会支付每周的工资但是如果它是TInvoice,那么它会检查是否需要付款,并且只在日期是死线前2天付款。
TEmployee / TInvoice甚至可能来自外派,要求付款。
这只是一个例子。
答案 0 :(得分:9)
是的,接口由子类继承。
从子类转换到接口是完全可以接受的。
然而,如果我错误地阅读你的问题而道歉,但如果“然后回到原来的班级”就意味着。 。
你有接口I,A类和B类。 一个实现I,而B继承A,你可以,但是真的不应该从A转换为B.
修改强>
你想从B到I再回到B. 。 。但是你已经有了B的引用,如果B是你传递给你的函数的,那么你不需要从I转换为B(除非是在讨论不同的对象,然后是否,不要这样做)< / p>
从I到B与从A到B的过程相同,你试图建立继承链,这是你不应该做的事情。需要这样做是代码气味,它告诉你应该尝试以不同的方式解决这个问题(可能通过重新设计你的类(例如向我添加更多的属性/方法),或者只是决定该函数只能工作使用子类 - 使用子类'B'将允许您访问A&amp; I)的所有方法。
您可以编辑您的问题并添加一些您尝试做的示例代码吗?
编辑2
procedure SendInterfaceObject(iobj:IMyInterFace);
begin
if (iobj is TSubMyObject) then
begin
//code here
end;
end;
那里的'If'语句有一个坏主意,并且打破了OO主体。如果你需要这样做,那么
编辑3:
@mghie:我同意你的意见,我没有解释得很好,SomeProperty有一些数据允许函数在那里分支,消除了类型检查的依赖性。 SomeProperty不应该“简单地”替换类型检查(例如,通过将类名放在属性中,然后检查类名)这确实是完全相同的问题。
继承接口的子类之间存在一些本质区别。这种差异应该由
表示e.g。
if(item.Color = Red) then
item.ContrastColor := Blue;
else
item.ContrastColor := Red;
IEmployee定义了一个CalculatePay方法,TManager和TWorker实现了IEmployee,每个方法在CalculatePay方法中都有不同的逻辑。
如果意图是做第一种情况,那么多态可能过度杀戮(多态不能解决所有问题)。
编辑4
你说“这里的//代码部分与传递给它的对象几乎没有关系......”我很抱歉,但是如果你需要支付那个声明是不正确的一个员工,你需要知道他们1)EmployeeCode 2)他们的工资细节3)他们的银行详细信息等,如果你收取您需要的发票1)InvoiceNumber 2)发票金额3)CustomerCode收取等。 。 。 这是多态性的理想场所。
让我们说接受界面的功能检查“账户”是否需要对该对象做一些事情(例如,支付员工,收取发票等)。所以我们可以调用AccountCheck函数。在内部账户检查中,您将拥有特定于每个子类的逻辑(支付员工,收取发票......)这是多态性的理想候选者。
在您的界面上(或在另一个界面上,或作为子类上的虚方法)定义“AccountsCheck”方法。然后每个派生类都有自己的Accounts check实现。
代码移出你单一的AccountCheck函数,并转移到每个子类的较小函数中。这使得代码
还有更多,“很好的理由”,如果有人想编辑/发表评论,请这样做。
如果您发现需要在AccountsCheck的实现之间共享一些逻辑,请创建一些实用程序函数,不要在每个函数中重新实现相同的轮。
多态性是解决问题的方法。
答案 1 :(得分:2)
我的建议是不要对该类进行强制转换,而是反对另一个接口。将TMyOtherObject更改为:
type
IOtherObjectIntf = interface
['{FD86EE29-ABCA-4D50-B32A-24A7E71486A7}']
end;
type
TMyOtherObject = class(TInterfacedObject,IMyInterFace,IOtherObjectIntf)
然后将您的其他例程更改为:
procedure SendInterfaceObject(iobj:IMyInterFace);
begin
if Supports(iObj,IOtherObjectIntf) then
begin
//code here for TMyOtherObject
end
else
begin
//code here for other standard implementations
end;
end;
这样,TMyOtherObject的“自定义”代码也将应用于任何ITS后代,而无需任何进一步的自定义代码。 IOtherObjectIntf接口仅用作“是的,我是其中一个”指示器,它允许您的代码正确分支。当然,它给另一个Guid产生了浪费......但是有很多人会注意到它们? :)
答案 2 :(得分:1)
接口由子类继承,您可以将对象强制转换为接口,但将接口强制转换为类是不安全的(或推荐)。如果你需要这样做,你可能错误地使用接口。
答案 3 :(得分:1)
对于如何理解您的问题似乎存在疑问,事实上在您对this answer的评论中,您说要“从B到I到B”。
这确实不推荐,只有使用有关如何在类上实现接口的信息才支持。
如果我理解正确,那么你想要做的是将接口传递给某个方法,并且在该方法中根据接口实现的具体类做不同的事情。但是,一旦开始使用接口,最好继续使用接口。你可以让接口有一个返回实现类的方法,但是你不应该对接口实现的类做出任何假设 - 它会让你花费一些编程对接口的好处。 / p>
您可以做的是创建不同的接口,并仅在(某些)后代类中实现其中一些接口。然后,您可以在传递的接口指针上使用 QueryInterface()或 Supports()。对于你的基类,这将返回 nil ,但对于实现该接口的所有后代类,它将返回一个指针,让你只调用它们拥有的方法。
编辑:例如,在OmniThreadLibrary中,您会找到:
IOmniWorker = interface
['{CA63E8C2-9B0E-4BFA-A527-31B2FCD8F413}']
function GetImplementor: TObject;
...
end;
您可以添加到您的界面。但同样,恕我直言,使用不同的接口要好得多。
答案 4 :(得分:1)
你不能直接将一个接口转换为一个对象(它不是那个接口的目的),但有时它能够做到这一点很实际,你无法抗拒......
如果您真的想这样做,可以直接在IMyInterFace中使用mghie给出的“IOmniWorker”示例:
IMyInterFace = interface
['{B7203E50-F5CB-4755-9DB1-FB41B7B192C5}']
function MyFunction: Boolean;
function GetImplementor: TObject;
end;
函数实现看起来像这样:
function TMyObject.GetImplementor: TObject;
begin
Result := Self;
end;
function TMyOtherObject.GetImplementor: TObject;
begin
Result := Self;
end;
SendInterfaceObject看起来就像那样:
procedure SendInterfaceObject(const iobj:IMyInterFace);
begin
if (iobj.GetImplementor is TSubMyObject) then
begin
//code here
end
else if (iobj.GetImplementor is TMyOtherObject) then
begin
//code here
end;
end;
顺便说一下,我添加了一个(非常)小的优化:通过将iobj作为“const”传递给函数,可以避免不必要的引用计数。