您的位置:首页 > 运维架构 > 网站架构

从吉日的一段话说起+寻找WinForm架构的最佳实践

2011-03-17 16:33 309 查看
本文转自:/article/4700497.html

private void function1ToolStripMenuItem_Click(object sender, EventArgs e)
{
Function1 function = new Function1();
MessageBox.Show(function.ToString());
}
private void function2ToolStripMenuItem_Click(object sender, EventArgs e)
{
Function2 function = new Function2();
MessageBox.Show(function.ToString());
}
private void function3ToolStripMenuItem_Click(object sender, EventArgs e)
{
Function3 function = new Function3();
MessageBox.Show(function.ToString());
}
private void function4ToolStripMenuItem_Click(object sender, EventArgs e)
{
Function4 function = new Function4();
MessageBox.Show(function.ToString());
}

现在问:这里的Main项目启动时,会主动加载这四个子功能模块么?

我们使用Visual Studio调试时输出窗口的功能看看:





窗口中,前部分输出的是加载的框架的模块,最后一个是加载Main程序集。但是FunctionModule1-FunctionModule4呢?我仔仔细细寻找了好几遍没发现。那么说明,Main项目启动时,不会主动加载子功能模块的,所以吉日的说法是错误的,除非他把所有子功能模块全部写在主功能模块中。那什么时候加载这些子功能模块?来,我们尝试点一下菜单:





子菜单的功能弹出来了,从输出窗口里也发现这个时候加载了FunctionModule1.dll。

以下是对WinForm应用架构设计的讨论

-----------------------------------------------------------------------------------------------------------

下面,我们来讨论一下WinForm应用的架构问题。

我不知道园子里有多少人在做.Net WinForm应用,只是看到园子里大部分人都是在搞Web或WPF。苦恼的我还在搞WinForm,不过也其乐无穷。

上面的实例实际上也给出了大部分WinFrom项目的一个雏形,很多的菜单,每个菜单对应一个功能,打开一个功能的时候要么是Mdi的模式,弹出一个窗口,要么就是一个TabPage。大的工程肯定是一个人完成不了的,需要多个人来做这个事情。但是又需要统一规划。

所以必定是这样的:由架构师来规划好底层支撑的框架,比如这里的Shell,还有一些引擎级别的服务(比如LanguageService“多语言”,PropertyService“持久化软件里某些设置”等)。这个框架必须稳定,项目组其他人员,每个人负责一个或多个功能,每个人只需要关注自己的业务就可以了,把功能做好,然后调用框架里提供的一些服务,把自己给挂接到主框架中。

那么如果按照上面示例的这种设计,我们的程序主框架就必须引用所有的子功能模块,项目开始的时候,可能只有一两个模块,随着项目的前进,引用也在不断的增加,而主界面上的菜单也在不断的增加,这可以说主界面是稳定的么?

那我们必须寻找一种机制。子功能对自己负责,子功能负责自己将自己注册到系统中去。这个时候我们可能采用这样的方式:

一个Modules文件夹,在这个文件夹下又放着很多子文件夹,每个子文件夹里放着一个功能,当系统启动的时候,由框架搜寻Modules子文件夹,在里面查找一个后缀名为addin(或者其他方式)的xml文件,文件里面的内容可能如下:

<MenuItem Site=”File” Text=”Edit” Icon=”Edit.png” CommandType=”MultiLibrariesApp.EditCommand” />

主模块读取这个之后就会生成一个菜单项,当点击这个菜单项的时候,根据CommandType,利用反射实例化一个EditCommand类型,所有的Command可能都实现一个ICommand接口,而这个接口里有一个Run方法:

public interface ICommand
{
void Run();
}
public class EditCommand : ICommand
{
 public void Run()
{
 //code here...
}
}
public class MenuItem
{
 public string Text{get;set;}
 //主框架的代码里
 ICommand command = null;
 private void MenuItem_Click(object sender, EventArgs e)
{
 //延迟加载
 if(command == null)
 command = //通过反射实例化具体的Command类型,这里就是EditCommand
command.Run();
}
 public ToolStripMenuItem CreateMenu()
{
 ToolStripMenuItem menuItem = new ToolStripMenuItem();
 menuItem.Text = this.Text;
 menuItem.Click += MenuItem_Click;
}
}

这样,就能保证主框架是稳定的了,子功能负责自己的菜单管理,只需要写一个文件就可以了。

当然,实际的框架实现过程中,肯定会碰到各种各样的问题,这只是一个思路。对于WinForm程序而言,你还可以参考一下开源项目:

SharpDevelop 一个开源的IDE

MonoDevelop 从SharpDevelop发展而来,但是现在大变样了

Mono.AddIn由MonoDevelop的插件机制发展出来的一个小插件系统

Composite Application Block 微软模式&实践小组的

还有很多其他,我就没有研究过了。

以上只是我的一个思路,由于WinForm开发的看到不多,我在网上搜WinForm Best Practice也没找到多少资料,所以希望能够在这方面有所讨论。也许能碰撞出一些火花出来。

后话

刚才出去了一趟,在路上又思考了一些问题。

通过上面对模块加载和WinForm架构设计的讨论,总结一下:吉日文中说的这种应用反射的情况并不是因为基于启动效率的问题,而是设计的考量。这个地方跟性能一点关系都没有(在这里对我在老赵博客里开始错误的评论表示道歉),用不用反射启动效率都是这样。

再看看吉日另外一个应用反射的场景:两个类循环引用。我不知道为什么有这样一个设计,如果是遗留代码那你首先应该考虑重构一下,如果实在不能重构,就必须这样,那只有用反射了。所以这个问题也是设计上的问题,跟反射也没啥关系。

还有吉日说的,配置多数据库的场景。老赵说了,这里推崇ORM。即使你不用ORM,我也觉得这是没有必要的。我不知道有多少情况一个正在运行的系统要突然更换不同类型的数据库?即使有这种情况,那么这也属于重大变更,对于这种变更,你完全可以修改代码。还有,针对这种情况,微软已经给出了Best Practice:提供者模式。虽然提供者模式最终还是反射。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: