存档

‘软件设计’ 分类的存档

转载:Top 10 Internet Startup Scalability Killers

2010年2月6日 admin 没有评论

原文地址 http://gigaom.com/2009/12/20/top-10-internet-startup-scalability-killers/

  • Thinking Scalability is just about technology 认为Scalability 只是一个技术问题
  • Overuse of Synchronous calls 过度使用同步调用
  • Failure to weed or seed soon enough 不能够快速消除表现欠佳者并培养优秀的人才
  • Inappropriate use of databases 不合理地使用数据库
  • Cesspools instead of swim lanes 未能实现对故障以及失败的隔离
  • Reliance on Vertical scale 依赖纵向的扩展
  • Failure to Learn from History 不能从历史的失败中吸取教训
  • Changing Development methodologies to fix problems通过改变开发的方式方法来解决问题
  • Too little caching, too late 缓存的使用太少或者太晚
  • Overreliance on Third parties to scale过度依赖第三方来实现可扩展

更多的内容可以参考近期出版的新书“The Art of Scalability

分类: 软件设计 标签:

软件开发沉思录阅读笔记一

2010年2月6日 admin 没有评论

软件开发沉思录简介

从编程技术到项目管理,Roy Singham、Martin Fowler、Rebecca Parsons等来自ThoughtWorks的思想领袖通过本书中的13篇美文,将自己多年沉思和实践所得倾囊相授,引领你走向敏捷软件开发的成功之路。
本书内容丰富,涵盖了软件开发的各个阶段,既包含DSL、SOA、多语言开发和领域驱动设计等热门主题,也有对象设计、一键发布、性能测试和项目管理等方面的经验之谈和独到见解。不论你是开发人员还是项目管理人员,都将从本书中获益匪浅。

第六章 对象健身操

  • 优秀设计背后的核心概念其实并不高深。比如内聚性、松耦合、零重复、封装、可测试性、可读性以及单一职责。这七条评判代码质量的原则就已经被广泛接受了。然而真正困难的是如何把这些概念付诸实践。理解了封装就是隐藏“数据、实现细节、类型、设计或者构造”,这只是设计出良好封装代码的第一步而已。
  • 更为严格的编码标准。
    • 方法只使用一级缩进
    • 拒绝使用else关键字
    • 封装所有的原生类型和字符串
    • 一行代码只有一个“.”运算符
    • 不要使用缩写
    • 保持实体对象简单清晰
    • 任何类型中的实例变量都不要超过两个
    • 使用一流的集合
    • 不使用任何Getter/Setter/Property
      • 这条规则通常被描述为“讲述而不要询问”(“Tell, dont’t ask”)
  • 如果努力地拥抱简单性,那么开发过程会快乐得多。
分类: 软件设计 标签: ,

转载:做人、做事,做架构师——架构师能力模型解析

2010年1月31日 admin 没有评论

要想从一名普通程序员发展成为优秀的架构师,“个人特性”与“技术技能”缺一不可;而“技术专业能力”、“人际关系能力”和“业务能力”更是优秀架构师重要的三种能力。
/ 周爱民(《程序员》2008年4月刊)
引子
究竟是什么让你在同一个位置上——例如程序员或技术负责人——工作了三年、五年或者更久,而仍然得不到任何的发展空间?你觉得自己已成为技术圈中的大牛,并信心满满地去拿明天就要颁发的某某大奖,然而却仍然停留在同样的技术职位上,去年到今年涨的薪水甚至填不平物价升幅?于是,你开始对老板不满,对员工不满,对昨天升职的那个同事不满……你开始计划明天就要跑单,或者准备考虑提出加薪却又心怀忐忑。
如果技术人员有发展的轨迹,那么他要么“看透工具的本质,把关注点转移到‘团队’的圈子里去”,要么“顺着代码铺就的道路,亦步亦趋地成为良匠大师”。仅以技术方向而言,你大概可以做到架构师、总架构师甚至首席架构师;但问题是:你现在还只是一个程序员。那要如何才能踏上通往架构师之路呢?本文为你解析一个架构师的能力模型。
你能不能做一个好的架构师?
架构师不是界定一个技术高下的职位名称,而是一个职务。所谓职务,包括职——职位,务——工作。前者决定了你具备哪些资源,可以影响到怎样的范围,以及面向的机构,后者则简单地是你需要完成的工作列表。
所以我说“架构师”不是指“一个能做架构的人”。前者是把架构师当职能,后者是当工人。能做一份工作列表中的事,并不等于就成为相应职位上的人。在管理体系里面,你的个人特性决定了你在哪个位置,而技术技能只是做事实施的必需。架构师这个职务,同时要求较高的个人素质和技术能力,因此它的进取之路总结起来就是:做人、做事,做架构师。
因此“模型”由“个人特性”和“技术技能”两个方面构成,在第一张图中,我特别说明“个人特性”既包括人际关系的能力,也包括(具体)业务能力;“技术技能”也是如此。所以个人特性主要与“做人”有关,部分地也包含“做事”的要素。

dd1
                                                           图1 架构师能力模型
“有效沟通”以及“学会谈判”与做具体的事无关,是个人能力特性的公共方面。前者是过程,后者是知道如何定目标与求结果。而“风险与防备”是做事过程控制的关键,与前面两项正好构成了一个做事基本能力的完整体系。基本上,这三项个人特性都是一个“普通程序员”所不具备的,甚至在大多数情况下,普通程序员并不愿意去具备这样的个人特性,因为在许多陷于技术泥淖的开发人员看来:沟通总是会使事情变得更加麻烦,谈判则徒耗时间而无济于事。然而事实上,在整个的架构决策过程中,架构师需要不停地沟通与谈判。将“架构”变成“决策”的过程,其实就是对各个技术角色(及其思想)兼容并包的过程,你需要不断地协调需求、实现之间的各种问题,也需要面对各种投资者(时间、资金、人才等方面的决策者)进行谈判,以确定项目的规模——没有规模也就没有范围,没有范围如何展开设计呢?
一部分开发人员会认为上述过程是“项目经理”的事情,但真的如此吗?当你作为一个更高级别的架构师,以至于要影响到多个项目的决策时,你就全然不会有这种感受了。因为这种情况下,你的决策将先于项目的启动,或者说你已经不单单是一个技术角色了。
设计是架构能力的一部分,但架构师不是设计师——看清楚二者之间的不同,你才真正迈出了架构师职业生涯的第一步。
抽象是思维能力、模型化是表达能力
个人特性中另一个非常重要的方面是“抽象思维”,而这是与架构师角色直接相关的一种能力。这种能力既有职业技能特征,又是普遍性的能力。
所谓普遍性的能力,是指“抽象”在我们——作为人这种个体的——生活中无处不在。例如我们说花、草,说桌、椅……我们用语言去指称任何一个既已存在的(可以脱离我们的语言而自然存在的)事物时,就用到了抽象。说“桌子”的时候,既没有描述桌子的具体形式,也没有说明它的规格,但我们用这个名词时,所有人都知道“桌子是什么”。所以,名词概念是整个抽象逻辑系统中的主体。如果失去了这些名词定义,我们基本上不能说话,也不能描述任何东西——那便到了“只可意会不可言传”的境地。
用现有的成熟语汇去描述你的系统时,大多数人会理解你所表达的含义,例如我们说“这个系统设计为一个三层结构”。然而架构师面临的系统在许多细节上并不见得能够用成熟的语汇去描述,因此必须自已构建一个抽象系统,这就需要概念抽象能力、概念表达能力和基于概念的逻辑表达能力。
概念抽象能力是一种思维能力。简单地说,就是“把目标分解或概括清楚”:你要么概而言之“它是什么”,要么详细地说明“它包括什么”。必须使用大量的语汇来陈述这个“什么”,这不单单是表达为文字,也表达为你在思想过程中的一个完整系统。通常用的方法是“映射系统”。例如你可以用数学中的“数轴”来映射“实数域”。将目标系统形式化为一个概念化的、可讨论的结构系统后,你的抽象过程就基本结束了。
dd3
                                          图2 能力模型中的个人特性
然而这个“抽象系统”可能只构建在你的思维意识里,还必须把它描绘出来。因为不能只是你自己思考清楚,系统就能设计完成。这个“描绘”就依赖于后面两种表达能力,一种是描绘概念实体,一种是描绘实体上的逻辑——有趣的是,这似乎又回到了“程序=结构+算法”。
现在大家回过头来看看UML,或者更多种类的ML(建模语言),他们就用于表达这两个方面的东西:要么是概念实体(例如用一个框表明系统边界),要么是实体上的逻辑(例如用箭头表明逻辑时序)。
所以大家应该清楚,我们再如何称赞UML,它也只是一种对模型化系统的“表达能力”,你只能把它当一种辅助表达的工具去使用,它本身既不能帮助思考,也不见得能作为抽象过程中的或抽象思维环境中的参考。
任何一个优秀的架构师都有自己独特的思考方式,这决定了他如何抽象系统,以及如何“创造性地”设计与构画这个系统。这种“独特的思考方式”贯彻他从孩童开始的整个成长过程,直至他形成独立的社会观、人生观与世界观。他认识世界的方式和接受世界的能力决定于他如何思考,也反映了他这种思考方式的“独特性”。但这并不表明他有特立独行的行为特性(我们这里只说他的思考方式),我们不应介意他是否用某种语言(例如UML或者形式化编程语言)来表达他的思考结果。
推动:设计做大,实施做小
架构师首先是把问题的真正目标确定下来,然后变成系统设计、平台设计或架构设计。而在此之后设计输出将会有两个方向的发展,一是被忠实地贯彻下来,二是被变形地发展下去。两个方向都存在致命的危险:架构最终能否被完整实现。对前者来说,可能是架构设计过度,或设计本身出现了错误;后者则是对架构直接的伤害。
所以架构师必须参与实施的全程——尤其是在架构被映射为目标系统的前期。在这个阶段中,架构师的任务就是推动架构实施,以保证在项目全程的设计/架构/体系的一致性。除了直接跟设计师或设计团队沟通,以保证他们的设计在你可以控制的范围之内以外,架构师还必须有阶段化设计的能力。这种能力用于将一个原本规模宏大的架构设计,变成较小的、易于实施的、对开发团队来说可控的关键点。例如在体系层次的规划上,设计可能是独立、异质的、可迁移的存储框架来实现数据层,但在(前期的)实施上,这里可能被表达为本地数据库,并要求前端开发人员必须通过一个清晰的数据交互层来访问——例如一组数据存取接口,或一个独立数据服务组件。开发人员可能在这里遇到障碍:因为要通过这些中间层来访问本地数据库,纯粹是多余的。然而,正是这“多余的工作”提供了系统弹性,为并行团队开发公共存储服务争取了周期,也为将来的灵活部署与数据迁移提供了可能。
这里的关键就在于,无论原始系统设定有多大,实施时总是在“做小”。每一个局部的实施块都是可控的,并为它在整个体系空间中留下了位置和接口,这样才可能由“小的部分”做大。一个大系统的架构师可能同时在考虑许多个项目中的、不同位置的架构,并且清楚这些项目最终的总体规模。而这,就是平台架构师和体系架构师所涉的领域。
dd4
                               图3 架构师模型图中的“实现能力”
架构真的是“好不好”的问题吗?如同我对工程的理解一样,架构问题的根本,也并不在于它是否完美或漂亮,而是在于是否合用。因此架构师必须对实施架构的团队以及实施过程有充分了解,知道他们的能力缺陷,知道实现过程要消耗的资源,清楚每个环节可能的故障以及先兆。只有这样,架构师才能设计一个让这个团队能实现,而且在实现过程中能受控的架构。
要知道,你作为架构师被请来,不是画几张图纸交给项目经理,说:你们去做吧,做不出来是你们不会做。即使你可以身体力行,在这个团队中教大家、培养大家,那么公司的开销呢?风险呢?这些东西难道就不考虑了?项目的周期因为实现的复杂程度而无法控制时,项目就死掉了。那么,追根究底来说,是不是架构师的问题?是啊,你为什么会做了一份“不合用”的架构呢?——你都不知道项目如何开发、由谁实施、如何管理等等,又如何能面对这些实际环境去设计架构呢?
所以这一部分能力,是要在你的开发经验、团队经验以及用人识人的经验中去找的。参考模型图的“实现能力”下的“设计能力→了解你的主要沟通对象”和“架构推行”等分支,对你或有一些可用的提示。
局部与全局
架构是一个从全局到局部的过程,而实施正好反过来,是从局部到全局。这也正是“设计做大,实施做小”的另一个层面的含义。设计大才可以见到全局,才知道此全局对彼全局的影响;实施小才可能关注细节,才谈得上品质与控制。
事实上,大多数情况下架构是在为“当前项目之外”去考虑,这可以看成全局关注的一个组成部分。因此我们需要界定所谓“全局”的范围:超出公司或整个产品系列、产品线或规划的范围才是多余的。
所以当架构决策谈及“全局”时,其目标并不见得是“保障当前项目”,而又必须由当前项目去完成。
一个经常被用到的例子是:如果仅为当前项目考虑,那么只需要做成DLL模块;如果为产品线考虑,可能会是“管道+插件”的结构形式。而“管道+插件”的形式显然比做成DLL模块更费时,这个时间成本(以及其它成本)就变成了当前项目的无谓开销。
这种全局策略对局部计划的影响是大多数公司不能忍受的,也被很多团队所垢病。然而这却是架构师角色对体系的“近乎必然”的影响——如果你试图在体系中引用架构师这个角色的话。一些情况下,体系能够容纳这种影响,例如“技术架构师”试图推动某种插件框架,而正好开发人员对这项技术感兴趣,那就顺其自然地花点工夫去实现了。但如果不是这样,实施者或实施团队看不到“多余的部分”对他们的价值时,来自局部的抵触就产生了。
这种情况下,平衡这些抵触就成了架构推行的实务之一。在我看来,“平衡”是全局的艺术和局部的技术。也就是说,一方面架构师要学会游说,另一方面也要寻求更为简洁的、成本更小的实现技术。只有当整个体系都意识到(你所推行的)架构的重要性,而且实施成本在他们可以接受的范围之内时,他们才会积极行动起来。
所以所谓平衡,其实也是折衷的过程。构架师只有眼中见大,才知道哪些折衷可以做,而哪些不能。所谓设计评估(模型图中的实现能力->设计能力->设计评估分支)并不是去分析一个设计结果好或不好,而是从中看到原始的需求,看到体系全局的意图,然后知道在将设计变得更为“适当”时可以做哪些折衷。同样的原因,架构师也必须知道自己的决策会产生的影响,才能控制它们,以防它们变成团队的灾难。有些时候,架构师甚至需要抛弃一些特性,以使得项目能够持续下去。因为产品的阶段性产出只是整个战略中的一个环节,而不是全部。
其它
“怎么做一个架构师”这个问题得分成两个部分来看,一个是“做到”,一个是“做好”。由于架构师本身不过是一个技术职位,所以时机成熟了自然会做得到。但问题是,真有一天你被放在这个位置上了,你能做得好吗?
我浏览过几套所谓培训机构的有关架构师的教程,也翻阅过一些讲架构的书。我发现他们普遍地是将架构作为一种“职业技术”来讲,就像培养程序员或者缝纫工一样来教育。但就我的经验来说,架构并不是一件纯粹表现技术能力的工作,所以并不是翻几本书学几种方法就可以投入“实战”的。更深层的问题是,架构师其实不是“战”出来的。昨天跟同事讨论这个话题,他把我们这几年来的一些思考用了三句话来概括,非常精彩:从无到有的,是架构;从表到里的,是抽象;从粗到细的,是设计。
那么到底什么是架构呢?从上面的概括中你是看不到答案的。到底如何做架构呢?从本文中你也是看不到答案的。然而我说,“你看不到答案”的根源其实是在于你的眼光与心性——后面这个词换成现代白话,就是“思想”。真正阻碍了你成为优秀架构师的,也许正是你既有的知识与思想方法,扔掉它们,接受一些全然有别的信息,也许正是良好的开端。
或许你现在正愤愤然:这篇文章怎么空洞无物?——我甚至能想象到一些读者的表情。然而请在问题面前停下来,不要急于给出答案。正如你将“?”稍微变下形,它就成为了“!”一样,问题的本身,就是答案。
作者简介
周爱民(aimingoo),具有十余年的软件开发、项目管理和团队建设的经验,现担任盛大网络的平台架构师,著有《大道至简》、《Delphi源代码分析》等。

分类: 学习, 软件设计 标签:

转载:WordPress数据表结构

2010年1月28日 admin 没有评论

wp_categories

用于保存分类相关信息的表。包括了5个字段,分别是:
cat_ID – 每个分类唯一的ID号,为一个bigint(20)值,且带有附加属性auto_increment。
cat_name – 某个分类的名称,为一个varchar(55)值。
category_nicename – 指定给分类的一个便于记住的名字,也就是所谓的slug,这是一个varchar(200)值。
category_description – 某个分类的详细说明,longtext型值。
category_parent – 分类的上级分类,为一个int(4)值,对应是的当前表中的cat_ID,即wp_categories.cat_ID。无上级分类时,这个值为0。

wp_comments

用于保存评论信息的表。包括了15个字段,分别为:
comment_ID – 每个评论的唯一ID号,是一个bigint(20)值。带有附加属性auto_increment。
comment_post_ID – 每个评论对应的文章的ID号,int(11)值,等同于wp_posts.ID。
comment_author – 每个评论的评论者名称,tinytext值。
comment_author_email – 每个评论的评论者电邮地址,varchar(100)值。
comment_author_url – 每个评论的评论者网址,varchar(200)值。
comment_author_IP – 每个评论的评论者的IP地址,varchar(100)值。
comment_date – 每个评论发表的时间,datetime值(是加上时区偏移量后的值)。
comment_date_gmt – 每个评论发表的时间,datetime值(是标准的格林尼治时间)。
comment_content – 每个评论的具体内容,text值。
comment_karma – 不详,int(11)值,默认为0。
comment_approved – 每个评论的当前状态,为一个枚举值enum(’0′,’1′,’spam’),0为等待审核,1为允许发布,spam为垃圾评论。默认值为1。
comment_agent – 每个评论的评论者的客户端信息,varchar(255)值,主要包括其浏览器和操作系统的类型、版本等资料。
comment_type – 不详,varchar(20)值。
comment_parent – 某一评论的上级评论,int(11)值,对应wp_comment.ID,默认为0,即无上级评论。
user_id – 某一评论对应的用户ID,只有当用户注册后才会生成,int(11)值,对应wp_users.ID。未注册的用户,即外部评论者,这个ID的值为0。

wp_linkcategories

用于保存在WP后台中添加的链接的相关信息的表。包括13个字段:
cat_id – 每个链接分类的唯一ID,bigint(20)值,为一个自增量auto_increment。
cat_name – 每个链接分类的名字,tinytext值。
auto_toggle -这个字段所包含的是一个比较特别的属性。如果为Y,则当该分类中加入了新链接时,其它的链接会变为不可见。它是一个枚举型的值enum(’Y’,’N’),默认为N。
show_images – 该字段也是枚举值enum(’Y’,’N’),默认为Y。用户指定是否允许在该链接分类显示图片链接。
show_description – 该字段指定相应的链接分类下的链接,是否再专门[换行]显示它们的说明,这是一个枚举型值enum(’Y’,’N’),默认为N,即不显示说明(但会通过title属性中显示说明)。
show_rating – 显示该分类下链接的等级。它也是一个枚举值enum(’Y’,’N’),默认为Y。此时,你可以用链接等级的方式来对该链接分类下的链接进行排序。
show_updated – 指定该链接分类有更新是,是否进行显示,枚举值enum(’Y’,’N’),默认为Y。
sort_order – 指定该链接分类中链接的排序依据,varchar(64)值。一般用链接的名字(name,即wp_links.link_name)或ID(id,即wp_links.link_id)。
sort_desc – 指定链接分类的排序方式,枚举值enum(’Y’,’N’),默认为N,即用降序。
text_before_link – 该链接分类下每个链接的前置html文本,varchar(128)值,默认是’列表开始标签’。
text_after_link – 该链接分类下每个链接的中,链接与说明文字(wp_links.link_description)之间的html文本,varchar(128)值,默认是’换行标签’。
text_after_all – 该链接分类下每个链接的后置html文本,varchar(128)值,默认是’列表结束标签’。
list_limit – 用于规定某一链接分类中显示的(可设定的?)链接的个数,int(11)值,默认为-1,即对链接分类下链接的个数无限制。

wp_links

用于保存用户输入到Wordpress中的链接(通过Link Manager)的表。共14个字段:
link_id – 每个链接的唯一ID号,bigint(20)值,附加属性为auto_increment。
link_url – 每个链接的URL地址,varchar(255)值,形式为http://开头的地址。
link_name – 单个链接的名字,varchar(255)值。
link_image – 链接可以被定义为使用图片链接,这个字段用于保存该图片的地址,为varchar(255)值。
link_target – 链接打开的方式,有三种,_blank为以新窗口打开,_top为就在本窗口中打开并在最上一级,none为不选择,会在本窗口中打开。这个字段是varchar(25)值。
link_category – 某个链接对应的链接分类,为int(11)值。相当于wp_linkcategories.cat_id。
link_description – 链接的说明文字。用户可以选择显示在链接下方还是显示在title属性中。varchar(255)值。
link_visible – 该链接是否可以,枚举enum(’Y’,’N’)值,默认为Y,即可见。
link_owner – 某个链接的创建人,为一int(11)值,默认是1。(应该对应的就是wp_users.ID)
link_rating – 链接的等级,int(11)值。默认为0。
link_updated – 链接被定义、修改的时间,datetime值。
link_rel – 链接与定义者的关系,由XFN Creator设置,varchar(255)值。
link_notes – 链接的详细说明,mediumtext值。
link_rss – 该链接的RSS地址,varchar(255)值。

wp_options

用于保存Wordpress相关设置、参数的表,共11个字段。最重要是的option_value字段,里面包括了大量的重要信息。
option_id – 选项的ID,bigint(20)值,附加auto_increment属性。
blog_id – 不详。或许用在单在用户的WP版本上并不重要吧,或许是针对不同用户的Blog来设置的一个值。int(11)值,默认为0,即当前blog。
option_name – 选项名称,varchar(64)值。
option_can_override – 该选项是否可被重写、更新,枚举enum(’Y’,’N’)值,默认为Y,即可被重写、更新。
option_type – 选项的类型,作用不详,int(11)值,默认为1。
option_value – 选项的值,longtext值,这个字段的内容比较重要。Wordpress初始化时就会设定好约70个默认的值,这里暂不介绍。
option_width – 选项的宽(?),作用不详。int(11)值,默认为20。
option_height – 选项的高(?),作用不详。int(11)值,默认为8。
option_description – 针对某个选项的说明,tinytext值。
option_admin_level – 设定某个选项可被操纵的用户等级(详情见我的相关文章),int(11)值,默认为1。
autoload – 选项是否每次都被自动加载,枚举enum(’yes’,’no’)值,默认为yes。

wp_post2cat

用于保存文章(posts)与分类(categories)之间的关系的表,只有三个字段:
rel_id – 关联ID,bigint(20)值,是个有auto_increment属性的自增量。
post_id – 文章的ID,bigint(20)值,相当于wp_posts.ID。
category_id – 分类的ID,也是bigint(20)值,相当于wp_categories.ID。
文章与分类的关系的形成是这样的:rel_id是一个不断增加的自增量,它用于识别每不同的post。post_id可以重复(当它对应多个分类时),因为它可被rel_id识别,所以不会出现混乱。每个post_id可对应多个category_id时,一个rel_id + post_id组合,可以识别某一个分类,因此每个文章的分类可以是不同的。通过这张表,可以非常快速、高效地找出某篇文章(post)对应了哪些分类 (category),反之亦然。

wp_postmeta

用于保存文章的元信息(meta)的表,四个字段:
meta_id – 元信息ID,bigint(20)值,附加属性为auto_increment。
post_id – 文章ID,bigint(20)值,相当于wp_posts.ID。
meta_key – 元信息的关键字,varchar(255)值。
meta_value – 元信息的值,text值。
这些内容主要是在文章及页面编辑页(Write Post, Write Page)的”Add a new custom field to this post(page):”下进行设定的。meta_key就对应名为”key”的下拉列表中的项,而值由用户自己填上(某些时候,wp也会自动加入,如文章中有的音频媒体)。

wp_posts

用于保存你所有的文章(posts)的相关信息的表,非常的重要。一般来讲,它存储的数据是最多的。一共包括了21个字段。
ID – 每篇文章的唯一ID,bigint(20)值,附加属性auto_increment。
post_author – 每篇文章的作者的编号,int(4)值,应该对应的是wp_users.ID。
post_date – 每篇文章发表的时间,datetime值。它是GMT时间加上时区偏移量的结果。
post_date_gmt – 每篇文章发表时的GMT(格林威治)时间,datetime值。
post_content – 每篇文章的具体内容,longtext值。你在后台文章编辑页面中写入的所有内容都放在这里。
post_title – 文章的标题,text值。
post_category – 文章所属分类,int(4)值。
post_excerpt – 文章摘要,text值。
post_status – 文章当前的状态,枚举enum(’publish’,’draft’,’private’,’static’,’object’)值,publish为已发表,draft为草稿,private为私人内容(不会被公开) ,static(不详),object(不详)。默认为publish。
comment_status – 评论设置的状态,也是枚举enum(’open’,’closed’,’registered_only’)值,open为允许评论,closed为不允许评论,registered_only为只有注册用户方可评论。默认为open,即人人都可以评论。
ping_status – ping状态,枚举enum(’open’,’closed’)值,open指打开pingback功能,closed为关闭。默认值是open。
post_password – 文章密码,varchar(20)值。文章编辑才可为文章设定一个密码,凭这个密码才能对文章进行重新强加或修改。
post_name – 文章名,varchar(200)值。这通常是用在生成permalink时,标识某篇文章的一段文本或数字,也即post slug。
to_ping – 强制该文章去ping某个URI。text值。
pinged – 该文章被pingback的历史记录,text值,为一个个的URI。
post_modified – 文章最后修改的时间,datetime值,它是GMT时间加上时区偏移量的结果。
post_modified_gmt – 文章最后修改的GMT时间,datetime值。
post_content_filtered – 不详,text值。post_parent – 文章的上级文章的ID,int(11)值,对应的是wp_posts.ID。默认为0,即没有上级文章。
guid – 这是每篇文章的一个地址,varchar(255)值。默认是这样的形式: http://your.blog.site/?p=1,如果你形成permalink功能,则通常会是: 你的Wordpress站点地址+文章名。
menu_order – 不详,int(11)值,默认为0。
post_type – 文章类型,具体不详,varchar(100)值。默认为0。
post_mime_type – 不详。varchar(100)值。
comment_count – 评论计数,具体用途不详,bigint(20)值。


wp_usermeta

用于保存用户元信息(meta)的表,共4个字段:
umeta_id – 元信息ID,bigint(20)值,附加属性auto_increment。
user_id – 元信息对应的用户ID,bigint(20)值,相当于wp_users.ID。
meta_key – 元信息关键字,varchar(255)值。
meta_value – 元信息的详细值,longtext值。
这是在Wordpress 2.0中才新加入的表。


wp_users

用于保存Wordpress使用者的相关信息的表。WP官方对2.0.2版本中该表的情况的说明有些矛盾(称有22个字段,但详细的列表中只有11个),所以这里只能列出11个字段进行说明:
ID – 用户唯一ID,bigint(20)值,带附加属性auto_increment。
user_login – 用户的注册名称,varchar(60)值。
user_pass – 用户密码,varchar(64)值,这是经过加密的结果。好象用的是不可逆的MD5算法。
user_nicename – 用户昵称,varchar(50)值。
user_email – 用户电邮地址,varchar(100)值。
user_url – 用户网址,varchar(100)值。
user_registered – 用户注册时间,datetime值。
user_level – 用于等级,int(2)值,可以是0-10之间的数字,不同等级有不同的对WP的操作权限。
user_activation_key – 用户激活码,不详。varchar(60)值。
user_status – 用户状态,int(11)值,默认为0。
display_name – 来前台显示出来的用户名字,varchar(250)值。

分类: 网站, 软件设计 标签:

疯狂的sql查询,随时可能引发性能问题

2010年1月26日 admin 没有评论

今天review了群发sms的一段代码,差点没吓死我。 代码如下

for (MtnPlanTosendlist mtnPlanTosendlist:tempList){
           int memberSendTodayCount = getToDayLimit(defaultBo,getMemberSentDal(mtnPlanTosendlist));
           int memberSendMonthCount = getMonthLimit(defaultBo,getMemberSentDal(mtnPlanTosendlist));
           if ((memberSendTodayCount < smsTodayLismitMember) && memberSendMonthCount < smsMonthLimitMember){
              insertList.add(mtnPlanTosendlist);
           }
}

public static int getToDayLimit(BaseBo defaultBo, MtnSmsLog mtnSmsLog) {
       int count = (Integer) defaultBo.get(mtnSmsLog, COUNT_TODAY_EXT)
              .getDefaultModel();
       return count;
}

public static int getMonthLimit(BaseBo defaultBo, MtnSmsLog mtnSmsLog) {
       int count = (Integer) defaultBo.get(mtnSmsLog, COUNT_MONTH_EXT)
              .getDefaultModel();
       return count;
}

这段代码看的出问题吗? For 循环类名套了2个count 查询。如果前面的那个for循环有100个的话, 执行完这个代码就需要200行的sql(远程调用),悲惨呀!

开发人员切莫要把数据库操作当做local方法一样的使用。一则,db操作本质上也是远程访问,远程调用很浪费时间,特别有大量的远程访问的时候;二则,数据库资源有限,大量的数据库访问会导致数据库load的升高,从而影响程序的性能。所以。劲量要减少对数据库的访问,例如通过batch进行操作,或者batch的load到内存中,进行处理。另外,如果count的数量数据行太多的话就需要考虑中间结果,例如做一张中间表来记录当前月、当天的数量,这样也可以有效的减少数据库的访问。

分类: 软件设计 标签:

对产品化和平台化的思考

2010年1月17日 admin 2 条评论

2009年4月加入公司以后做的第一个项目是给公司的事业部A做个类似Google Calendar的时间、任务管理平台,使事业部A能够在这个平台上开展一项具体的业务,并加以管理和统计。所以公共Calendar服务差别最大的一点是我们的Calendar需要实现对事业部A或者未来其他事业部的“应需而变”。当时做第一版设计的时候就提出了“抽象出一个通用的任务制定、整理、展现的平台。这个平台可以方面的加以扩展以满足多种业务对任务管理的需求”这样一个目标,可在经过之后的若干次的业务和功能的扩展以后,我发现这个产品离产品化和平台化越来越远,直到最后成为了事业部A的专属产品。最近的一段时间内对这个产品如果实现产品化和平台化做了进一步的思考,这篇文章就是这些思考的汇总。

2009年4月的第一版设计——基础设计的失败

当时在设计的时候,有两个设计重点。其中一个是“任务”这个概念,当时主要的精力放在了对任务的抽象上,所以针对任务这个对象设计了开始时间、结束时间、重要度、标题,还包括了任务的创建、完成、推迟等行为。当时考虑如果通用的任务概念无法满足某个事业部的需求时就为这个事业部定制“任务”这个概念,这种设计的具体表现体现在当时设计了一张Task表并预留了许多字段,并且自欺欺人地认为这些字段能够支持未来业务平台的扩展。

剩下的另一个设计重点是如何为这个业务部门定制Calendar的展现样式。业务部门的需求是要根据这个事业部的需求定制一个Calendar的每日单元格。当时对Calendar控件提出了“能够支持日历、月历、周历等多种操作模式的变换以取得类似Outlook中日历功能的效果”,“Calendar容器能够支持多种Widget的Plug in。有需要使用Calendar功能的业务可以根据自身业务需求定制Widget,并能方便地跟Calendar容器整合在一起。”。Calendar控件在之后得到了基本的实现,但应需而变的Widget方案并没有能够实现。

当时就是带着这两个巨大的问号,我们完成了这个功能的第一版。

2009年12月的第三版功能升级——平台化的终结

2009年12月还是这个事业部提出了很多“管理”方面的需求,这些需求使得这个事业部成为了这个产品的“惟一”主人。

2010年1月——新的思考

对于一个大型的企业应用需求往往总是来自于某个较小单位,并逐步的扩展到更大的范围,而另一方面软件设计往往力求能最大程度的支持未来业务的扩展,也就是想实现产品化、平台化的目标,当有新的需求需要实现的时候,能够付出最小的成本。这两者之间某种程度上存在着很大的矛盾,要解决这种矛盾,我目前的思考是要依赖以下几点内容

  • 分析需求,识别出需求涉及的领域。如果没有对需求进行领域的划分,或者划分是错误的,结果只有一个——产品化和平台化的终结。
  • 在领域划分的基础上,规划每个领域的基础、高阶服务。每个领域可能会有若干个层次的服务:基础、高阶。以Calendar领域为例,基础服务可能就有任务的管理(创建、删除),以日历、月历、周历等多种操作模式的变换。高阶的服务可能包括时间冲突的提醒,任务的提醒等等。
  • 将具体的需求通过,某个领域的服务,或者多个领域的服务组合来加以实现。组合式的服务基于几个领域的基础服务构成一个新的服务。每个领域的基础服务要力求“最强大化”,否则整个平台的扩展就会受到很大的限制。

image

还是以那个事业部A的需求为例。

  • 我们识别出需求涉及Calendar和A事业部两个领域。
  • Calendar领域提供的基础服务包括任务、时间的管理、Calendar页面的展现,高阶服务包括任务提醒等等。A事业部领域包括了具体任务的制定业务、任务执行的监督、任务执行状态在Calendar页面的展现、任务执行的具体内容和后续跟踪等等。
  • 通过将Calendar领域的任务管理服务和A事业部领域的具体任务制定服务组合在一起实现A事业部任务安排的需求。将Calendar领域的任务管理服务和A事业部领域的结果反馈服务组合在一起完成A事业部的结果反馈需求。
  • 当后续B事业部想要在Calendar平台上开展自己的业务的时候,由于Calendar领域的服务没有夹杂任何其他领域的内容并且实现了功能的最大化能够很有效的被B事业部加以重用。

 

总结

  • 分析需求,识别出需求涉及的领域。对领域间的隔离态度上是“决不妥协的”。
  • 每个领域的服务要追求最大化、最强大化。
  • 具体的需求通过,某个领域的服务,或者多个领域的服务组合来加以实现。
分类: 软件设计 标签:

SOA实践指南读书笔记

2010年1月16日 admin 没有评论

最近读完了《SOA实践指南——分布式系统设计的艺术 》这本书,以下是这次的读书笔记。

面向服务的架构(SOA)。它是一种帮助系统在增长的同时保持可扩展性和灵活性的方法,它也有助于填平“业务/IT”鸿沟。该方法由三个主要元素构成:

  • 服务。服务一方面体现了自足的、能作为一个或多个流程一部分的业务功能;另一方面,服务能由任何技术、在任何平台上实现。
  • 企业服务总线(ESB)。它是个专门的基础设施,它使我们能够用简单和灵活的方式来结合这些服务。
  • 政策和过程。它们处理这样的事实——大型分布式是异质的、处于不断维护中的,其所有者各异。 SOA接受这样的观点:在大型分布式系统中,保持灵活性的唯一办法是支持异质、分散和容错。

     

    大型分布式系统的特征

所有大型系统天生是异质的。这些系统的目的有别、实施时间各异、新旧程度差异悬殊。大型系统呈现出堆积了不同平台、编程语言、编程范式,甚至不同中间件的系统景观。异质性的一个原因是,大型系统和它们的数据有非常长的生命周期。在此生命周期中,通过加入新的系统和流程,不断开发出促进业务的新功能。大型分布式系统还有一个重要的附加特性:所有者各异。大型分布式的另一个关键系统时“不完美性”。尽善尽美的代价太高。

 

SOA的关键技术概念

  • 服务
  • 互操作性
  • 松耦合

ESB

ESB的关键特性是,它使你能在异质系统间进行服务调用。它的职责包括数据转化、(智能)路由、处理安全和可靠性、服务管理、监测以及日志。

服务

服务的定义:一项“服务”是一个自足的、无状态的业务功能,通过定义良好的标准接口,它接受一个或多个请求,返回一个或多个应答。服务也可能执行离散的工作单元,如编辑和处理一个事物。服务应该不依赖于其他功能或过程。用于提供服务的技术,如编程语言,不构成本定义的一部分。

服务应该代表一项自足的功能,对应着一项真实世界的业务活动,换句话说,业务人员应该能理解服务干什么?

有一系列的性能指标关注于度量/性能参数,如可用性(组织中的成员应该何时能执行功能)、持续时间(执行功能需要多少时间)、频率(一段时间内功能被执行得有多频繁)。

松耦合

SOA松耦合可能的形式

  紧耦合 松耦合
物理连接 点对点 通过中介者
通信风格 同步 异步
数据模型 公共复杂类型 只有简单的公共类型
类型系统
交互模式 通过复杂的对象树导航 以数据为中心、自足的消息
业务逻辑控制 集中控制 分布式控制
绑定方式 静态 动态
平台 强平台依赖性 平台无关
事务性 2PC(两阶段提价) 补偿
部署 同时进行 非同时进行
版本划分 显示进行 隐式进行

异步通信:从消费者的角度来看,异步通信可能指的是消费者不用阻塞自己来等待答复,然而,从基础设置(ESB)的角度连看,异步通行可能指的是用来解耦消费者和供应者的消息队列。

异质数据类型:在面向对象成为主流以后,弄出一个公共的“业务对象模型”(BOM)就成了一个普遍的目标。但是,人们后来发现,对大型系统来说,这个方法时自求灾难。迟早有一天,一致话的代价会变得过高。从时间和资金的角度来看,保持所有系统同步的成本太高。即使你努力做到了,下一次公司合并又会引入异质。

通常的办法是,服务定义者定义自己提供的服务中所以用的数据类型。服务消费者必须接受这些类型。注意,服务消费者应避免在自己的源代码中使用供应者的数据类型。相反,消费者应该有一个薄薄的“映射层”,将供应者数据类型映射为自己的数据类型。

不用公用数据模型有优点也有缺点:

  • 优点是,系统可以修改自己的数据类型,不会对其他系统造成直接影响(修改好的服务接口只影响相应的消费者)。
  • 缺点是,你不得不从一个系统想另一个系统映射数据。 补偿:即保持总体异质性,同时耦合程度也更松的方法时补偿。使用这种方式时,你先修改第一个后端系统,再修改第二个后端系统;如果第一个修改是成功的,你就需要针对具体问题,适当的做出“补偿”。例如,你可以回滚成功的修改,,从而恢复修改前的一致状态,或者,向某个错误监控桌面机发出问题报告,这样,某人就可以检查问题的细节,手动处理问题。

松耦合有各种形式,你必须找到适合自己特定上下文和项目的耦合松紧程度。

