面向对象的设计原则之一:针对接口编程,而不是针对实现编程 (100分)

>要想针对接口编程,就必然要最大化接口类,使包括所有子类的方法,这样我们才能利用
>多态性用接口类来一致的操作子类
在定义接口类时,应定义尽可能少的接口。(因为一旦发布,再想收回就难了)
可以这样说:
每个接口类只定义其必须完成的操作。
--还有,似乎楼主把接口与继承有点儿混淆了 :)
 
TO wab
是的,我是有点混,你是否能详细说说你的想法?
谢谢!
 
对于接口编程,我觉得广义的接口编程就是定义对象的属性、事件、公共方法,对外部程序只提供这些东西,相当于说,你申明了一个类的属性,这个属性就可以看成是这个类的一个接口函数。
当然,自己定义一个Interface,然后用一个或几个类来实现,那就是一般意义上的接口编程。
>要想针对接口编程,就必然要最大化接口类,使包括所有子类的方法,这样我们才能利用
对于说这一点,我是建议你可以定义几个接口,你的类实现这几个接口就行了,接口应该是合乎逻辑的一组函数的定义,而不是一个大杂烩。
 
那针对我的那个例子来说,我应该怎么做?
先谢谢你的指导!
 
C#里边比如窗体的控制等等,好像都是通过各种接口实现的
接口应该是合乎逻辑的一组函数的定义,这点是非常正确和重要的
复制,剪切,粘贴,
,重命名,查看属性、设置它的图标,添加到树节点上等等。每种节点的相同操作(比如复制)的实现是不同的。
ITNProperty = Interface(IInterface)
[CTRL+G]
property NodtText: String read .. write ..;
property NodeIcon: ..(通过HICON or Index)
end;

ITNOperation = Interface(IInterface)
[CTRL+G]
Function CanDelete
Function CanCopy
Function CanCopyTo
Function CanMoveTo
Function CanSubCopyFrom
Function CanSubMoveFrom
end;

ITNClipboard = ...(模拟剪贴版,整个Tree的借口)
[CTRL+G]
end;
 
to zjan521
能不能具体讲讲,谢谢!
 
我总觉得你的理解有偏差,真对接口编成应是针对某一特定用途而制定的不同对象之间访问规范,不应该让接口最大化。
你举的例子,我认为较适合用接口实现,
但我不明白你为什么这么说?(“如果不放到父类中,就没办法以统一的方法操作所有的子类了!”)
如果你要放到父类中就没有用接口的必要了,不如声明成虚方法。
首先看:
如果A 和 B类 不是同一父类 但他们有一些其同的事物要做
这时可以用接口 I 来实现
AI = class(A,i);
..实现借口方法
BI = class(B,i);
..实现借口方法
当接口对象访问时
I可以=AI
I也可以=BI
这时调用I接口分别访问AI类方法或BI类方法
如果你将其放在父类中,借口起不到任何作用,反而给编程添麻烦,
B=Class(A,I)
C=Class(B)
D=Class(B)
这时它们的作用等同于
B=Class(A)
C=Class(B)
D=Class(B)
你还要维护借口声明效果会适得其反






 
其实所谓接口并不是仅仅只Delphi中的接口这个语言元素。所谓接口,就是一个封装好的代码和外部通讯的方式,其实所有类中的public和published方法、属性都可以看作类的接口,单元的inferface部分的函数、过程也可以看作是接口。
所谓针对接口编程,其实是封装原则的发展,不过不同的是这个要求是针对调用者提出的,而封装的要求是针对被调用者提出的。
封装和针对接口编程的优点就是如果封装好的代码保证接口与以前版本或其它代码兼容,则使用这些封装好的代码的程序就不必为兼容性担心,也降低了代码维护、升级的成本。
 
对于leasun说的,我想补充一点,公布的事件也应该是“接口”。
 
非常同意leasun的观点,其实很多问题都被我们给狭义化了,一说到接口往往就认为只Interface,一说到中间层恨不得表连接的操作都要在中间层写。
 
哈哈哈哈 哈哈哈哈
原文:其实很多问题都被我们给狭义化了,一说到接口往往就认为只 Interface ...
 
原文:其实所谓接口并不是仅仅只Delphi中的接口这个语言元素。所谓接口,就是一个封装好的代码和外部通讯的方式,
其实所有类中的public和published方法、属性都可以看作类的接口,单元的inferface部分的函数、过程也可以看作是接口。
 
原文:对于接口编程,我觉得广义的接口编程就是定义对象的属性、事件、公共方法,对外部程序只提供这些东西,相当于说,
你申明了一个类的属性,这个属性就可以看成是这个类的一个接口函数。
 
原文:接口只是公开的调用方法,可以比喻为dll中的函数或过程,你只需知道调用什么dll中的什么函数和过程就可实现什么功能即可,
无须了解实现细节。从写接口的方面说你只要按照接口定义的函数和过程写好实现细节,给别人调用即可。简单的说dll也是一种接口。
原文:所谓接口类,就比接口更加高级,就好比dll和类库的比较一样。dll中只指定了某些关连的函数或过程;而类则可以继承可以进行
更多的接口组合,所以接口类比接口更加重要。
 
八仙过海,各显神通.
 
对树操作可以使用visitor pattern,
给每个节点加一个AcceptVisitor(visitor : TAbstractVisitor)方法
在方法里调用visitor类 的visit方法, 并把节点作为参数传入visito的方法中r.
eg. Visitor.VisitNote(Self);
然后写一个visitor的抽象类TAbstractVisitor,里有一些Visit操作的虚方法, 如VisitNote
每种操作都作为一个TAbstractVisitor的派生类.具体操作在Visit方法中实现.
在对树节点操作时可直接调用节点的AcceptVisitor方法, 不同操作传不同的visitor类.
这样一来当你想加对树节点加新操作时只需写一个新的Visitor类就可以了.不需改节点类的代码.
 
就我理解,楼主的主要问题在于,基础接口或类 设计,是以“透明性”为原则,
还是以"安全性"为原则。
如果以“透明性”为原则:
那在基础类中,尽量添加最大集合的方法。
优点:这样可以忽略相关子类型的具体类型。操作方便。
缺点:会引入一些对部分类型无意义的操作。
如果以"安全性"为原则:
那在基础类中,尽量添加最小集合的方法。
优点:保证每个操作都有意义,很安全。
缺点:需要判断子类型的具体类型,操作复杂。
我也很希望既能得到“透明性”又能得到“安全性”,
就我目前的理解来看,好象是不能兼得。
只能根据具体的情况,进行平衡取舍。
在《设计模式》的Composite模式章节中,就有对在针对
Composite情况下,对“透明性”与“安全性”进行
取舍的讨论,可以借鉴一下。
yyanghhong,觉得可以用使用visitor pattern来处理。
在我看来,visitor的主要作用在于把一些操作,从原本的类型
中分离出来。避免这些操作「污染(polluting)」它们的类型。
如果只是遍历操作,visitor真是个很不错的选择。
但我不知道visitor 如何来兼顾“透明性”和“安全性”。
TAbstractVisitor抽象类里定义哪些虚方法呢?
又怎么来处理,有些类型支持copy,delete。而另一些类型只支持copy呢?
在我看来,围着visitor pattern绕了一圈,又回到了问题原来的位置。
 
我曾经采取过这样的做法,在树的节点操作(这个动作)和这个操作的实现完全分开处理,
然后添加一个管理类,来协调节点操作和这些操作的实现。
这些操作的实现是由各个不同的类实现,这些类实现一个或几个接口(Interface),这些接口是根据节点操作定义的。
不同的节点操作对应具体操作类,这个对应关系是可以动态改变的,通过管理类来实现,但是他们之间的接口定义是不能改变的。
楼主说以统一的方法操作所有的子类,我赞同tuti说的,需要自己进行平衡取舍。
 
tuti,
当有了新操作以后, 可在 TAbstractVisitor里的增加虚方法, 然后在派生类去实现这个方法.对于节点类里,只要在acceptVisitor里调用相应方法就可以了.你说的不同节点有不同操作, 这是节点的自身属性, 可在acceptVisitor里加判断.
visitor模式不光可用来遍历.
 
顶部