[Flex]Prana Framework力推ActionScript 3应用开发

mikel阅读(778)

Prana Framework力推ActionScript 3应用开发

作者 Moxie Zhang译者 张龙 发布于 2008年11月5日 下午3时59分

社区
Java
主题
RIA
标签
Flex

Prana是一个面向Adobe Flex及ActionScript 3的控制反转(Inversion of Control,即IoC)应用框架。InfoQ最近采访了Prana Framework的创建者Christophe Herreman和Damir Murat以深入了解该框架的使用。

InfoQ:您能否向InfoQ的读者说明一下当初为何在其他控制反转应用框架已经存在的前提下还要开发Prana呢?

Herreman:Prana诞生于我们开始重写之前用ActionScript 2和Flash开发的一个在线学习平台之际。我们使用的一个库是来自于as2lib的IoC容器,由于之前IoC对我们的工作提供了巨大的帮助,因此我们 想在自己的这个新平台上也添加同样的功能。那时还没有ActionScript 3的IoC容器,所以我打算自己开发一个。

我以一个自己的实现(基于Spring XML方言)开始,但很快我就决定尽可能地以Spring提供的代码作为基础。这样做更容易实现某些特性,因为可以参考Spring的源码;熟悉 Spring的开发者使用Prana时会很容易上手,当然我也借此机会更深入地学习Spring的内核。

InfoQ:您认为Prana framework最突出的特点是什么?

Herreman:它是一个通用、可扩展、功能强大的IoC容器。如果你了解Spring IoC容器,那么你就会清楚Prana能做些什么。

它有一个很棒的特性:你可以向其XML解析器中增加自定义的预处理器。预处理器用来转换已加载但尚未解析的XML。接下来,你可以增加新的元素和属性以方便地描述自定义对象,同时还可以让自定义的预处理器将元素转化为Prana解析器可以理解的形式。

除了IoC容器,Prana还有一个构建于describeType()之上的Reflection API。这样你就可以在运行时获得对象的信息,比如对象包含的属性和方法以及实现的接口等等。接下来,我们还为领域对象创建了一些基础类(这是从Eric Evans的Domain-Driven Design一书中得到的灵感)。这些基础类具有比较和克隆对象等逻辑。Prana还包含几个有用的帮助类。

Murat:Prana还提供了一些工具,这些工具可用来快速建立基于Prana的项目。其中一个主要特性就是 动态更新Flex编译器的配置信息以包含编译好的swf中的类,而这些类是无法通过代码访问的。这在IoC系统中很常见,因为IoC鼓励面向接口(而不是 类)编程。我们的工具与Eclipse/Flex Builder紧密集成,同时他们可以检测到Prana的配置信息何时发生了变化,如果需要的话,他们就会解析Prana的配置并相应地更新flex编译 器设置。当程序员必须显式声明无法通过代码访问的类以将其包含在最终编译好的swf中时,这种方式就无需再使用典型的flex“模式”了。我们的工具会自 动完成这些事情。

还有其他一些特性,如预定义的项目布局、定义好的Ant target,对subversion的支持等等。所有这些特性都是可配置的,并可通过几个步骤轻松搞定。开发者可以查看prana-tools项目(从svn或是分发包中都可以得到)以了解感兴趣的信息。

InfoQ: Prana集成了Cairngorm和PureMVC。您能否说明一下Prana为什么要与这两个框架集成,并且是如何实现集成的?

Herreman:我们为Cairngorm和PureMVC提供了一套扩展。因为我们使用了IoC,所以我们还想将该原理应用到我们正在使用的框架上,同时我们想用依赖注入(Dependency Injection,即DI)对应用的不同部分进行包装。

我们为Cairngorm提供了一个可配置在IoC容器中的服务定位器。你可以在外部定义远程对象、channelsets、consumers等, 并可以改变他们而无需重新编译应用。通过这种方式,你也无需编译services-config.xml文件并可以轻松地将其部署到不同的地方。它还使测 试变得更简单。典型的例子就是当你从开发机器迁移到测试或是产品服务器上时,你得改变端点(endpoints)。Prana使这一切变得简单,你无需重 新构建应用。

我们提供的frontcontroller是Cairngorm frontcontroller的一个子类,它接收定制的命令工厂。通过这种方式,你可以控制命令创建的方式,一旦命令创建好后,你就可以将额外的属性注入到命令中。除此以外,我们还支持链式的事件/命令,这样你就无需显式地从另一个命令中调用命令了。

Murat:与PureMVC的集成最初只是一种实验性的尝试,用来将IoC带到PureMVC应用中。后来发现这是可行的,于是我们就将这项工作公开了。

对于PureMVC用户来说,主要的好处是当遇到依赖时可以使用依赖注入。同时这也是最大的缺点,因为DI的使用不可避免地会改变一些原始的 PureMVC使用习惯,而这些习惯是基于服务定位器模式的。然而我们相信DI对任何应用都是很有帮助的,PureMVC也不例外。为了减轻移植到DI的 代价,我们尽可能简化Prana的PureMVC集成。例如,PureMVC开发者可以选择一个DI的应用范围。Prana只能用来管理非PureMVC 对象,或者说它只能用来管理部分PureMVC类,当然它可以管理应用中的PureMVC对象和非PureMVC对象。

InfoQ:能不能推荐一下使用Prana的最佳方式(或者是应用类型)?

Herreman:如果你需要在应用中保持一定程度的灵活性以便其可以运行在不同的上下文中,或者是你拥有大量的配置,想要集中管理他们,那么我极力推荐使用Prana。因为它基于Spring,很多开发者已经熟悉了其概念和XML方言。

就我们的情况来说,我们已经创建了一个在线学习平台,用户可以定制其自己的需求。因为我们自己管理该平台,所以需要有一种机制以允许所有这些定制。 如果没有IoC,我们就不得不对每个定制编译不同的软件版本,或者是我们必须编写一个基于XML或者是数据库的客户配置系统,而对其的维护绝对是一个噩 梦。与此相反,我们可以让每种定制都有一个应用上下文,当应用加载时就去装载该上下文,这取决于登录的用户。更酷的是我们可以从ASP页面(需要从数据库 中读取配置)中即时生成应用上下文。

InfoQ:Prana的长期计划是什么?

Herreman:最重要的事情就是IoC容器,我们期望1.0版会有一个稳定的容器。目前来看,容器本身很不错,但我们还可以改进一些东西,增加更多的Spring特性,如parent beans及自动装配等。我们还需要编写一些文档。