服务的分类

  • 基本服务。基础服务有两类,基本数据服务和基本逻辑服务。基本服务应该具备所谓的ACID特性。在大系统中,把一致性的责任交给更高的层,要它们确保针对多个后端的多个调用是一致的,这通常也并非易事。为了使事情简单些,应该明确哪个系统为主,哪个系统时派生的,然后应用相应的同步措施。 基本服务永远只应属于一个后端系统,只该对后端内的一致性负责。
  • 组合服务
  • 流程服务 组合服务与流程服务对多个后端的一致性负责。在多个后端间强制一致的通常办法是补偿,而不是事物安全或两阶段提交。

    消息交换模式

      

    模式 说明 优点 缺点
    请求/应答 又分为阻塞和非阻塞的请求/应答模式(有时也称为“同步”和“异步”请求/应答模式)

    1.代码很简单。处理一个服务的调用与处理任何其他函数或过程调用一样。当你需要某些信息,或者需要完成某些事情时,你发出请求等待回到或确认,等问题被解决以后再继续自己的工作。

    2.请求/应答模式的优点是,应答被递交到触发流程最初请求的同一个流程实例。

    在你等待应答时,无法做任何事情。典型情况是,要么你需要一个快速的应答,要么对运行所耗时间要求不高。在实践中,处理这样的请求的供应者应该时常处于可用状态,并且能够在合理的时间内发出应答。
    单程 如果你不需要应答,从消费者的角度来看,要做的事情甚至更简单:发送消息,然后就完事。这种单程模式通常被称为“发射后不管”。单程消息的序列带来了(业务)流程链的概念 消费者通常给供应者发送一个返地址”,供应者将其和应答消息一起送回。处理收到的应答消息的消费者进程/线程可以用这个地址信息,在内部将应答路由到正确的流程实例(即最初发送请求的那个实例)  
    请求/回调 这个模式通常被称为“观察者”或“发布/订阅”模式,它是基础的设计模式之一。一般来说,这个模式,这个模式允许多个“观察者”向一个系统”订阅”,而该系统会在发生特定情况时通知观察者。    

      出资模型

      有一个集成的,可重用的服务并不会在供应者和消费者发生第一次通行时就产生回报,但是当许多供应者和消费者时就会产生回报。同样,松耦合对一个解决方案可能没有回报,但是,有许多解决方案要运行和扩大规模是,松耦合就产生回报。

    多层验证

    • 在后端验证。所有的验证只在后端进行。此时,前端不做任何预验证。使用这种方法时,用户填入大量数据,而后端因为最开始几个输入项有错拒绝接受数据,最终用户可能会感到非常受挫。及早发现问题会节省大量的时间和工作。
    • 在前端验证。所有的验证只在前端进行。此时,后端假设服务的消费者不会发送任何错误的或者不一致的数据。当然,这种方法也有风险,因为前端可能犯错误或者漏过一些错误,就会导致后端的未定义行为,造成后端不一致和/或崩溃。
    • 冗余。前后端都独立进行验证。前端会试图在前面找到可能的错误,后端将保证它接受的输入是有效的。然而,独立的验证可能导致不一致。
      实践中的验证通常混合使用所有这些方法,后端永远都应该检查所有输入的有效性。 总体来说,使前端。使用前端预验证服务输入是个好主意。然而,预验证无法做到完美,否则前对就可以实现整个业务逻辑了(包括处理可能的竞争条件)。一般来说,你必须提供合适的流程来处理事情会出错这个事实。预验证的目的应该是保证服务流程“通常”会成功。小心点,不要再预验证上花不恰当的力气。

    什么是SOA  

    在计算科学中,术语“面向服务的架构”(SOA)表达了一种软件架构的概念,它定义为使用服务来满足满足软件用户的需求。在SOA环境中,网络上的节点以独立服务的形式将自己的资源开发给网络上其他参与者,其他参与者按一种标准的方式使用资源。大多数对于SOA的定义都提到了实现SOA时使用Web Services(即使用SOAP或REST)。然而,可以使用任何基于服务的技术实现SOA。

    ……

    与传统点对点架构不同,各种SOA都由松耦合、高度可以互操作的应用服务构成。这些服务基于某种格式定义进行互操作,该定义独立于底层平台和编程语言(例如,WSDL)。接口定义封装(隐藏)了供应商和语言相关的实现。SOA独立于开发技术(比如JAVA和.NET)。由此,软件组件将具备非常高的重用性,因为接口按与标准兼容方式定义。

    基于SOA的逻辑架构模型

    image

    处理状态的方法对比

    1. 在服务中保持状态
    2. 在后端中保持状态
    3. 在前端/消费者中保持状态
    4. 在所有这3种选择有不同的优点和缺点:

      将状态放入前端/消费者意味着,服务可以是无状态的,并且可以不牵涉后端。然而,当会话在运行时,你无法改变前端(如果要求支持多前端,或者如果你希望能够将会话转移到另一个桌面,这就是个缺点)。
      将状态放入有状态服务需要在服务层上使用更多的资源,并且,要有办法将请求路由到同一个服务实例。然后,你可以改变前端,后端也不被牵扯进来。
      将状态放入后端使你能使用无状态服务,前端也不必提供存储。然而,每一个会话都牵涉后端。
        状态在前端 状态在服务 状态在后端
      在前端 所有状态 ID ID
      服务 无状态 有状态无状态 无状态
      在后端存储 / / 所有状态
      支持多通道

       

      无状态服务为什么更好

          首先,对于无状态服务来说,在服务层做到负载均衡和失效备援相当简单。ESB仅需实现能使用下一个最容易获得的服务实例即可。它甚至可以是其他消费进程以前使用过的线程。这意味着如果一个系统失败了,其他系统可以轻易地接手,继续工作。如果服务层的通量不够高,你可以将服务实例的数量翻倍,从而是通量翻倍。这意味着解决方案能线性伸缩,并且,如果一个服务实例意外死亡,其他服务实例能投入进来。

      而有状态服务的缺点是,它们绑定在会话上。这样一来,知道消费者会话结束(或者连接失效)之前,为它们分配的资源都绑定在一个特定的消费会话上。除此以外,你必须能让ESB把连续的服务调用路由到同样的服务实例上。

      然而,要注意的是,有状态服务仍然可以线性伸缩。使用一种“粘性”路由的政策,根据可用的资源,你在第一个服务调用被执行时找到你的服务实例。这一来,对服务实例数量的翻倍就又能将可能的会话数量翻倍了。然而,此时不具备服务资源可以在不同消费者之间共享的优点。并且,如果一个服务实例意外死亡,会话状态就丢失了。

      同时也要注意,有状态服务可以有失效备援机制(并且仍然可以线性伸缩)。此时,每个服务器不但返回它自己的会话ID,还返回失效备援会话的ID。只要这个失效备援会话不在一个中央服务器上,而是在邻近的服务器上(并且,每个服务器的邻近服务器各不相同),这样就不会造成瓶颈。

      事件驱动架构

      • 如果一个事件仅仅通知消费者客户数据有变化,需要处理,那就只需要发送少量数据。随事件发送的数据会帮助消费者判断更新是否与自己有关——例如,可能包含着新的常用合同信息或类似信息——但是,本质上,这些通知知识简单表明有更新发送,需要被处理。
      • 如果一个事件包含了要处理的数据,该事件会更粗粒度一些。此时,事件的消费者会收到处理该事件所需的所有信息。

       

      幂等性

      等幂性是服务的一种可能的属性。在服务的上下文环境中,它意味着对同一服务调用的多次递交/处理不会引发问题。假设你有一个向银行中增加钱的服务。如果一个消费者调用这个服务(例如,为了处理银行转账)并且没有得到应答,消费者不会知道服务的请求还是服务的应答失败了。例如下图中,服务可能在请求处理之前或之后丢失。

      image

      在后一种情况下,从供应者的角度来看,服务成功了,指定数量的钱被增加到了银行账户上。在前一种情况下,服务没有影响(即没有增加钱)。

      当服务的消费者没有得到应答时,它不知道服务是否成功了。为求确保,它可能重试服务调用,再次发出同样的请求。结果是,达到供应者的服务请求可能有一次或两次。如果服务是幂等的,那么,服务请求到达多少次都没有关系:同一服务调用的多次请求只产生一次的效果。

      刚刚描述的银行服务不是幂等的,因为,它依赖于第一次服务调用失败发生在何处,客户的余额可能最终被增加了两次,而不是消费者想要的一次。为了避免这样的问题,通常说来,只要有可能就是服务幂等是个好主意。

      所有的读取服务都是幂等的,因为一个服务供应者无论多频繁地执行返回数据的请求都没有关系。然而,要注意,一旦做了类似每个请求都写一个备忘录项这种事,读取服务就变成了写入服务。如果这个备忘录和业务有某种关联,这就可能成为问题(例如,对同一样的事项,最终可能有多个备忘录项)。

      写入服务可能是幂等的,也可能不是。当一个服务调用的影响依赖于后端现有的状态时,写入服务不是幂等的。举个例子,在前面的例子中,银行账户的最终余额依赖与最初的余额。如果开始的余额是500美元,服务调用增加100美元,结果将是600美元。如果因为最初的应答丢失了,另一个请求到达并且得到处理,最后的余额将是700美元。

      一个幂等的写入服务的例子是,一个通过发送所有地址来设定客户地址的服务。供应者处理多少次请求都没有关系,结果将是一样的。

      注意:你总是可以避免让服务不幂等。例如,我们可以修改银行服务语义,发送新值而不是应该增加的值,从而使服务幂等。这就是说,不是调用:

      addToBalance(100);

      我们可以调用:

      setBalance(600);

      这个调用可以反复地发送和处理,余额的结果都会是600。当然,其他服务也能在相同的账户中增加或减少资金,这就带来了问题。这就带来了问题。你如何才能知道最终的余额应该是多少呢?正如你看到的,从业务的角度来看,让服务幂等可能变得复杂。

      除此以外,从业务角度来看,有的服务内生就是不幂等的。所有创建某些东西的服务都没有办法和一个状态去比较(除非你知道创建新资源的工厂的状态)。例如,如果你有一个创建银行账户的服务,你就是在创建账户,仅此而已。

      如果从业务的角度来看,引入幂等性是个难题的话,那么,你需要一些对幂等性的技术支持。实现幂等性的普通方法相当简单,消费者发送数据的方式必须能让供应者看的出两个技术性的请求其实是同一个。为此,消费者可以随着每次新请求发送一个唯一的ID。如果消费者没有得到应答,它的每次重试都是同样的ID。下图是对此的图解。

      image

      在处理每个进入的请求前,供应者应将请求及相关联的唯一ID存储到一个中央数据库中。如果供应者收到一个ID已经在存储库中的请求,它就知道该请求已经被处理过。于是,供应者就不会再处理它,而是向消费者重新发送最初的应答(应答在第一次被送出去之前,也保存下来)。如果第二次请求到达是,服务实现正处于运行中,则供应者可以回复一个应答,说服务调用在运行中,或者供应者可以等到第二次请求处理完毕以后,再次把应答发送出去。

      作为一项优化,消费者能和请求一起发送一个标志,这样供应者就知道此请求是否是一个重试。这样能做到改善性能,因为如果调用没有被标记为重试的华话,供应者就不必验证请求是否已经在存储库中了。

      分类: 软件设计, 阅读 标签:

      Code Review的一点感悟

      2010年1月10日 admin 1 条评论

      这两天review同学的代码,除了代码本身的质量(注释、命名、函数过长等等)以外,还能感觉到代码中那种heavy(沉重)的感觉,于是用借着这篇博文把这些感觉整理出来。

      要索取资源而不是自己去寻找资源(也就是依赖注入笛米特法)。

      例如我们的代码有一个文件的下载器要实现,文件下载器除了要处理要知道一个下载文件的源路径以外,要需要两个参数,其中一个是下载完成以后将文件保存到的目录,还有一个是下载失败的重试次数。在同学的实现中是由下载器这个类实现这两个参数的获取逻辑。代码看到这里就就让我对文件下载器的实现有一种沉重的感觉,这种感觉就是这两个参数的获取逻辑对整个文件下载器可重用性的破坏。这种实现就会导致整个文件文件下载器只能将文件输出到某个特定的目录,导致这个文件下载器对这个特定配置(不管配置数据是保存数据库中还是配置文件)的依赖,也就是说下次要重用这个文件下载器(例如文件下载器的使用场景发生变化,或是有新的场景要对文件进行下载)还必需要提供给这配置项保存的资源,如果是配置项是保存到数据库中的,就必须要移植相应的表结构和DAO访问层。这种实现会让当前文件下载器被重用的几率只有“零”。所以在文件下载器的设计上要贯彻“索取资源而不是自己去寻找资源(也就是依赖注入笛米特法)的原则”,也就是让文件下载器的消费者在构造文件下载器的时候提供文件下载器所需依赖的参数,这样让文件下载器具备100%的可重用性。至于说文件下载器的调用方本身就是负责某个具体业务的实现,本身的重用需求就比较小,依赖特定的上下文也是合理的。

      分类: 软件设计 标签:

      转载:笛米特法则详解(the Law of Demeter or Principle of Least Knowledge)

      2010年1月10日 admin 没有评论

      原文地址:笛米特法则详解(the Law of Demeter or Principle of Least Knowledge)

      The Law of Demeter和 Principle of Least Knowledge讲的都是一回事,是说一个软件实体要尽可能的只与和它最近的实体进行通讯。通常被表述为:talk only to your immediate friends ( 只和离你最近的朋友进行交互)。  “talk”其实就是对象间方法的调用。这条规则表明了对象间方法调用的原则:

      (1) 调用对象本身的方法;

      (2) 调用通过参数传入的对象的方法;

      (3) 在方法中创建的对象的方法;

      (4) 所包含对象的方法。

      上面的4点看起来有点别扭,下面通过一个具体的例子,就可以对上述4条guideline有进一步感性的认识:

       demeter_code

      下面对start()方法中的语句进行分析:

      第10行-key.turns():符合上述的第(2)条,key对象是通过参数传入start()方法的。

      第13行-engine.start():符合上述的第(4)条,engine对象是包含在Car的对象之中的。

      第14行-UpdateDashboardDisplay():符合上述的第(1)条,UpdateDashboardDisplay()方法是Car对像自身的方法。

      第15行-doors.lock():符合上述的第(3)条,doors对象是在start()方法中创建的对象。

      接下来看一个违反Principle of Least Knowledge的例子:

      1 public float getTemp() {
      2   Thermometer thermometer = station.getThermometer();
      3   return thermometer.getTemperature();
      4 }

      上面的方法中station对象是immediate friends。但是上面的代码却从station对象中返回了一个Thermometer对象,然后调用了thermometer对象的getTemperature()方法,违反了Principle of Least Knowledge

      下面对上面的方法作出符合Principle of Least Knowledge的改进:

      1 public float getTemp() {
      2  return station. getTemperature();
      3 }

      我们在Station类中添加一个方法getTemperature()。这个方法将调用Station类中含有的Thermometer对象的getTemperature()。这样getTemp()方法就只知道Station对象而不知道Thermometer对象。

      总结:笛米特法则告诉我们要尽量只和离自己最近的对象进行交互。离自己最近的对象包括:自身包含的对象,方法中创建的对象,通过参数传进的对象,还有自己本身。

      参考资料

      Breaking the Law of Demeter is Like Looking for a Needle in the Haystack

      分类: 设计模式, 软件设计 标签:

      谈设计的学习和特定技术学习的平衡和取舍

      2010年1月3日 admin 3 条评论

      有位大三还在公司实习的同学问了我一个问题,说他在加入公司之前通过网上下载一些Hibernate、Spring之类的视频来学些一些主流的Java技术和框架,通过这种方式和内容的学习,他的作品得到了老师的认可,并成为了公司的实习生。可在到了部门以后,发现我特别强调面向对象设计、设计模式的运用、UML以及文档。可就他自己的感觉而言,他对这些内容的学习不怎么感冒,我当时没有直接回答这个问题,今天整理了一下我的思路,说不上是一个回答,只是谈一下我的感觉吧。

      故事的起点其实并不久远,就在我加盟现在的公司,并在9月份开始接手了一个产品线的开发工作。至此以后,我面对了很多的难题:

      • 产品的规划、维护做的很差,看不出模块化的设计,很多模块混成一团,理不清减还乱。这种“人造”的复杂,再加上缺乏文档,开发人员的陆续离开,系统的理解、维护出现很大的真空。
      • 产品的升级、扩展、改造难度大。
      • 产品设计、编码质量很差。这种情况存在在部门负责的多个系统中,不只是我们的产品线有这样的问题。这一点给我提了一个大大的问号?甚至是让我产生了离开公司的想法,其中的纠结是很难向外人说明白的。
      • 团队绩效低下,新的团队成员很难摸清楚现有的业务逻辑、长期依赖原有的开发人员开展工作,要很长的时间才能成为某个模块的owner,至于说整个产品线的,更加困难。
      • 产品质量差,新增加一个功能往往会影响几个历史功能,每次有新的大小版本要发布,都提心吊胆。这一点不仅影响到了开发团队,连测试部门很增加了很多的压力。
      • 工作压力大,对工作的满意度差。由于上面的几点严重影响了工作的效率,导致我和我的团队加班越来越频繁,满意度、幸福感随之下降。

      与此相反的是我和我的团队所负责的产品线,需要支持一个年收入30亿的公司的使用,有10几20个业务部门在该产品线上开展核心的非核心的业务,每年有上百个的功能改进需求,有4-5次的产品版本升级,以上种种任务都需要一个7人左右的开发团队来支撑。如此突出的矛盾,我如何解决呢?我把强化设计视为解决我和我团队诸多问题的方案之一。坦白的说,我之前的工作经历中,我和我之前的公司、同事都没有对相关的内容有特别的关注,正是客观的环境教育了我,使得我把近期把技术重点放到了相关的内容上来。

      在这段时间,我特别要求我和我的开发团队做好产品线的设计工作,强烈地要求我的团队重视设计工作、强化设计能力、完善文档、做好产品模块化的设计。也直接地在具体的工作中对团队成员的面向对象设计、设计模式、UML等方面的知识有提出了一定的要求,这些内容或许给他们有了不少的压力,他们多数是刚刚走出校园的学生或从未对相关的内容进行系统的学习。

      最后回到那位同学问我的问题,具体的技术和设计如何取舍。首先,我不认为两者存在什么冲突和矛盾,如果说要有什么冲突和矛盾,也是由于一定时间内人的学习精力和时间有限,所以很难在两者直接加以平衡。我对技术的整理可以以下面这张图来理解。黄色图框的面向对象、设计模式之类的技术都是那些超越了具体语言、框架、平台的内容,而蓝色图框中的都是针对的某些语言或某种平台。但大家不要错误地认为两者之间是“井水不犯河水”的,其实两者之间存在着紧密的联系,以Java语言为例,Web框架总是离不开MVC模式的运用,Spring最核心的功能——DI(依赖注入)也可以在很多讲述软件架构的书中找到说明和分析。以我个人的学习经验而言,很多蓝色图框中的技术、框架的学习,最后都在要通过对黄色图框中相应内容的学习,找到彻底的答案。例如,WebWork中的Interceptor是整个Webwork框架的核心概念和开发重点。如果只是去学习Webwork,甚至是看Webwork的源代码都很难理解在Webwork中为什么要有Interceptor这种概念,能给整个Web框架带来什么样的好处,这些问题的都可以在Interceptor的相关设计模式中找到答案。

      beyong_language

      所以请我们的同学根据自己的情况、从事的工作内容在黄色图框和蓝色图框间做好平衡。其实我们每天的工作就像镜子一样可以反映出我们的缺陷,引导我们加以改进,如果我们能更主动地来加以调整,我们每个人的学习成长经历会更有效率和充满乐趣。最后,附上一个“木桶原理”的示意图,供大家玩味。

      分类: 学习, 软件设计 标签: