接口运用的疑问

yanghuanji 2003-08-04 09:11:20
背景:1,利用UML和组件概念实现软件
2,写了三个模块的程序:ClientProgram,ServerProgram,Interface
3, ClientProgram与ServerProgram是通过Interface发生关系
4,在ClientProgram和ServerProgram中都通过工程引用了Interface
5,MSDN中介绍:ClientProgram一方面引用了Interface,另一个方面还要实例一个ServerProgram中的一个类。
6,ServerProgram中的类实现Interface

代码:
现象:
问题:1,ClientProgram既然引用了Interface,但是还要实例一个ServerProgram中的一个类,那么不是多此一举的事情吗?和直接引用这个业务类有什么区别??
2,凡是解释接口的都用到了航天飞机接口的概念,那么就是说ServerProgram是一个很灵活的东西,而在ClientProgram不会出现ServerProgram中的类的,它是通过Interface来实现对ServerProgram中的类的引用的。
3,很可惜,这些解释只是表面的,并没有用代码的形式表示出来。请问各位怎么样具体的用代码实现正真意义上的接口?
本人是COM的初学者,请多关照。
...全文
20 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
yanghuanji 2003-08-08
  • 打赏
  • 举报
回复
你都让我感动啊!谢谢!
把你的EMAIL给我吧,yanghuanji@sina.com。
我还有很多的问题想请你指教。


sogno 2003-08-07
  • 打赏
  • 举报
回复
问题1、你是指把实体类作为参数传给数据转换层组件吗?还有更具体的要求吗?

问题3、参考的代码,你可以看微软visual studio带的enterprise samples的Duwamish
4、5是用VB写的,7是用C#写的,它讲了一个系统从CS->BS、从DNA->.net演化的过程,很有启发性
sogno 2003-08-07
  • 打赏
  • 举报
回复
问题2,必须用byref方式传递才行

其他2个问题我再琢磨琢磨
yanghuanji 2003-08-06
  • 打赏
  • 举报
回复
谢谢!
不过还有问题,就是传递参数的问题,不知道你有没有时间。如果没有,那我就结帖了。

背景:1,采用UML设计软件
2,有一个商业规则层组件,通过接口和数据转换层组件通信(商业规则层里包装了实体类)
3,接口有四个的操作:GetInfo(),Delete(),Updata,Insert()
4,数据转换层组件实现这个公共接口。
5,商业规则层组件中的实体类引用这个接口。


代码:
现象:
问题:1,怎么样在接口中传递不同的实体类的参数??
2,我用了数组作为传递的参数,而且类型设置成为了Variant。但是VB报错,这是为什么?
3,有无参考的代码?
yanghuanji 2003-08-04
  • 打赏
  • 举报
回复
请教各位!
sogno 2003-08-04
  • 打赏
  • 举报
回复
3、问题到目前为止解决了一半,虽然在源代码级我们做到了重用的最大化,可是接下来,我们一样需要重新编译两端的程序、重新打包、重新部署。

经过观察,我们发现,如果不是那个CreateServer方法发生了变化,客户端的程序我们根本就不需要作任何修改,因此也就不需要重新编译、重新打包和重新部署。

我们怎么才能做到呢?……
既然Server一直在变,就只让它变好了,Client就不陪它玩了,呵呵

循着这个思路,我们就可以得出这个结论:把CreateServer移到Server里面去,Client不就不会变了吗?

在Server工程里定义这样一个类ServerFactory,Instancing为global multiuse(这样就可以不创建对象而直接使用对象公开的方法,就像使用全局函数一样),同时把IProvider的所有实现类的instancing都改为private,这样这些类对client完全不可见(但仍然可用),这就完全实现了对Server实现的封装

Option Explicit

Public Function CreateServer() As IProvider
Set CreateServer = New clsNewServer
End Function

Client的代码这次就完全不需要修改了,还是原来那个样子

Public Sub ClientSub?()
Dim objServer As IProvider
Set objServer = CreateServer
objServer.DoSomething
//...
End Sub

由于ServerFactory对象封装了IProvider接口实现类的创建,而且全部位于Server工程,那么Client工程就剩下了所有不需要跟着Server一起改的代码了,这样,,使两边可以各自相对独立地变化,灵活性大大提高了,耦合也就降到了最低。

sogno 2003-08-04
  • 打赏
  • 举报
回复
问题2、3
写个简单的例子
我们看看看解决方案的演化:
在VB里新建一个工程组,包含一个标准EXE工程名叫Client,一个ActiveX Dll工程名叫Server,Client工程引用Server工程

1、我们在Server里定义一个类clsServer,instancing属性是multiuse,代码如下
Option Explicit

Public Sub DoSomething()
MsgBox "I'm Done."
End Sub

在Client里的某个地方(窗体或模块)定义若干子过程ClientSub?,如下
Public Sub ClientSub1()
Dim objServer As clsServer
Set objServer = New clsServer
objServer.DoSomething
//...
End Sub

Public Sub ClientSub1()
Dim objServer As clsServer
Set objServer = New clsServer
objServer.DoSomething
//...
End Sub

...

这就相当于你所说的直接引用业务类。

如果后来你发布了新的业务类clsNewServer来代替clsServer,instancing属性依然是multiuse,代码如下
Option Explicit

Public Sub DoSomething()
MsgBox "I'm done perfectly."
End Sub

所有的用到了clsServer的client代码都必须要修改,当然也需要重新编译,重新打包,然后重新部署。

那么我们需要改进一下,以减轻我们的负担。

2、我们发现,clsServer和clsNewServer的接口声明是完全一样的,所要做的工作也是完全一样的,不同的只是做得好坏的程度而已。

于是我们决定从这2个类里提取出一个接口IProvider,我们用一个Instancing为Public not Creatable的类作为接口,这个属性值保证了client代码无法直接创建一个IProvider对象的实例,因此它可以在client端作为一个接口来使用了。代码如下
Option Explicit

Public Sub DoSomething()

End Sub

然后让clsServer和clsNewServer都实现这个接口,修改后的代码如下
clsServer类的代码:
Option Explicit

Implements IProvider

Private Sub IProvider_DoSomething()
MsgBox "I'm done."
End Sub


clsNewServer类的代码:

Option Explicit

Implements IProvider

Private Sub IProvider_DoSomething()
MsgBox "I'm done perfectly."
End Sub

client代码也作相应修改

Public Sub ClientSub1()
Dim objServer As IProvider
Set objServer = New clsNewServer
objServer.DoSomething
//...
End Sub

Public Sub ClientSub2()
Dim objServer As IProvider
Set objServer = New clsNewServer
objServer.DoSomething
//...
End Sub

稍等……
我们发现这样改好像并没有减少工作量,假如再次发布了新的clsNewNewServer,我们的工作量并没有什么太大的变化。而原因呢,就在于那条Set objServer = New clsNewServer,这显然还是一个实现依赖,而我们只需要接口依赖以降低不同模块之间代码的耦合。

我们决定把对象的创建提出来变成一个函数
于是在client端代码添加一个函数CreateServer
Public Function CreateServer() As IProvider
Set CreateServer = New clsNewServer
End Function

所有的ClientSub?()都作相应修改

Public Sub ClientSub?()
Dim objServer As IProvider
Set objServer = CreateServer
objServer.DoSomething
//...
End Sub

这样,如果用clsNewNewServer代替clsNewServer的时候,我们在client端就只需要改CreateServer()函数就OK了,代码的重用度大为提高,我们也轻松了不少^_^
sogno 2003-08-04
  • 打赏
  • 举报
回复
这个问题好大,20分怎么够~~呵呵:)

这里涉及到OO的设计原则问题,比如LSP(Liskov Substitution Principle,里式替换原则)啦,ADP(Acyclic Dependencies Principle,依赖倒置原则) 啦等等,不是我这样的水平的人一两句话可以说清楚的。
所以还是就事论事,讨论一下你提出的问题好了。
问题1、这个写法并不是多次一举,如果你直接创建了一个业务类,并在Client代码里使用它,那么你的代码就依赖于实现而不是接口,如果你以后写了另外一个业务类来代替原先的那个(前提是接口不变),你也需要在你的Client代码里逐一修改和重新编译,如果你是通过引用接口来写Client代码,那么你只需要在创建对象的地方作修改,这样就比直接引用业务类对象好一些。假设你的系统很大,有很多地方创建了你的业务类对象,而你的业务类的实现又很不稳定或者可能需要Client定制,那你也需要大量的修改这些创建对象的地方,并且重新编译,那么可以使用工厂对象和工厂方法来进一步解耦。了解工厂方法的话你可以去参考Gof的Design Patterns,那是公认的经典。
我只举个简单的例子:Windows Common Control里面的TreeView控件的node对象和ListView里面的ListItem对象你是无法直接创建的,你只能通过TreeView.Nodes.Add和ListView.Items.Add方法来获得,那么这两个方法就是工厂方法。其实工厂方法在OOP里应用得非常广泛,COM里的类厂也是这么个东东。
yanghuanji 2003-08-04
  • 打赏
  • 举报
回复
请教各位了!!

7,763

社区成员

发帖
与我相关
我的任务
社区描述
VB 基础类
社区管理员
  • VB基础类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