我们一直在与开发团队探讨将扩展(Cairngorm、PureMVC等)从主代码库中移除,然后将其作为独立的扩展库发布。这么做将有利于发布管理。

我还准备开发一个AOP(Aspect-Oriented Programming,面向方面的编程)框架,但遇到了一些麻烦,这些麻烦是由ActionScript 3的一些限制导致的。AOP背后的主要思想是基于动态代理机制创建新的对象类型,该对象会在运行时实现一些接口。问题在于这在ActionScript 3中是不可能的。我们已经在Adobe JIRA上发布了这个话题,如果有人愿意与我们分享一些见解的话我将感激不尽。该话题位于:http://bugs.adobe.com/jira/browse/ASC-3136

至于其他方面,我们还没有制订严格的路线图。当我们有新想法时就会引入一些新特性和进行一些改进,我们一直在倾听来自其他开发者的建议,同时还期待有更多的人能加入到我们的团队中。

查看英文原文:Prana Framework Helps on ActionScript 3 Application Development

[C#]BlogEngine.Net架构与源代码分析系列part4:Blog全局设置——BlogSe

mikel阅读(813)

     这 已经是本系列的第四篇了,以前我多数时间是看文章,自己写起来才感觉到当博主不容易啊,所以我们无论评论也好,阅读也好,都要尊重博主的劳动成果。闲话少 说,在这篇文章中我们将对BlogEngine.Net的全局配置进行一下分析与探讨。关于配置这一部分单独拿出来做一篇文章实在有些牵强,但是我总觉得 这个配置部分比较独立,而且BlogEngine.Net的设计和实现都有很多可以参考的地方。

在一个企业级应用系统中,对一些系统全局参数进行配置是必不可少的,那么我们是怎么处理这些配置的呢?

     一般都有以下三步:

1.在业务模块开发的过程中将一些可变的参量提取出来。

2.当所有业务模块开发完成时,将这些参量分类存储起来。

3.开发出相应的管理功能,允许用户对这些参量进行设置。

     相信大多数开发者都是直接操作数据库中的数据,可能有些比较完善的系统 会做出单独的页面来给用户管理使用,本质上也都是直接与数据库打交道,没有涉及太多的逻辑,比较直接。不过在BlogEngine.Net的架构模型上这 种操作就失效了,我们不可以在BlogEngine.Net的数据库中修改数据而希望在它的运行系统中体现出来(除非重启应用),因为 BlogEngine.Net的数据库只是完成数据存储功能,所有的业务逻辑都在BlogEngine.Core层由对象的状态维护来完成(可以参考我的第二篇文章)。有人可能会想可不可以进一步开发一种像ASP.NET中Cache那样对数据库表的依赖机制,我觉得这个问题要看需求,这种在BlogEngine.Net中似乎没有什么太多必要。

那么BlogEngine.Net的全局设置到底是怎么实现的呢?

     在安装 BlogEngine.Net以后进入后台管理中,我们会看到有很多分类的配置选项,包括主题,语言文化,时区等。这些配置的逻辑处理都是通过 BlogEngine.Core中的一个类完成的,那就是BlogSettings。BlogSettings只是完成BlogEngine.Net的全 局的配置处理,对于后文讲述的Widget的一些具体配置并不在这里完成,这主要是为了配置独立的目的,使得Widget可以独立开发。

     BlogSettings与外界交互图(这个图使用画图程序弄的,大家先凑合着看吧):

 

     

     在BlogSettings中除了各种配置项的对应属性以外,还有一个 静态的Changed事件用来通知外部全局配置已经发生了改变,这样就可以写出更多扩展来。BlogSettings使用单例模式来实现(一个设计模式的 很好的应用,全局配置具有系统唯一性)。

Code

 

从这里我们就可以知道为什么对于数据源的直接修改不能在BlogEngine.Net的运行系统中体现的原因了。

     BlogSettings在对象构造时执行了一个Load方法来加载所有数据存储中的配置信息,这个加载过程应用到了.Net反射技术,找到数据存储中与对象属性相同名称的配置项并将其值强制转换以后付给属性,当然这里的数据访问是通过我的第三篇文章中讲述的BlogService调用获得。同理修改配置是通过Save将数据保存回数据存储,也是使用反射完成。

Code

 

客户端的使用方法(注意:这里所说的客户端是指BlogSettings使用者或调用者)

Code

 

总结

     从BlogEngine.Net的全局配置部分我们可以学习到以下几点:
1.单例模式是如何应用在实际项目中的。
2.配置项的数据存取部分的实现有很好的参考价值,可以了解到.Net的反射给开发带来的方便。
3.对于静态事件的使用(BlogEngine.Net中有很多例子)使得我们可以在外部做一些扩展,例如开发一个监控配置修改的跟踪系统。

     好的设计要经过不断的重构才可以达到。

     上一篇:BlogEngine.Net架构与源代码分析系列part3:数据存储——基于Provider模式的实现

版权声明
作者:Thriving.country
出处:http://thriving-country.cnblogs.com/
本文版权归作者和博客园共同所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接.

[FireFox]FireFox3.1开始支持HTML5的视频和音频

mikel阅读(796)

Firefox 3.1现在包含对HTML 5 视频video音频audio 标签tags的支持了!直接可以在页面上控制音频和视频信息,Mozilla开发者网站发布一篇新的文章指导如何使用。Firefox 3.1目前支持Ogg format(一种新的崭露头角的音频文件格式),其他音乐格式还在讨论中,可能未来支持。
下面是范例使用方法:

Html代码 复制代码
  1. <video src="http://v2v.cc/~j/theora_testsuite/320×240.ogg" autoplay>   
  2. Your browser does not support the <code>video</code> element.   
  3. </video>  

是不是非常简单?你能为不同的格式提供不同的代码选项:

Html代码 复制代码
  1. <video autoplay>    
  2.    <source src="foo.ogg" type="video/ogg"></source>    
  3.    <source src="foo.mov"></source>    
  4.    Your browser does not support the <code>video</code> element.    
  5. </video>   

目前,所有源码元素还没有完全支持,媒体录音能通过JavaScript控制:

Js代码 复制代码
  1. var v = document.getElementsByTagName("video")[0];   
  2. v.play();  

你还可以通过不同的媒体事件来更新你的UI:

Js代码 复制代码
  1. var v = document.getElementsByTagName("video")[0];  
  2.   
  3. v.addEventListener("seeked"function() { document.getElementsByTagName("video")[0].play(); }, true);  
  4. v.currentTime = 10.0;   

真是挺强大的,希望其他浏览器对HTML 5的支持也能加强。

来自:ajaxian.com

[设计]30大优秀Logo设计详解

mikel阅读(952)

原文:30 Brilliant Vector Logo Designs, Deconstructed -Chris Spooner

编译:Vinwin (不倒猫

 

拥有一个抢眼的Logo对企业来乃一大幸事,毕竟Logo千千万,但真正让人过目不忘的作品可是屈指可数。好的Logo必须量体裁衣,迅速传递出企业的价值和理念。
但Logo不能只是金玉其外,还必须有思想、多功能。比如,可以用标准色在任何尺寸的纸张完美复制和再现。下面就晾出当前30个自认为很杰出的Logo,飨宴大家。

1.    Castle Print
一个打印机品牌,该Logo直截了当地体现了了企业的业务性质:利用减色模型,直指其打印行业背景,同时通过色彩的混合塑造出一个与其品牌相符的城堡(Castle)形象。


2.    Ryan-Biggs
负空间的运动使得这幅Logo有一种奇幻的效果,完全考验你的空间想象力!B和R两个字母代表了这个品牌,微微的倾斜让整个设计看起来更有深度和立体感。色彩搭配极为简单—红色,赋予了Logo更广的使用范围。

3.    One Leaf
One Leaf, 顾名思义,即一片树叶。以此为轴线,就呈现出了如此简洁巧妙的画面。

4.    Greener
设计师用粗细不同的灯芯体(San-serif)字体塑造出一种现代感。该Logo不但层次感强,更重要的是可以用单一色调复制重现(这也是评判Logo好坏的重要标准之一)。

5.    Talkmore
Talkmore字面意思是“多说点儿”。设计师采用象征的手法,用英文中的单引号分别代替字母“a”和“e”,从而在图形上给予品牌最鲜活的注解。


6.    Black Sparrow
看上去很简单的图标,但在细节处理上却达到了极致。从麻雀(即Black Sparrow的中文)的图案到字体,柔和的曲线与平滑的字体相得益彰,将完整的设计融入品牌表现。


7.    Swannie Lake
富有时代气息的Avenir字体配合平滑的图案,不但与该Logo完美贴合,而且增添了一许微妙的色彩。

8.    Elara Systems
先介绍一下,Elara Systems是一个动画和动态模型工作室,也必然要求2D和3D的结合。体现在Logo上,就是大家所看到的效果:柔软弯曲的字体配上3D的字母“e”(即首字母),很好的创意

9.    Onwine
完美的字体搭配独到的理念,Onwine Logo为我们展现出一个酿酒商的特质,无论是图案还是字体设计都无可挑剔。


10.    Popp
什么叫一气呵成?这幅Logo将基于同一字体的设计方法带上了全新的高度。每个字母都包含字母“O”,只是做了细微的改动就成了“P”,独具匠心。

11.    Therauz Fashions
能看出这个Logo的绝妙之处吗?该公司从事时装设计业务,所以针线活是少不了的。画面中央的长针,不仅反映了公司业务,而且将中间一串字母给“缝”了起来,成了必不可少的一部分,内涵之深可见一斑。

12.    Alatau
无论是字母还是彩色远点,共同的特点就是间距较大,这种手法带来的效果就是严肃而前卫。这样排列的好处还在于以中间两个字母中心,整个Logo看起来非常平衡。


13.    Spiffy Sparrow
作品采用复杂的形状、线条和色彩,为以后的调整打好了基础。另外,用负空间来塑造鸟的躯干也是令人叫绝的地方。


14.    About Thyme
干净利落的线条和形状,是Logo设计的固有套路。不过,本作品似乎摆脱了这样的条条框框:粗糙的手绘图案,给人以一种亲和力,也给体现了品牌所有者作为调味品公司的价值。

15.    Ta Jevi
一个娱乐网站。Logo的每一部分都闪烁着欢乐感。箭头组成的笑脸传递出了这样的信息—欢乐无止境。跳跃的色彩和超酷的手写字更突出了该网站的娱乐价值。


16.    Anti Particle
中文名—反粒子,是一家电影制作公司。这里有必要做个小小的科普,反粒子其实很简单,比如负电子的反粒子就是正电子,质子的反粒子就是反质子……好了,回 到Logo上,很明显,这又是一个用公司名做文章的范例。首先首字母“a”有无数颗粒组成,但里面恰恰有一颗蓝色颗粒,这就体现了“反粒子”的概念。


17. 69 Monos
不同于平面2D设计,有些Logo,就像69 Monos,通过3D效果给Logo增添深度和趣味。改变角度就能带来动感,何乐而不为?


18.    Green Dolphin
Logo使用海豚(Dolphin)的轮廓和绿色来传递品牌信息,同时也隐藏了次级画面—即大写字母G。

19.    Aramova
经典的莫比乌斯带(Mobius Strip http://hi.baidu.com/totogo/blog/item/629a233ff2239aec54e723ad.html)使用案例!柔软的质地+奇幻的色彩,整幅画面让人体会到永不停息的动感。


20.    Vize
并不是所有的Logo都需要附属图案,比如我们看到的这个。虽然只有字体,但由于在排版上做足了功夫,Logo同样可以表达品牌的理念。

21.    Friends in Places
交友网站,很明显。看似纷繁复杂的箭头构成了一幅世界地图,表现了互联网时代网络社交的全球性和广泛性,企业的品牌价值由此得到体现。


22.    Koloroo
一款色彩方案的软件产品。随机的颜色搭配勾勒出了一只袋鼠的形象,这也符合其产品形象。字体设计同样出众,因此可以说,该Logo两部分合起来是精品,拆开来也是优秀的图案和文字标识。


23.    Pangur
手工玻璃制造商。通常而言,透明的运用在Logo设计中是慎而又慎的,不过随着打印技术的改进,已经没有理由限制这种技法的使用了。设计师用不同色彩的玻璃碗进行简单堆砌,且呈现不规则倾斜,这恰恰抓住了玻璃手工艺的特点。Futura字体也给作品锦上添花。

24.    Tammy Lenski
美国一家争端解决公司(比如劳资纠纷等等)。现在,搭上千纸鹤,来一趟意境之旅吧。大多数Logo都直截了当地反映目标企业的各个方面,然而这个设计却通过千纸鹤的故事和寓意来展现企业形象(编者注:之所以这么说,是因为西方人并不了解千纸鹤的含义)。


25.    Eco Cafe
图案简洁又不是标识性,整幅作品看起来就像破土而出的生命。生态咖啡馆—该企业的名称—得到了最鲜明的宣示。而正因为标识本身的简洁,使得它贴在窗户上,也可以印在咖啡杯上。

26. Firefish
这又是一个运用流行技术—比如透明—塑造多层次标识的典范。火焰拼成的鱼(即Firefish,火鱼)搭配上经典的大写灯芯体(san-serif)字母,堪称一绝。

27. Boxbound
立体和透明同属当今Logo设计潮流,这幅作品同时运用了这两种技巧。生动的色彩加上浑圆可爱的字体,俨然一副基于网络的时代先锋形象。


28. AdMagik
看完这个Logo,你应该能学会用颜色区分信息。品红色部分突出了公司的名称“Magic”(魔术),而灰色字体暗示了该公司的身份,也是Logo的重点 所在,即“Ad”(广告)。最后,在字母J和I上做图,使之成为兔子的形象(编者注:兔子在西方是魔术的象征),再一次强调了企业点石成金的业务能力。


29. Jivespace
这个Logo用爆炸性的手法给人以强烈的视觉冲击,既有深度又充满活力,同时,全部小写字母的排版方式体现出其时代气息。

30. Core
和前面提到的“Popp”Logo类似,它也是基于一个形状延续下去,线条干净利落,颜色单一,该Logo的功能性可想而知有多么强大。

来自:blogbeta.com

[C#]BlogEngine.Net架构与源代码分析系列part3:数据存储——基于Provider

mikel阅读(845)

   上一篇文章中,我们主要分析了一下BlogEngine.Net的整体设计,在后半部分我们又对BusinessBase业务对象的状态维护做了一些比较深入的探讨。在这篇文章中我将引领大家完成对BlogEngine.Net中业务对象数据存储的设计思路与实现细节的分析。

BlogEngine.Net中的数据存储主要是应用Provider模式实现的,那么首先让我们认识一下Provider模式。

     Provider 模式应该是一种设计模式,是用来解决软件变化问题的。不过它关注的角度(或者维度)是功能声明跟功能实现的分离。一般来说,系统对某一功能的需求可能是相 对稳定的(比如每个系统都要求对登录用户进行验证,这个需要是相对稳定的),而这个功能的的具体实现却可以是多样的(比如,你可以去数据库里匹配用户进行 验证,也可以去一个XML文件里面去匹配)。Provider模式在.Net类库的设计中随处可见,如:MembershipProvider、 SiteMapProvider等,它的出现使我们的应用程序有了更大的扩展性,要注意Provider可以是一个数据工厂的提供者,也可以是一个逻辑处 理的提供者。在BlogEngine.Net中我们看到的都是数据工厂的提供者,对于逻辑处理的提供者,大家可以参考一下微软企业库 ApplicationBlock中的加密Block的实现。
     在.Net中要实现这种模式是相当的简单,因为它已经为我们实现了一部分,我们只需实现以下三步即可:
1、定义一个类,抽象出我们所需要的操作,它的基类为ProviderBase
2、实现一个Section,用来从配置文件中读取Provider的相关配置,该类继承于ConfigurationSection
3、在调用时去读取配置文件,并加载指定的Provider

对于BlogEngine.Net中的数据存储部分我是怎么看的呢(个人观点,不必在意)?

     BlogEngine.Net可以支持多种数据存储,在它目前的版本中 我们可以看到XML(默认采用,主要是为安装时即插即用考虑的)和数据库两种存储方式。BlogEngine.Net数据存储的Provider只是针对 数据如何存储部分(不涉及到一些逻辑处理与运算),所以对于数据库的要求非常的低,只要支持SQL语句并可以存储数据就行,实际上它在数据库中只有一些 表,从它的Provider的实现来看并没有使用数据库的主键级联删除等功能,而完全使用多条嵌入式SQL语句完成,这样做就可以使 BlogEngine.Net支持更多的数据库。对于数据库的存储BlogEngine.Net只使用了一个DbBlogProvider,而没有具体区 分到底采用何种数据库,我们只要在配置文件中根据链接字符串设定providerName就可以指定具体的数据库存储了。BlogEngine.Net的 这种对数据处理方式在业务逻辑很复杂的系统中我并不是很推荐。

那么再让我们看看在BlogEngine.Net中是怎样应用ProviderBase来完成数据存储问题的。

     先看一幅继承关系图:

从上图我们可以看出DbBlogProvider 和XmlBlogProvider都继承了抽象类BlogProvider(包含一些业务类或其它类等的操作声明),而BlogProvider直接继承 自ProviderBase,ProviderBase是微软的标准Provider框架,我们只要按照这种模型开发就行了,这个标准的Provider 框架已经解决很多问题,例如依赖注入问题等。为了利用.Net平台已有资源对于角色和成员BlogEngine.Net采用了.Net提供的 MembershipProvider和RoleProvider,BlogEngine.Net中 DbMembershipProvider,XmlMembershipProvider继承于 MembershipProvider,DbRoleProvider和XmlRoleProvider继承于RoleProvider,.Net的 MembershipProvider和RoleProvider同样也继承自ProviderBase。BlogProviderSection这个类 是为了解决配置问题的,也就是.Net中Provider模式实现中的第二步,这就很好的解决了依赖注入问题。BlogProvider定义了一些业务类 的标准的操作方法:

Code

子类只要重写这些方法就可以了。

     此外在BlogEngine.Net中还有一个比较推荐的处理,由于BlogProvider需要处理很多业务类型数据的操作,方法成员就会很多,所以在实现XmlBlogProvider时采用了partial class解决。

     DbBlogProvider重写了ProviderBase的 Initialize方法来达到链接字符串、表前缀、字段前缀等的获得,这么设计我觉得用处可能是由于BlogEngine.Net的数据库只有数据存储 功能,用户可能直接把它部署到了已有数据库中,为了和已有数据库中的其它表区分,我们可以指定一个表名前缀。对于DbRoleProvider和 DbMembershipProvider处理方式也是类似。在实现时BlogEngine.Net似乎也考虑了Mono,部分代码出现了对Mono运行 时的判断,但是我没有试过是否可以跨平台安装,感兴趣的朋友可以研究一下!

客户端是如何使用BlogProvider的呢?

     对于MembershipProvider和RoleProvider 的使用这里不再介绍,可以参照一下MSDN文档,BlogEngine.Net也是这么用的。这里主要讲一下BlogProvider的使用问 题,BlogEngine.Net中提供了一个BlogService的静态类,这个类提供给外界一个统一的数据访问接口,它内部的静态方法实现调用 BlogProvider来完成。在BlogService中有一个LoadProviders方法来根据配置文件动态加载BlogProvider:

BlogService

 

最后让我们看一下Web.config的相应配置

BlogProviderConfig

 

MembershipProviderConfig和RoleProviderConfig

 

总结

     BlogEngine.Net数据存储的实现应用了Provider模 式,这个在.Net平台下有很好的支持和案例,这种设计思想很值得借鉴,尤其是BlogService设计的很巧妙,也有一点面向服务的味道。不过在一些 业务逻辑非常复杂,尤其是一些应用到数据库逻辑处理的系统设计时,这种设计可能就无法满足需求了,还要坚持数据的处理离数据越近越好的原则。

     取其精华!

     上一篇:BlogEngine.Net架构与源代码分析系列part2:业务对象——共同的父类BusinessBase

版权声明
作者:Thriving.country
出处:http://thriving-country.cnblogs.com/
本文版权归作者和博客园共同所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接.

[C#]BlogEngine.net 学习笔记(一)

mikel阅读(625)

最近在园子溜达的时候发现了有人说BlogEngine.net 不错, 看了一下,
真的不错, 代码简洁, 功能不少, 真是麻雀虽小, 五脏俱全啊!!!
附上作者的主页:http://blog.madskristensen.dk/(注:他这个主页就是这个BlogEngine.net!!!!)
准备全程跟踪这个BlogEngine.net!!!!
下面写一些笔记:



[Flash]Flash与3D编程探秘(五)- 摄像机旋转和移动

mikel阅读(699)

点此下载程序源文件

 

上一篇我们讨论了关于旋转摄像机的一些知识,在这篇文章中我们将讲解如何定点旋转摄像机,以及把移动和旋转摄像机结合在一起。当我们只运用旋转摄像机时, 在屏幕上我们看到物体围绕着摄像机旋转,动画并不是那么的"3D",不过这个是我们的必经之路,等你完全的明白了旋转这个概念后,我们添加上摄像机在3D 空间移动,那样你就不会觉得乏味了。首先我们来看一个定点旋转摄像机的例子当作热身。

这个例子,我们还是使用我们的小P,不过是很多的小P,这样使我们的场景看起来更加的有层次感。运行我们的程序(效果如下),所有的物体都在围绕摄像机旋转,我想你会有摄像机在不停的旋转的错觉(或者没有…)。

 

定点旋转摄像机

动画制作步骤:

1. 一开始还是一些老步骤,设定原点,建立一个舞台,还有定义我们的摄像机,这些都是前几篇所讨论过的,就不再过多解释了。

// same as usual
var origin = new Object();
origin.x 
= stage.stageWidth/2;
origin.y 
= stage.stageHeight/2;
origin.z 
= 0;
var scene 
= new Sprite();
scene.x 
= origin.x;
scene.y 
= origin.y;
this.addChild(scene);
var camera 
= new Object();
camera.x 
= 0;
camera.y 
= 0;
camera.z 
= 0;
camera.panning 
= 0;
var focal_length 
= 300;

 

2. 下面定义一些常量,比如物体的总数量,PI和物体z间距。

// constants
var MAX_OBJ = 100;
var PI 
= 3.1415926535897932384626433832795;
var DISTANCE_Z 
= 20;                // the distance to your camera

 

3. 下面是初始化所有的物体,我们运用随机数产生小P所在的角度(对于摄像机),递增小P所在点到摄像机的距离(3D空间的),使用三角函数我们就可以得到小P的x和z,我们同样使用随机数产生它的y,最后把它添加到舞台上。

// now create lots of balls around your camera
for (var i = 0; i < MAX_OBJ; i++)
{
    var ball 
= new Sphere();
    ball.angle 
= Math.random()*(0PI*2+ PI*2;            // this is the rotate angle on the xz plane
    ball.dist_center = 140 + (MAX_OBJi)* DISTANCE;     // the distance to your camera
    ball.x_3d = Math.cos(ball.angle) * ball.dist_center;    // then we use trig to get x
    ball.z_3d = Math.sin(ball.angle) * ball.dist_center;     // and z
    ball.y_3d = Math.random()*(240240+ 240;          // now put the ball at random y
    scene.addChild(ball);                                            // add the ball to the collection
}

 

4. 对于每一个物体,我们在摄像机转动角度时刷新它的大小和位置。于是我们下一步写一个函数来达到目的,首先我们要确定小P相对于我们的摄像机的旋转角度。然 后我们根据这个角度和他们之间的垂直距离,算出它到摄像机的x,z和y的距离。最后,还是运用我们之前学过的算法,缩放和移动物体。

// update ball size and position
function update(obj)
{
    
// get the angle relative to your camera panning angle
    var angle = obj.angle  camera.panning;
    var x_pos 
= Math.cos(angle)*obj.dist_center  camera.x;        // use trig calculate the x
    var z_pos = Math.sin(angle)*obj.dist_center  camera.z;        // and z
    var y_pos = obj.y_3d  camera.y;                       // calculate the relative y
    
    
if (z_pos > 0)                                                  // if the ball isin front of the camera
    {
        
if (!obj.visible)                                
            obj.visible 
= true;                                    // make the ball visible anyway
            
        var scale 
= focal_length/(focal_length+z_pos);      // cal the scale of the ball
        obj.x = x_pos*scale;                              // calcualte the x position in a camera view 
        obj.y = y_pos*scale;                             // and y position
        obj.scaleX = obj.scaleY = scale;              // scale the ball to a proper state
    }
    
else
    {
        obj.visible 
= false;
    }
}

 

5. 写一个循环函数,在每一次执行时,递增摄像机的角度,并且刷新舞台上的所有的物体。

function run(e:Event)
{
    camera.panning 
+= 0.01;                           // increase the panning angle
    
    
if (camera.panning > 2*PI)
        camera.panning 
-= 2*PI;
    
if (camera.panning < 1*2*PI)
        camera.panning 
+= 2*PI;
    
    
for (var i = 0; i < scene.numChildren; i++)    // update all the balls on the screen
    {
        update(scene.getChildAt(i));
    }
}
// add loop event listener
this.addEventListener(Event.ENTER_FRAME, run);

注意:

这里我们提到的旋转,都是在保持y不变的情况下,横向旋转摄像机,换句话说,让我们的摄像机绕着y轴旋转,当然同理我们可以写出摄像机围绕着x轴旋转的操作。另外,同时进行上述两种旋转我将在后面的文章里进行介绍。

移动和旋转的组合

那 么现在你已经知道如何横向旋转摄像机,并且前几篇文章中也已经介绍了如何移动摄像机,如果我们把这两个操作结合在一起,那一定很棒。我想你应该觉得不会很 困难,因为我们前面已经把两个分开操作学会了,下面我们所要做的只是把这两种操作组合在一起。来看一个动画,其中发灰的摄像机是运动前的位置,另外一个是 向后(沿摄像机镜头的反方向)移动后位置(当摄像机镜头垂直向上看得时候移动得到),从动画中可以看到,对于摄像机镜头来说,景物的位置是不一样的。

移动加旋转摄像机

 

再来看一个图例,在这个图片中,摄像机沿BO方向向后移动,我们可以看出,摄像机的转角是不变的。那么我们就可以结合摄像机移动的位置和三角函数就可以算出它的x移动量(图中红色实线)和y移动量(图中蓝色实线),进而我们便可以算出对于移动后摄像机而言,小P的x和y。

 

移动和旋转角度的图解

需要注意的是,当你首先旋转摄像机,然后向后或者向前移动摄像机,那么摄像机是沿着摄像机旋转过后的角度运动的,至于移动量和物体到现在摄像机的距离,我们一样可以使用三角函数得到(三角函数!Nice!)。下面我们就看一个应用的例子:

 

定点旋转摄像机,WS前后移动摄像机,AD旋转

动画制作步骤

1. 重复前面的3步。

// constants
var MAX_OBJ = 100;
var PI 
= 3.1415926535897932384626433832795;
var DISTANCE_Z 
= 20;                                                 // the z distance to your camera
// same as usual
var origin = new Object();
origin.x 
= stage.stageWidth/2;
origin.y 
= stage.stageHeight/2;
origin.z 
= 0;
var scene 
= new Sprite();
scene.x 
= origin.x;
scene.y 
= origin.y;
this.addChild(scene);
var camera 
= new Object();
camera.x 
= 0;
camera.y 
= 0;
camera.z 
= 0;
camera.panning 
= 0;
var movement 
= 0;
var focal_length 
= 300;
var pan_left;
var pan_right;
var move_forward;
var move_backward;
// now create lots of balls around your camera
for (var i = 0; i < MAX_OBJ; i++)
{
    var ball 
= new Sphere();
    ball.angle 
= Math.random()*(0PI*2+ PI*2;                // this is the rotate angle on the xz plane
    ball.dist_center = (MAX_OBJi)* DISTANCE_Z;              // the z distance to your camera
    ball.x_3d = Math.cos(ball.angle) * ball.dist_center;        // then we use trig to get x
    ball.z_3d = Math.sin(ball.angle) * ball.dist_center;         // and z
    ball.y_3d = Math.random()*(300300+ 300;              // now put the ball at random y
    scene.addChild(ball);                                                // add the ball to the collection
}

 

2. 下面这个函数是和上面例子中不同的主要部分。首先我们要得到物体和摄像机的x,y和z距离,然后我们使用反三角函数就可以得出物体所在的角度,同时使用勾 股定理得到物体和摄像机的距离(注意y距离为0),同理使用三角函数我们便可以得到在摄像机移动之后物体的x和z。然后再根据物体的x和z对物体进行2D 空间的缩放和移动。

// update ball size and position
function display(obj)
{
    var x_pos 
= obj.x_3d  camera.x;            // calculate the x distance from obbject to the camera
    var y_pos = obj.y_3d  camera.y;            // and y distance
    var z_pos = obj.z_3d  camera.z;            // and z distance
    
    var angle 
= Math.atan2(z_pos, x_pos);                    // caculate the relative angle
    
// now get the actual object radius around camera
    var radius = Math.sqrt(z_pos*z_pos + x_pos*x_pos);
    
    x_pos 
= Math.cos(angle+camera.panning)*radius;    // get the x position after panning
    z_pos = Math.sin(angle+camera.panning)*radius;     // and y position
    
    
if (z_pos > 0)                                                    // if the ball isin front of the camera
    {
        
if (!obj.visible)                                
            obj.visible 
= true;                                      // make the ball visible anyway
            
        var scale 
= focal_length/(focal_length+z_pos);  // cal the scale of the ball
        obj.x = x_pos*scale;                                     // calcualte the x position in a camera view 
        obj.y = y_pos*scale;                                    // and y position
        obj.scaleX = obj.scaleY = scale;                     // scale the ball to a proper state
    }
    
else
    {
        obj.visible 
= false;
    }
    
    txt_z.text 
= int(camera.z)+"";
    txt_panning.text 
= Number(camera.panning*(180/Math.PI)).toFixed(1+ "";
}

 

3. 写一个循环函数,在每一次执行时刷新舞台上的所有的物体。

function run(e:Event)
{
    
if (camera.panning > 2*PI)
        camera.panning 
-= 2*PI;
    
if (camera.panning < 1*2*PI)
        camera.panning 
+= 2*PI;
    
    
for (var i = 0; i < scene.numChildren; i++)                    // update all the balls on the screen
    {
        display(scene.getChildAt(i));
    }
}

 

4. 下面设置一些键盘相应事件,使用WS我们可以使摄像机前进和后退,AD旋转我们的摄像机。键盘事件我们在前面提到过,就不多说了,如果有什么问题的话可以查看一下前面的例子。

function run(e:Event)
{
    
if (camera.panning > 2*PI)
        camera.panning 
-= 2*PI;
    
if (camera.panning < 1*2*PI)
        camera.panning 
+= 2*PI;
    
    
for (var i = 0; i < scene.numChildren; i++)             // update all the balls on the screen
    {
        display(scene.getChildAt(i));
    }
}
function key_down(e:KeyboardEvent):
void
{
    
if (e.keyCode == 65)
        pan_left 
= true;
    
if (e.keyCode == 68)
        pan_right 
= true;
    
if (e.keyCode == 87)
        move_forward 
= true;
    
if (e.keyCode == 83)
        move_backward 
= true;
}
function key_up(e:KeyboardEvent):
void
{
    
if (e.keyCode == 65)
        pan_left 
= false;
    
if (e.keyCode == 68)
        pan_right 
= false;
    
if (e.keyCode == 87)
        move_forward 
= false;
    
if (e.keyCode == 83)
        move_backward 
= false;
}
function key_response(e:Event):
void
{
    
if (pan_left)
        camera.panning 
+= 0.015;                    // increase the panning angle
    if (pan_right)
        camera.panning 
-= 0.015;                    // decrease the panning angle
    if (move_forward)
    {
        movement 
= 20;
    }
    
if (move_backward)
    {
        movement 
= 20;
    }
    
if (move_forward || move_backward)
    {
        camera.x 
+= Math.sin(camera.panning)*movement;
        camera.z 
+= Math.cos(camera.panning)*movement;
    }
}
// add loop event listener
this.addEventListener(Event.ENTER_FRAME, run);
this.addEventListener(Event.ENTER_FRAME, key_response);
stage.addEventListener(KeyboardEvent.KEY_DOWN, key_down);
stage.addEventListener(KeyboardEvent.KEY_UP, key_up);

 

注意:

我们在这两个例子里,并没有涉及到物体层次,当然如果你在开发的时候,最好加上一个层次排序。这个算法我们在第一篇文章里就已经实现,你可以试着把那个函数添加到这两个例子里。

建议:

在开发的时候,我建议你使用面向对象的书写方式,这样便于你的管理。我一直没有使用OO的写法,是因为我不想给读者的阅读造成不必要的困惑,你可以试着把 例子中的代码写成类,然后从.fla文件调用。例如你可以我们例子中的小P写成一个类,它具有x_3d,y_3d,z_3d和其他一些属性。

 

点此下载程序源文件


作者:Yang Zhou
出处:http://yangzhou1030.cnblogs.com
感谢:Yunqing
本文版权归作者和博客园共有,转载未经作者同意必须保留此段声明。请在文章页面明显位置给出原文连接,作者保留追究法律责任的权利。

[Flash]Flash与3D编程探秘(四)- 摄像机旋转基础知识

mikel阅读(832)

关于三角函数

现在我们已经基本上把最基本的移动摄像机技巧介绍完了,并且我相信上篇的几个例子也应该让你加深了印象。可是你会发现前面我们只是把摄像机沿着x 轴,y轴或者z轴移动我们的摄像机,可是实际中我们可以把摄像机向左,向右,向上或者向下旋转一定的角度,这样我们在观看空间时就有更大的自由度。 不过介绍旋转之前,我们需要知道一些关于三角法的计算(Oh,No!),如果你还对三角函数不熟悉,那么文章看起来可能会有些费解。不过不要担心,中国人 有着聪明的头脑,这些对你来说很容易。提醒一下:如果下面的内容对你来说太容易了,那么你可以跳过这些。不过我想我还是给已经忘记的(Just like me)各位补上一课,毕竟就是这些简单的知识驱使着我们的3D空间旋转着。

 

三角法是数学的一个分支,我们主要用它来分析三角形的边 和角度的关系。为什么要三角形要和角度有关系呢?任意一条在2D空间里有旋转角度的直线,它都会在x轴和y轴有相应的映射(当然在3D空间里, 我们并不光有x轴和y轴)。我们假设有一条线段从原点到点B(4,0),当你把这个线段哦OB沿着原点旋转一定角度,过B(旋转后)向x轴做一条垂线 BC,那么x轴和BC,OB所组成的就是一个直角三角形。当然你可以把OC看作是线段OB在x的投影。那么对这个旋转角度我们用sin和cos就可以计算 得到我们这条线段在x和y的分量。

 


旋转一条直线得到的三角形

 

以原点为中心旋转小P

 

以原点为中心旋转只是个例子,这里提到的旋转点不一定是原点,我们可以把任意一点作为旋转的原点。你会发现在旋转中,当x线段OB旋转一定角度后, 它会与旋转前的线段重合,B在旋转中所过的点的轨迹就是一个圆,那旋转的这条线段就OB就是这个圆的半径。OK,这就是我们在Flash里需要知道基本三 角函数。下面两个是我们根据圆的半径得到x和y分量的公式:

object.x = Math.cos(angle) * radius;
object.y = Math.sin(angle) * radius;

 

 

旋转后的直线在x和y轴的分量

当然,上面的公式在旋转点为原点的情况下成立,如果旋转点不是原点的话,我们使用:

object.x = origin.x + Math.cos(angle) * radius;
object.y = origin.y + Math.sin(angle) * radius;

 

 

弧度

当我们测量物体小P的旋转角度的时候,我们可以使用度数,这也是我们最常用的,它从0到360沿逆时针方面递增。但是我们所用的Flash,并不知 道 360是什么,它所知道的只是弧度(当然你可以自己写sin和cos函数,用0到360作为你的参数)。这里的弧度我们可以这样理解,360度数是 2*PI弧度,那么就是说360度旋转是一个整圆的话,2*PI旋转也是同样效果。把度数转化成弧度的公式是:

randians = degrees * (Math.PI / 180); PI = 3.1415926535897932384626433832795…

 

 

反三角函数

在Flash里,我们可以通过直角三角形的两个边的比率得到角度,用下面的代码即可:

angle = Math.atan2(object.y, object.x);
angle 
= Math.atan(y/x);

 

还有一个要说明的那就是勾股定理:

hypotenuse = Math.sqrt(x*+ y*y);

 

那么你现在已经具备2D旋转的基本知识了,我们再看一下3D,3D旋转中加进了z轴,hum,那么我们就把xy平面,yz平面,zx平面的旋转组合 起来,就得到摄像机的全方位旋转。不过指得注意的是,三角法不能直接运用到我们的摄像机旋转中,我们可以在横向旋转摄像机的时候保持摄像机的旋转角度不 变,取而代之我们旋转x和z轴,要旋转180度的话,我们可是使z旋转到x的位置然后再转到z的位置,同时保持y轴不动。

旋转x和z轴

 

横向旋转摄像机

上面讲述了一些旋转摄像机的原理,我们更关心的是如何使用这些原理来解决问题。联想一下实际,对于摄像机的横向旋转来说,摄像机的所在位置的高度 y,深度z和横向x都是保持不变的,唯一改变的就是摄像机的旋转角度。也就是说当一个空间中的物体在位置不变的情况下,摄像机与它的距离是不变的,于是我 们的问题转化成在角度变化的情况下保证它们的距离不变。我们设旋转后的角度为a,那么物体所在的x和y(以摄像机为原点建立坐标系)就是:

= distance * Math.cos(a);
= distance * Math.sin(a);

 

如果我们摄像机所在的坐标为(x0,y0)的话,我们就可以得到物体所在的坐标(还是上面讲过的公式):

= x0 + distance * Math.cos(a);
= y0 + distance * Math.sin(a);

 

以上所述的仅是对一个物体的操作,同样道理我们对所有舞台上的物体进行操作,那么我们所看到的就是我们摄像机旋转后的景象。

 

      

对比旋转物体和旋转摄像机

 

在上面的动画中,我们俯瞰整个场景,对比一下旋转摄像机和旋转舞台上所有物体的差异。当然对于其他的平面(纵向旋转摄像机)来说,我们可以运用相同 的理论对 物体进行操作。那么,你应该有种感觉你已经具备所有摄像机旋转的理论知识了,不过还有一小点,我们将在下一篇文章进行分析。

作者:Yang Zhou
出处:http://yangzhou1030.cnblogs.com
感谢:Yunqing
本文版权归作者和博客园共有,转载未经作者同意必须保留此段声明。请在文章页面明显位置给出原文连接,作者保留追究法律责任的权利。

[C#]BlogEngine.Net架构与源代码分析系列part2:业务对象——共同的父类Busin

mikel阅读(791)

上篇文章朋友的评论给了我很大的动力写这个系列的文章,看来大家都比较关注这个系列。为了后续文章做一个铺垫,我在这篇文章的前半部分讲解一下BlogEngine.Net的整体架构,后半部分主要是对于BusinessBase类的分析。

     下载源代码以后打开解决方案,我们发现从项目的组织结构上BlogEngine.Net分成两个项目:一个是 BlogEngine.Core,顾名思意,它就是BlogEngine.Net的核心逻辑层。所有的业务逻辑和一些功能都在这个项目中体现,实际上这个 核心业务层中也有数据访问的一部分,那就是Provider模式。在BlogEngine.Net中,关系数据库或XML等的作用只有一个,那就是存储数 据,BlogEngine.Net的业务对象的ID生成是由核心层控制的,而不是用数据存储部分生成的,因为这样可以支持更多的数据源。它不同于很多其他 业务系统,数据库里面可能有很多存储过程,触发器,函数等来完成一定的业务运算和数据处理。在BlogEngine.Net中,我们甚至可以使用一 个.txt文件来自己开发一个Provider给BlogEngine.Net使用,方法很简单,只要实现 BlogProvider(BlogEngine.Net提供),MembershipProvider和RoleProvider就可以了,实际上 BlogEngine.Net也在很大程度上利用了.Net本身的经典模型。另外的一个项目是一个站点,主要就是具体的Web实现,但是具体的功能都是调 用核心层来完成的。

     实际上刚开始看BlogEngine.Net的源代码时我也很难入手,不知道从哪里看起,找不到入口的地方。其实也难怪,官方提供的资料 大都是关于使用和开发扩展的,社区里找到的东西也不是自己最想要的。研究了一段时间以后我发现整个BlogEngine.Net都在围绕这 BusinessBase这个基类展开,其它的类都是为它提供服务或接收它的消息,例如Provider,Extension等。 BusinessBase是所有业务类的基类,里面封装了很多业务类共有的特征。它的子类有:

AuthorProfile:用户的Profile的封装。

Page:这个类实际上是对应着BlogEngine.Net中的一篇静态文章,page和post具体区别不是很重要,感兴趣的朋友可以参照一下官方提供的说明。

Post:在BlogEngine.Net应用最多的一个类,代表作者提交的一篇文章。

Category:文章分类,一篇文章可以属于多个分类,分类之上还可以有父分类。

     下图是他们的继承关系:

     

 图中的IPublishable接口我会在以后的文章中做详细的讲解。

 从BusinessBase的原型

BusinessBase原型

我们可以看出:

1.BusinessBase是一个泛型类。这个泛型设计的很好,Type用来标识具体的子类类型,Key主要是子类对象的唯一ID,这个ID在AuthorProfile是String类型,而在Page等其它类中是Guid类型,所以定义基类时才会采取这种泛型设置。

2.BusinessBase实现的接口也是微软推荐的业务对象应该实现的接口。

IDataErrorInfo用来标识对象内部的错误信息,这个接口的实现主要用来定义某个属性的验证规则。
INotifyPropertyChanged用户通知客户端数据发生了改变。主要是对象内部数据发生改变时,对于一些绑定控件数据的同步更新。
IChangeTracking如果对象内部数据发生改变,它用来完成接收了数据的改变,包括更新数据存储等。
IDisposable这个就不用解释了吧,做.Net都知道。

     从以上我们可以看出凡是BusinessBase的子类对象都具有当修改数据时,通过属性的改变对外发出属性改变的通知,并实现 INotifyPropertyChanged来通知绑定控件,实现IChangeTracking来更新数据的存储。我们再看一下源代码发现 BlogEngine.Net将所有业务对象的数据验证交给了Validation模型来处理,这一点运用的很巧妙,统一了验证模型,我很推荐。

Validation

     那么,BusinessBase的子类都需要做什么呢?它们需要重写数 据存储的操作方法(通过Provider的调用完成,主要是DataSelect等),这也是面向接口编程所提倡的。对于内部数据的处理 BusinessBase使用了IsNew,IsChanged和IsDeleted统一了编程模型,并定义了一个SaveAction枚举来实现统一的 处理与通知消息的封装。

     BusinessBase提供了两个事件在内部数据进行存储前后触发——保存前事件和保存后事件,用来给外部提供访问点,有点类似于ASP.NET的管道 事件的东西,不过这些事件都是类的事件,并非属于某个对象。这样外部可以对于保存前的事件进行处理,也可以对于保存后的事件处理,这种模型很有利于扩展。 例如我们可以很轻松的纪录业务日志,而且纪录的很统一。此外,BusinessBase重写了相等的方法或操作符,用于两个对象的排序和比较,这都是业务 对象所共有的特性。

     那么,对于派生类从这个基类继承之后我们要实现什么呢,无非就是自己的数据存储方法,数据检验规则,自己的ID类型,还需要大家注意的就是每个派生类都提 供了一个静态属性用来提取整个对象列表,还可以根据具体的查询信息获得对象列表,不过他们都属于类的方法,也就是静态方法。对于结构和关系比较复杂的派生 类,例如Post包含了Comment列表及其相应的方法用来表示和操作对象之间的关系。此外基类的MarkOld方法用于标识这个对象已经经过了处理, 不需要在处理了,子类可以重写这个方法用于提供自己的实现。其余的一些方法或属性都是具体类中所需要的,这个很好分析。

     例如,对于客户端程序我们只要这样就可以完成数据操作:

添加文章:

Code

修改文章:

Code

删除文章:

Code

      很明显,BlogEngine.Net采用了面向对象的设计方法,事件和继承得到了很广泛的应用,此外它更好的运用了.Net平台自身 提供的模型解决问题,例如Provider模型,一些接口规范等。此外我们可以看到,BlogEngine.Net在内存中有很多对象,以空间换取时间的 处理方法在这样的系统中还是比较可靠的。

     做一些总结是很有必要的。

     上一篇:BlogEngine.Net架构与源代码分析系列part1:开篇介绍

版权声明
作者:Thriving.country
出处:http://thriving-country.cnblogs.com/
本文版权归作者和博客园共同所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接.