软件架构设计系列总结

架构引用维基百科:软件体系结构是构建计算机软件实践的基础。与建筑师设定建筑项目的设计原则和目标,做为绘图员画图的基础同样,一个软件架构师或者系统架构师陈述软件构架以做为知足不一样客户需求的实际系统设计方案的基础。从和目的、主题、材料和结构的联系上来讲,软件架构能够和建筑物的架构相比拟。一个软件架构师须要有普遍的软件理论知识和相应的经验来实施和管理软件产品的高级设计。软件架构师定义和设计软件的模块化,模块之间的交互,用户界面风格,对外接口方法,创新的设计特性,以及高层事物的对象操做、逻辑和流程。
软件架构师与客户商谈概念上的事情,与经理商谈普遍的设计问题,与软件工程师商谈创新的结构特性,与程序员商谈实现技巧,外观和风格。

软件架构是一个系统的草图。软件架构描述的对象是直接构成系统的抽象组件。各个组件之间的链接则明确和相对细致地描述组件之间的通信。在实现阶段,这些抽象组件被细化为实际的组件,好比具体某个类或者对象。在面向对象领域中,组件之间的链接一般用接口来实现。

架构来源于建筑工程学,描述对软件密集型系统设计蓝图。在不一样软件领域,有其不一样特征,但有一部分共同基础设计原则和共性。css

目录:html

1—面向对象设计原则理解java

2—一些软件设计的原则程序员

3—逻辑层 vs 物理层web

4—服务层的简单理解面试

5—SOA面向服务架构简述算法

6—业务逻辑层简述sql

7—设计箴言理解数据库

8—数据访问层简述编程

9—存储过程传言

10—表现层模式-MVC

1—面向对象设计原则理解

面向对象设计(OOD)核心原则让个人程序模块达到“高内聚低耦合”,这是来自于30年前兴起的结构化设计(structured Design),可是一样适用于咱们的OOD。

1.高内聚:

高内聚是指某个特定模块(程序,类型)都应完成一系列相关功能,描述了不一样程序,类型中方法,方法中不一样操做描述的逻辑之间的距离相近。高内聚意味可维护性,可从新性,由于模块对外部的依赖少(功能的完备性)。若是两个模块之间的修改,互不影响这说明模块之间是高内聚的。模块的内聚和其担当的职责成反比,即,模块的职责越多,模块的内聚性越低,这也是模块的单一原则(SRP),SRP提倡每一个类型都最好只承担单一的职责,只有单一的改变因素。

2.低耦合:

耦合是描述模块之间的依赖程度,若是一个模块的修改,都有影响另外一个模块则,两模块之间是相互依赖耦合的。(依赖具备传递性,耦合的两个模块可能间接依赖),低耦合是咱们的设计目的,但不是不存在耦合不存依赖,依赖是必须的,由于模块之间必须通讯交互,不过个人设计依赖应该依赖于不变或者不易变的接口,无需了解模块的具实现(OO封装性)。

在面向对象:咱们能够简述为功能完备(高内聚)的对象之间的交互是依赖于不变或不易变的接口契约(低耦合)。

实现高内聚低耦合:行之有效的方式是分了关注点(SOC),将系统拆分红功能不一样没有重叠功能集。每一个功能只关注一个方面(Aspect)保证模块之间功能没有或者尽可能少的重复。模块化内部实现细节隐藏,只暴露必须的接口,使得模块之间依赖于抽象,达到稳定。分离关注点的思想存在于咱们软件设计的各个领域。如在.net的世界里SOA(面向服务架构)服务就是关注点,只暴露出必要的契约。分层架构从逻辑上利用接口抽象信息隐藏,减小依赖。MVC,MVP也是遵循分了关注点原则,达到表现层和逻辑的分离。

面向对象设计原则:

1.下降耦合度:对象直接须要交互,这就存在依赖,为了实现低耦合就必须减小依赖,依赖于稳定或不易变抽象。考虑以下订单日志记录场景:咱们须要在订单每部操做记录更改日志。

public class OrderManager
{
   public void Create(Order order)
  {
      //订单处理.
     Logger log = new Logger();
     String history=GetHistory();
     log.log(history);
 }
}

在这里咱们的OrderManager和Logger存在高耦合,Logger类的修改可能致使OrderManager的修改,并且不能随意切换咱们的日志记录方式,好比文件,控制台,数据库等日志方式。

面向抽象编程提出抽象(接口,abstract类)是不易变的稳定抽象;对于OrderManager来讲我不须要了解日志记录组件内部,只须要明白提供那些接口可用,怎么用。

public interface ILogger
{
  void Log(History history);
}
public class Logger
{
  public void Log(History history)
{
//内部实现
};
}

那么咱们能够从设计模式工厂模式(工厂模式是负责一些列类似对象的建立)Create 日志组件ILogger。

咱们的OrderManager 就能够实现为:

ILogger log =LoggerFactory.Create();
log.Log(history);

这样咱们的OrderManager就依赖于ILogger,而隔离Logger具体实现,将依赖于抽象,把变化缩小到Factory内部(一样也能够用抽象工厂),若是日志实现变化咱们能够从新实现ILogger ,修改Factory逻辑,若是内部利用配置个人需求变动转移到配置。这就是面向对象第一原则,依赖于抽象而隐藏实现。(利用IOC是一种更好的方式)

2.代码的重用性:尽可能保证相同功能代码只出现一次(Code once run anywhere)。代码的重用在面对对象设计中有继承和组合两种方式,通常推荐组合优先。组合依赖于接口,组合更安全,易于维护,测试。继承存在父类访问权限,父类的修改致使子类的变化,太多的继承也有致使派生类的膨胀,维护管理也是件头痛的事。

3.开闭原则(OCP):表述拥抱需求变化,尽可能作到对模块的扩展开发,修改关闭。对于新增需求咱们完美的作法是新增类型而不是修改逻辑,这就意味着咱们必须使用组合或者是继承体系(为了不上一条重用性,个人继承应该是干净的继承体系,派生类应该只是新增功能而不是修改来自父类上下文),

4.里氏替换(LSP):表述派生类应该能够在任何地方替代父类使用。并非全部的子类均可以彻底替换子类,好比设计父类私有上下文信息的访问,致使子类没法访问。

5.依赖倒置(DIP):描述组件之间高层组件不该该依赖于底层组件。依赖倒置是指实现和接口倒置,采用自顶向下的方式关注所需的底层组件接口,而不是其实现。DI框架实现IOC(控制反转)就是DIP很好的插入底层组件构造框架(分构造注入,函数注入,属性注入)。微软Unity,Castle windsor,Ninject等框架支持。

最后分离关注点,衍生出声明式编程,面向方面编程(AOP)实现纵切关注点,把具体业务逻辑和日志安全等框架集公用逻辑分离。

2—一些软件设计的原则

之前本站向你们介绍过一些软件开发的原则,好比优质代码的十诫和Unix传奇(下篇)中因此说的UNIX的设计原则。相信你们从中可以从中学了解到一些设计原理方面的知识,正如我在《再谈“我是怎么招聘程序”》中所说的,一个好的程序员一般由其操做技能、知识水平,经验层力和能力四个方面组成。在这里想和你们说说设计中的一些原则,我认为这些东西属于长期经验总结出来的知识。这些原则,每个程序员都应该了解。可是请不要教条主义,在使用的时候仍是要多多考虑实际状况。其实,下面这些原则,不仅仅只是软件开发,能够推广到其它生产活动中,甚至咱们的生活中。

Don’t Repeat Yourself (DRY)

DRY 是一个最简单的法则,也是最容易被理解的。但它也多是最难被应用的(由于要作到这样,咱们须要在泛型设计上作至关的努力,这并非一件容易的事)。它意味着,当咱们在两个或多个地方的时候发现一些类似的代码的时候,咱们须要把他们的共性抽象出来形一个惟一的新方法,而且改变现有的地方的代码让他们以一些合适的参数调用这个新的方法。

参考:http://en.wikipedia.org/wiki/Don%27t_repeat_yourself

Keep It Simple, Stupid (KISS)

KISS原则在设计上可能最被推崇的,在家装设计,界面设计 ,操做设计上,复杂的东西愈来愈被众人所BS了,而简单的东西愈来愈被人所承认,好比这些UI的设计和咱们中国网页(尤为是新浪的网页)者是负面的例子。“宜家”(IKEA)简约、效率的家居设计、生产思路;“微软”(Microsoft)“所见即所得”的理念;“谷歌”(Google)简约、直接的商业风格,无一例外的遵循了“kiss”原则,也正是“kiss”原则,成就了这些看似神奇的商业经典。而苹果公司的iPhone/iPad将这个原则实践到了极至。

把一个事情搞复杂是一件简单的事,但要把一个复杂的事变简单,这是一件复杂的事。

参考:http://en.wikipedia.org/wiki/KISS_principle

Program to an interface, not an implementation

这是设计模式中最根本的哲学,注重接口,而不是实现,依赖接口,而不是实现。接口是抽象是稳定的,实现则是多种多样的。之后面咱们会面向对象的SOLID原则中会提到咱们的依赖倒置原则,就是这个原则的的另外一种样子。还有一条原则叫 Composition over inheritance(喜欢组合而不是继承),这两条是那23个经典设计模式中的设计原则。

Command-Query Separation (CQS) – 命令-查询分离原则

  • 查询:当一个方法返回一个值来回应一个问题的时候,它就具备查询的性质;
  • 命令:当一个方法要改变对象的状态的时候,它就具备命令的性质;

一般,一个方法多是纯的Command模式或者是纯的Query模式,或者是二者的混合体。在设计接口时,若是可能,应该尽可能使接口单一化,保证方法的行为严格的是命令或者是查询,这样查询方法不会改变对象的状态,没有反作用,而会改变对象的状态的方法不可能有返回值。也就是说:若是咱们要问一个问题,那么就不该该影响到它的答案。实际应用,要视具体状况而定,语义的清晰性和使用的简单性之间须要权衡。将Command和Query功能合并入一个方法,方便了客户的使用,可是,下降了清晰性,并且,可能不便于基于断言的程序设计而且须要一个变量来保存查询结果。

在系统设计中,不少系统也是以这样原则设计的,查询的功能和命令功能的系统分离,这样有则于系统性能,也有利于系统的安全性。

参考:http://en.wikipedia.org/wiki/Command-query_separation

You Ain’t Gonna Need It (YAGNI)

这个原则简而言之为——只考虑和设计必须的功能,避免过分设计。只实现目前须要的功能,在之后您须要更多功能时,能够再进行添加。

  • 如无必要,勿增复杂性。
  • 软件开发先是一场沟通博弈。

之前本站有一篇关于过分重构的文章,这个示例就是这个原则的反例。而,WebSphere的设计者就表示过他过分设计了这个产品。咱们的程序员或是架构师在设计系统的时候,会考虑不少扩展性的东西,致使在架构与设计方面使用了大量折衷,最后致使项目失败。这是个使人感到讽刺的教训,由于原本但愿尽量延长项目的生命周期,结果反而缩短了生命周期。

参考:http://en.wikipedia.org/wiki/You_Ain%27t_Gonna_Need_It

Law of Demeter – 迪米特法则

迪米特法则(Law of Demeter),又称“最少知识原则”(Principle of Least Knowledge),其来源于1987年荷兰大学的一个叫作Demeter的项目。Craig Larman把Law of Demeter又称做“不要和陌生人说话”。在《程序员修炼之道》中讲LoD的那一章叫做“解耦合与迪米特法则”。关于迪米特法则有一些很形象的比喻:

  • 若是你想让你的狗跑的话,你会对狗狗说仍是对四条狗腿说?
  • 若是你去店里买东西,你会把钱交给店员,仍是会把钱包交给店员让他本身拿?

和狗的四肢说话?让店员本身从钱包里拿钱?这听起来有点荒唐,不过在咱们的代码里这几乎是见怪不怪的事情了。

对于LoD,正式的表述以下:

对于对象 ‘O’ 中一个方法’M',M应该只可以访问如下对象中的方法:

  1. 对象O;
  2. 与O直接相关的Component Object;
  3. 由方法M建立或者实例化的对象;
  4. 做为方法M的参数的对象。

在《Clean Code》一书中,有一段Apache framework中的一段违反了LoD的代码:

final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();

这么长的一串对其它对象的细节,以及细节的细节,细节的细节的细节……的调用,增长了耦合,使得代码结构复杂、僵化,难以扩展和维护。

在《重构》一书中的代码的环味道中有一种叫作“Feature Envy”(依恋情结),形象的描述了一种违反了LoC的状况。Feature Envy就是说一个对象对其它对象的内容更有兴趣,也就是说总是羡慕别的对象的成员、结构或者功能,大老远的调用人家的东西。这样的结构显然是不合理的。咱们的程序应该写得比较“害羞”。不能像前面例子中的那个不把本身当外人的店员同样,拿过客人的钱包本身把钱拿出来。“害羞”的程序只和本身最近的朋友交谈。这种状况下应该调整程序的结构,让那个对象本身拥有它羡慕的feature,或者使用合理的设计模式(例如Facade和Mediator)。

参考:http://en.wikipedia.org/wiki/Principle_of_Least_Knowledge

面向对象的S.O.L.I.D 原则

通常来讲这是面向对象的五大设计原则,可是,我以为这些原则可适用于全部的软件开发。

Single Responsibility Principle (SRP) – 职责单一原则

关于单一职责原则,其核心的思想是:一个类,只作一件事,并把这件事作好,其只有一个引发它变化的缘由。单一职责原则能够看做是低耦合、高内聚在面向对象原则上的引伸,将职责定义为引发变化的缘由,以提升内聚性来减小引发变化的缘由。职责过多,可能引发它变化的缘由就越多,这将致使职责依赖,相互之间就产生影响,从而极大的损伤其内聚性和耦合度。单一职责,一般意味着单一的功能,所以不要为一个模块实现过多的功能点,以保证明体只有一个引发它变化的缘由。

  • Unix/Linux是这一原则的完美体现者。各个程序都独立负责一个单一的事。
  • Windows是这一原则的反面示例。几乎全部的程序都交织耦合在一块儿。

Open/Closed Principle (OCP) – 开闭原则

关于开发封闭原则,其核心的思想是:模块是可扩展的,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。

  • 对扩展开放,意味着有新的需求或变化时,能够对现有代码进行扩展,以适应新的状况。
  • 对修改封闭,意味着类一旦设计完成,就能够独立完成其工做,而不要对类进行任何修改。

对于面向对象来讲,须要你依赖抽象,而不是实现,23个经典设计模式中的“策略模式”就是这个实现。对于非面向对象编程,一些API须要你传入一个你能够扩展的函数,好比咱们的C 语言的qsort()容许你提供一个“比较器”,STL中的容器类的内存分配,ACE中的多线程的各类锁。对于软件方面,浏览器的各类插件属于这个原则的实践。

Liskov substitution principle (LSP) – 里氏代换原则

软件工程大师Robert C. Martin把里氏代换原则最终简化为一句话:“Subtypes must be substitutable for their base types”。也就是,子类必须可以替换成它们的基类。即:子类应该能够替换任何基类可以出现的地方,而且通过替换之后,代码还能正常工做。另外,不该该在代码中出现if/else之类对子类类型进行判断的条件。里氏替换原则LSP是使代码符合开闭原则的一个重要保证。正是因为子类型的可替换性才使得父类型的模块在无需修改的状况下就能够扩展。

这么说来,彷佛有点教条化,我很是建议你们看看这个原则个两个最经典的案例——“正方形不是长方形”和“鸵鸟不是鸟”。经过这两个案例,你会明白《墨子 小取》中说的 ——“娣,美人也,爱娣,非爱漂亮人也….盗,人也;恶盗,非恶人也。”——妹妹虽然是美人,但喜欢妹妹并不表明喜欢美人。盗贼是人,但讨厌盗贼也并不表明就讨厌人类。这个原则让你考虑的不是语义上对象的间的关系,而是实际需求的环境。

在不少状况下,在设计初期咱们类之间的关系不是很明确,LSP则给了咱们一个判断和设计类之间关系的基准:需不须要继承,以及怎样设计继承关系。

Interface Segregation Principle (ISP) – 接口隔离原则

接口隔离原则意思是把功能实如今接口中,而不是类中,使用多个专门的接口比使用单一的总接口要好。

举个例子,咱们对电脑有不一样的使用方式,好比:写做,通信,看电影,打游戏,上网,编程,计算,数据等,若是咱们把这些功能都声明在电脑的抽类里面,那么,咱们的上网本,PC机,服务器,笔记本的实现类都要实现全部的这些接口,这就显得太复杂了。因此,咱们能够把其这些功能接口隔离开来,好比:工做学习接口,编程开发接口,上网娱乐接口,计算和数据服务接口,这样,咱们的不一样功能的电脑就能够有所选择地继承这些接口。

这个原则能够提高咱们“搭积木式”的软件开发。对于设计来讲,Java中的各类Event Listener和Adapter,对于软件开发来讲,不一样的用户权限有不一样的功能,不一样的版本有不一样的功能,都是这个原则的应用。

Dependency Inversion Principle (DIP) – 依赖倒置原则

高层模块不该该依赖于低层模块的实现,而是依赖于高层抽象。

举个例子,墙面的开关不该该依赖于电灯的开关实现,而是应该依赖于一个抽象的开关的标准接口,这样,当咱们扩展程序的时候,咱们的开关一样能够控制其它不一样的灯,甚至不一样的电器。也就是说,电灯和其它电器继承并实现咱们的标准开关接口,而咱们的开关产商就可不须要关于其要控制什么样的设备,只须要关心那个标准的开关标准。这就是依赖倒置原则。

这就好像浏览器并不依赖于后面的web服务器,其只依赖于HTTP协议。这个原则实在是过重要了,社会的分工化,标准化都是这个设计原则的体现。

参考:http://en.wikipedia.org/wiki/Solid_(object-oriented_design)

Common Closure Principle(CCP)– 共同封闭原则

一个包中全部的类应该对同一种类型的变化关闭。一个变化影响一个包,便影响了包中全部的类。一个更简短的说法是:一块儿修改的类,应该组合在一块儿(同一个包里)。若是必须修改应用程序里的代码,咱们但愿全部的修改都发生在一个包里(修改关闭),而不是遍及在不少包里。CCP原则就是把由于某个一样的缘由而须要修改的全部类组合进一个包里。若是2个类从物理上或者从概念上联系得很是紧密,它们一般一块儿发生改变,那么它们应该属于同一个包。

CCP延伸了开闭原则(OCP)的“关闭”概念,当由于某个缘由须要修改时,把须要修改的范围限制在一个最小范围内的包里。

参考:http://c2.com/cgi/wiki?CommonClosurePrinciple

Common Reuse Principle (CRP) – 共同重用原则

包的全部类被一块儿重用。若是你重用了其中的一个类,就重用所有。换个说法是,没有被一块儿重用的类不该该被组合在一块儿。CRP原则帮助咱们决定哪些类应该被放到同一个包里。依赖一个包就是依赖这个包所包含的一切。当一个包发生了改变,并发布新的版本,使用这个包的全部用户都必须在新的包环境下验证他们的工做,即便被他们使用的部分没有发生任何改变。由于若是包中包含有未被使用的类,即便用户不关心该类是否改变,但用户仍是不得不升级该包并对原来的功能加以从新测试。

CCP则让系统的维护者受益。CCP让包尽量大(CCP原则加入功能相关的类),CRP则让包尽量小(CRP原则剔除不使用的类)。它们的出发点不同,但不相互冲突。

参考:http://c2.com/cgi/wiki?CommonReusePrinciple

Hollywood Principle – 好莱坞原则

好莱坞原则就是一句话——“don’t call us, we’ll call you.”。意思是,好莱坞的经纪人们不但愿你去联系他们,而是他们会在须要的时候来联系你。也就是说,全部的组件都是被动的,全部的组件初始化和调用都由容器负责。组件处在一个容器当中,由容器负责管理。

简单的来说,就是由容器控制程序之间的关系,而非传统实现中,由程序代码直接操控。这也就是所谓“控制反转”的概念所在:

  1. 不建立对象,而是描述建立对象的方式。
  2. 在代码中,对象与服务没有直接联系,而是容器负责将这些联系在一块儿。

控制权由应用代码中转到了外部容器,控制权的转移,是所谓反转。

好莱坞原则就是IoC(Inversion of Control)或DI(Dependency Injection )的基础原则。这个原则很像依赖倒置原则,依赖接口,而不是实例,可是这个原则要解决的是怎么把这个实例传入调用类中?你可能把其声明成成员,你能够经过构造函数,你能够经过函数参数。可是 IoC可让你经过配置文件,一个由Service Container 读取的配置文件来产生实际配置的类。可是程序也有可能变得不易读了,程序的性能也有可能还会降低。

参考:

  • http://en.wikipedia.org/wiki/Hollywood_Principle
  • http://en.wikipedia.org/wiki/Inversion_of_Control

High Cohesion & Low/Loose coupling & – 高内聚, 低耦合

这个原则是UNIX操做系统设计的经典原则,把模块间的耦合降到最低,而努力让一个模块作到精益求精。

  • 内聚:一个模块内各个元素彼此结合的紧密程度
  • 耦合:一个软件结构内不一样模块之间互连程度的度量

内聚意味着重用和独立,耦合意味着多米诺效应牵一发动全身。

参考:

  • http://en.wikipedia.org/wiki/Coupling_(computer_science)
  • http://en.wikipedia.org/wiki/Cohesion_(computer_science)

Convention over Configuration(CoC)– 惯例优于配置原则

简单点说,就是将一些公认的配置方式和信息做为内部缺省的规则来使用。例如,Hibernate的映射文件,若是约定字段名和类属性一致的话,基本上就能够不要这个配置文件了。你的应用只须要指定不convention的信息便可,从而减小了大量convention而又不得不花时间和精力啰里啰嗦的东东。配置文件不少时候至关的影响开发效率。

Rails 中不多有配置文件(但不是没有,数据库链接就是一个配置文件),Rails 的fans号称期开发效率是 java 开发的 10 倍,估计就是这个缘由。Maven也使用了CoC原则,当你执行mvn -compile命令的时候,不须要指源文件放在什么地方,而编译之后的class文件放置在什么地方也没有指定,这就是CoC原则。

参考:http://en.wikipedia.org/wiki/Convention_over_Configuration

Separation of Concerns (SoC) – 关注点分离

SoC 是计算机科学中最重要的努力目标之一。这个原则,就是在软件开发中,经过各类手段,将问题的各个关注点分开。若是一个问题能分解为独立且较小的问题,就是相对较易解决的。问题太过于复杂,要解决问题须要关注的点太多,而程序员的能力是有限的,不能同时关注于问题的各个方面。正如程序员的记忆力相对于计算机知识来讲那么有限同样,程序员解决问题的能力相对于要解决的问题的复杂性也是同样的很是有限。在咱们分析问题的时候,若是咱们把全部的东西混在一块儿讨论,那么就只会有一个结果——乱。

我记得在上一家公司有一个项目,讨论就讨论了1年多,项目原本不复杂,可是没有使用SoC,所有的东西混为一谈,再加上一堆程序员注入了各类不一样的观点和想法,整个项目一会儿就失控了。最后,原本一个1年的项目作了3年。

实现关注点分离的方法主要有两种,一种是标准化,另外一种是抽象与包装。标准化就是制定一套标准,让使用者都遵照它,将人们的行为统一块儿来,这样使用标准的人就不用担忧别人会有不少种不一样的实现,使本身的程序不能和别人的配合。Java EE就是一个标准的大集合。每一个开发者只须要关注于标准自己和他所在作的事情就好了。就像是开发镙丝钉的人只专一于开发镙丝钉就好了,而不用关注镙帽是怎么生产的,反正镙帽和镙丝钉按标来就必定能合得上。不断地把程序的某些部分抽像差包装起来,也是实现关注点分离的好方法。一旦一个函数被抽像出来并实现了,那么使用函数的人就不用关心这个函数是如何实现的,一样的,一旦一个类被抽像并实现了,类的使用者也不用再关注于这个类的内部是如何实现的。诸如组件,分层,面向服务,等等这些概念都是在不一样的层次上作抽像和包装,以使得使用者不用关心它的内部实现细节。

说白了仍是“高内聚,低耦合”。

参考:http://sulong.me/archives/99

Design by Contract (DbC) – 契约式设计

DbC的核心思想是对软件系统中的元素之间相互合做以及“责任”与“义务”的比喻。这种比喻从商业活动中“客户”与“供应商”达成“契约”而得来。例如:

  • 供应商必须提供某种产品(责任),而且他有权指望客户已经付款(权利)。
  • 客户必须付款(责任),而且有权获得产品(权利)。
  • 契约双方必须履行那些对全部契约都有效的责任,如法律和规定等。

一样的,若是在程序设计中一个模块提供了某种功能,那么它要:

  • 指望全部调用它的客户模块都保证必定的进入条件:这就是模块的先验条件(客户的义务和供应商的权利,这样它就不用去处理不知足先验条件的状况)。
  • 保证退出时给出特定的属性:这就是模块的后验条件——(供应商的义务,显然也是客户的权利)。
  • 在进入时假定,并在退出时保持一些特定的属性:不变式。

契约就是这些权利和义务的正式形式。咱们能够用“三个问题”来总结DbC,而且做为设计者要常常问:

  • 它指望的是什么?
  • 它要保证的是什么?
  • 它要保持的是什么?

根据Bertrand Meyer氏提出的DBC概念的描述,对于类的一个方法,都有一个前提条件以及一个后续条件,前提条件说明方法接受什么样的参数数据等,只有前提条件获得知足时,这个方法才能被调用;同时后续条件用来讲明这个方法完成时的状态,若是一个方法的执行会致使这个方法的后续条件不成立,那么这个方法也不该该正常返回。

如今把前提条件以及后续条件应用到继承子类中,子类方法应该知足:

  1. 前提条件不强于基类.
  2. 后续条件不弱于基类.

换句话说,经过基类的接口调用一个对象时,用户只知道基类前提条件以及后续条件。所以继承类不得要求用户提供比基类方法要求的更强的前提条件,亦即,继承类方法必须接受任何基类方法能接受的任何条件(参数)。一样,继承类必须顺从基类的全部后续条件,亦即,继承类方法的行为和输出不得违反由基类创建起来的任何约束,不能让用户对继承类方法的输出感到困惑。

这样,咱们就有了基于契约的LSP,基于契约的LSP是LSP的一种强化。

参考:http://en.wikipedia.org/wiki/Design_by_contract

Acyclic Dependencies Principle (ADP) – 无环依赖原则

包之间的依赖结构必须是一个直接的无环图形,也就是说,在依赖结构中不容许出现环(循环依赖)。若是包的依赖造成了环状结构,怎么样打破这种循环依赖呢?有2种方法能够打破这种循环依赖关系:第一种方法是建立新的包,若是A、B、C造成环路依赖,那么把这些共同类抽出来放在一个新的包D里。这样就把C依赖A变成了C依赖D以及A依赖D,从而打破了循环依赖关系。第二种方法是使用DIP(依赖倒置原则)和ISP(接口分隔原则)设计原则。

无环依赖原则(ADP)为咱们解决包之间的关系耦合问题。在设计模块时,不能有循环依赖。

参考:http://c2.com/cgi/wiki?AcyclicDependenciesPrinciple

上面这些原则可能有些学院派,也可能太为理论,我在这里说的也比较模糊和简单,这里只是给你们一个概貌,若是想要了解更多的东西,你们能够多google一下。

不过这些原则看上去都不难,可是要用好却并不那么容易。要能把这些原则用得好用得精,而不教条,个人经验以下:(我觉得这是一个理论到应用的过程)

  1. 你能够先粗浅或是表面地知道这些原则。
  2. 但不要急着立刻就使用。
  3. 在工做学习中观察和总结别人或本身的设计。
  4. 再回过头来了回顾一下这些原则,相信你会有一些本身的心得。
  5. 有适度地去实践一下。
  6. Goto第 3步。

3—逻辑层 vs 物理层

Layer 和Tier都是层,可是他们所表现的含义不一样,Tier指的是软件系统中物理上的软件和硬件,具体指部署在某服务器上,而Layer(逻辑层)指软件系统中完成特定功能的逻辑模块,逻辑概念。

Layer是逻辑上 组织代码的形式。好比逻辑分层中表现层,服务层,业务层,领域层,他们是软件功能来划分的。并不指代部署在那台具体的服务器上或者,物理位置。

Tier这指代码运行部署的具体位置,是一个物理层次上的划为,Tier就是指逻辑层Layer具体的运行位置。因此逻辑层能够部署或者迁移在不一样物理层,一个物理层能够部署运行多个逻辑层。

从Layer和Tier就会延伸到逻辑架构和物理架构。咱们一个逻辑分层(N-Layer)的部署运行环境能够在一台或者是多台服务器,因为物理环境的多样性,逻辑层次的部署也具备多样性。这就须要咱们必须了解物理架构和逻辑架构。

大多数状况下咱们所说的N层应用系统指的是物理模型,具体模块的分布物理位置。客户端,服务层,逻辑层,数据库服务器,与咱们的逻辑模型之间并非一对一的关系。逻辑上的分层架构与物理位置上的服务器数量和网络边界多少无关,逻辑架构层次只与咱们的功能划分相关,是按照功能划分。经典的3-Layer架构:表现层,业务层,数据访问层,他们可能运行在同一物理位置上。也能够是3台计算机上,这并非逻辑架构所关注的。逻辑层次和物理分层数量关系为:逻辑层数必须不小于物理层数,由于一个物理层能够部署一个或者多个逻辑层次,逻辑层次只能迁移在不一样的物理环境。

逻辑层次的架构能帮助咱们解决逻辑耦合,达到灵活配置,迁移。

一个良好的逻辑分层能够带来:

  1. 逻辑组织代码
  2. 易于维护
  3. 代码更好的重用
  4. 更好的团队开发体验
  5. 代码逻辑的清晰度

一个良好的物理架构能够带来:

  1. 性能的提高
  2. 可伸缩性
  3. 容错性
  4. 安全性

逻辑层次越多会影响程序运行的性能,但代码层次的低耦合,松散化,是须要架构师的权衡的,我以为通常应用程序的瓶颈并不在这里。

4—服务层的简单理解

在ddd设计中咱们常常会提到服务层,服务层是什么?职责是什么?有什么好处?。

先看简单的层次图(注:这里并无考虑其余多余的领域逻辑数据层存储,或者UOW这些细节)

个人理解是服务层是处于个人应用程序业务层和表现层之间的应用程序边界,边界多是很薄的一层类设计或者是分布式服务网络跃点。它是一个与技术无关的名词。由表现层直接调用,契约,执行命令(修改状态(CUD))或者是查询返回dto(数据迁移对象)(cms,命令-查询分离)。他对业务逻辑层接口很清楚,组织业务逻辑 微服务造成宏服务,适配表现层。

这里谈到宏服务和微服务,宏服务有一些列粗粒度的服务组成。用户的一次操做usecase,好比电子商务下单,CreateOrder就是一个宏服务,而不是下单中的细粒度的商品库存检查,订单合法性等。而与之对应的微服务(有时也叫应用程序服务),则表现为问题领域逻辑细节,就如上面的库存检查和合法性检查这些细粒度的服务。宏服务是由一个或者多个微服务组成,有时咱们的usecase逻辑很简单服务层仅由单一微服务组成,变现为很简单的几句微服务调用。

服务层的职责:

1:在面软件开发无论是结构化编程(sp)仍是面向对象编程(oop)咱们一直都强调高内聚低耦合,分离关注点(soc)。服务层处于应用程序和业务层之间,应用边界,使得两次直接解耦,利用第三个对象破坏两对象直接的依赖,并转化适配领域对象(do)和试图对象(vo)的差别。

2:服务层隐藏了业务逻辑层的细节,其内部须要组织业务微服务,提供更宏观,面向表现层的服务逻辑,利用契约接口暴露,包装。系统全部的交互都是从表现层进入。

目前流行SOA架构,提供了一种分布式服务架构,以服务为关注点,提升服务和业务逻辑的重用,可是这里说的服务并非特定的技术wcf或者webservice,服务同时候多是一次规定契约的一些列粗粒度组织的类组成。可是利用SOA或者MTS创建服务会让咱们的服务获得跟多的附加优点,例如安全,事物,日志,扩展性的提高。

服务层带来的优点:如上所述服务层为表现层提供的同一的接口契约和入口。让咱们的业务层能够关注与实现问题领域逻辑,问题领域实际需求。组织微服务避免太多的细粒度服务的调用充斥在咱们的项目表现层和问题领域中,过多的交互。若是采用soa等服务领域可让咱们的应用程序轻易的跨过应用程序边界和网络跃点。可是须要付出一点的性能代价。

数据迁移对象(dto)就是携带数据穿过应用程序边界的对象,减小数据的交互次数,经常咱们将其做为值对象,只是一组简单的get,set属性组成,不存在行为操做,仅仅为数据的载体。在领域设计中dto是一个很重要的模式,不是咱们全部的领域对象都能轻松的到达表现层,仅仅表现层和领域层部署在同一物理位置。若是须要穿过网络跃点或者进程边界,由于领域对象使咱们的业务的核心存在不少的天然世界的关系,依赖,甚至可能存在循环依赖好比电商用户和订单,用户用户一组订单的集合,而每一个订单都指向一个特定的用户,咱们就必须破换掉这种循环依赖,才可能使其可序列化,穿过跃点。其次咱们的领域对象每每都是一堆领域富对象,存在大量数据,不少时候咱们的场景并不须要所有的数据信息。有了dto的存在就能很好的解决这些问题,是的咱们的项目变得simple(keep it simple,Stupid。 KISS原则)。

可是与此同时dto存在会为咱们带来一些额外的复杂度,咱们必须有一层do到dto的映射适配层。

理论上完美的设计咱们须要为每个应用定义一个dto,可是在一个复杂的系统中咱们可能存在不少的领域对象,加入500个do,每一个do通常都会存在多个dto,这将一个增长一个庞大的集合和mapping逻辑,对于维护也存在不小的挑战。在软件领域存在一句话就是bug的数量随着代码量增长,代码量增长须要测试点也随着增长。除非咱们必须跨越应用程序网络跃点边界,我以为不然咱们也能够存在一些简单do的直接使用。根据世界项目,情形由咱们的架构师决定。

5—SOA面向服务架构简述

在上篇中咱们简单谈了下架构设计中服务层的简单理解,在这里咱们将继续服务层的架构,在本节咱们将重点在于分布式服务。在分布式系统中表现层和业务逻辑层 并不处于同一物理部署,因此咱们必须存在分布式服务,以契约方式发布于网络中,咱们的关注点在于服务,面向服务编程,这种经过组合业务逻辑暴露可用服务的架构叫作面向服务架构(SOA)。

SOA强调一个松耦合,基于宏服务的架构,经过契约暴露给服务消费者可用的服务交互。SOA是以服务为组成构建,原则有:

1.边界清晰:

服务层是消费者交互到系统业务的惟一入口,全部咱们的服务必须可以被消费者所理解,以及最好处理Request/Response基于消息交换RPC调用,职责明确单一.还有咱们更但愿咱们的服务为做用明确的,CQS(命令-查询分离原则).

2.服务的自治性

服务自治主要表如今每一个服务都是独立的,其系统部署,管理监控都是独立的。自治体现了服务的松耦合,但并非服务就是一个孤岛,其能够经过消息交换消费其余服务。

3.使用契约(接口和数据载体),而非实现

这也是面向对象设计第一原则。在咱们的服务设计中SOA一个重要目标就是互操做,基于SOAP等标准协议实现跨平台互操做,可能存在异构系统。因此咱们该选择接口而不是语言具体的类以及基于消息交互。服务对于开发就是一些列行为的组合,数据契约就是数据迁移对象,数据载体。契约使得咱们并不关心服务的内部实现,而只关心提供了那些服务,服务的签名如何,怎么调用之类的。

4.兼容性基于策越

对于消费者来讲服务是否能知足他的需求,这须要服务语义兼容,语义兼容也应该经过可访问方式暴露。是的服务可发现。

SOA是一种设计原则规范,其目标在于为复杂系统提供互操做性和以服务为基础组件构造系统逻辑。把具体的业务逻辑和流程屏蔽,暴露出用户可用的行为集合。SOA是一中原则而非集体技术。wcf,webservice是具体SOA技术。同时SOA也不是咱们的目标,客户是不与关心咱们采用soa与否,这只是咱们对系统的一种解决方案。

SOA优点在于给咱们提供更好的代码重用,版本控制,安全控制,扩展延伸性。同时下降和服务的耦合,交互必须依赖于服务契约和数据契约,并不关心服务的内部实现。在咱们的版本升级,修改过程当中能够彻底能够从新实现替换原有服务,并不会影响消费程序的使用。

最后咱们必须的说下当下流行的restfull,一般咱们认为这是一种风格,而非架构,是由Roy Thomas Fielding在其博士论文 《Architectural Styles and the Design of Network-based Software Architectures》中提出REST是英文Representational State Transfer的缩写,中文翻译为“表述性状态转移”。是一种基于web的架构,它很好的利用http协议的method。根据不一样的method表示对资源的不一样语义操做。其核心在于将发布在网络的一切事物归属为资源,每一个资源定位于一个资源定位符(URI)。以及无状态,缓存,分层架构。在微软最新的WCF resetfull,web api应用框架。以及wcf ria ,wcf data service,须要的注意的是微软同时候加入的本身的oData协议(开元数据协议)。

最后说一点:我以为无论是服务或者resetfull服务咱们都必须定义契约,依赖于契约,虽然微软的而技术容许咱们直接寄宿服务类,可是对于服务的扩展和延伸而言,说这句话的缘由在于我最近看见一些直接寄宿服务类的resetfull架构。

6—业务逻辑层简述

业务逻辑层是专门处理软件业务需求的一层,处于数据库之上,服务层之下,完成一些列对Domain Object的CRUD,做为一组微服务提供给服务层来组织在暴露给表现层,如库存检查,用法合法性检查,订单建立。

业务逻辑层包含领域对象模型,领域实体,业务规则,验证规则,业务流程。1:领域对象模型为系统结构描述,包含实体功能描述,实体之间的关系。领域模型处于天生的复杂性:2:领域实体:业务层是一些操做业务对象(BO)的处理。业务对象包含数据和行为,是一个完整的业务对象。其不一样于上节架构设计中服务层的简单理解提到的数据迁移对象(dto),对于dto存在数据的,不存在行为,dto是bo(ddd中又称do)的子集,负责与特定界面需求的扁平化实体,dto仅仅是一个数据载体,须要跨越应用程序边界,而业务对象则不会存在复制迁移,每每一个业务对象存在一个或者多个数据迁移对象。3:业务最大的逻辑就在处理一些列现实世界的规则,这也是软件中最容易变化的部分,这里一般会出现咱们众多的if-else或者switch-case的地方。也这由于若是说以我的以为在咱们的项目最应该关系和分离需求的层次。4:验证规则:业务规则很大程度上也是对对象的数据验证,验证业务对象的当前数据状态。我以为在每一个业务对象上都应该存在一个对外部对象暴露的验证接口,能够考虑微软企业库的VAB 基于Attribute声明式验证或者上节FluentValidation验证组件基于IOC的解耦。

业务层模式:在常见的业务层模式中主要分为过程是模式和面向对象模式。过程模式有是事务性脚本和表模式,而面向对象模式为活动记录模式和领域驱动模式。理论上说事务性脚本模式是最简单的开发模式,其前期投入下,但随着项目周期和复杂度上升明显,而领域模型(DDD)前期投入较大,可是理论上说是随着项目周期和复杂度呈线性增长,固然这些都是理论值。

1:事务脚本模式是业务逻辑层最简单的模式,面向过程模式。该模式以用于的操做为起点,设计业务组件,即业务逻辑直接映射到用户界面的操做。这一般是从表现层逻辑出发,表现层我须要什么业务层提供什么,直到数据层。针对没一个用户的新功能都须要新增一个从UI到关系数据库的分支流程。其使用与逻辑不是很复杂或者变化不大稳定的应用系统开发。其不须要付出与业务无关的额外代价,而且在现代VS之类的IDE帮助下可以很快的进行快速应用开发(RAD)。也因为这种优点,也是其最大的劣势,程序中充满了IF-else,switch-case之类的逻辑或者大量的static的方法,每一个功能都是一个程序分支,这对代码没法重用。编码不易于维护,对复杂项目和变化需求不适应。

2:表模式:为每一个数据库表定义一个表模块类,包含操做该数据的全部行为方法。做为一个容器,将数据和行为组织在一块儿。其对数据的粒度针对于数据表,而非数据行,所以须要以集合或者表传递数据信息。表模式基于对象可是彻底又数据库驱动开发,在业务模型和数据库关系模型显著差别的状况下,应对需求,并非那么适合。可是在.net中提供的一些列如强类型DataSet等IDE的辅助下自动生成大量的代码,也是一个不错的选择,由于部分数据库的操做趋于自动化。表模式没太过于关注业务,而是关注数据库表结构。而业务逻辑和领域问题才是软件核心。

3:活动记录模式:一个以数据库表一行Row为对象,而且对象中包含行为和数据的模式方法。其数据对象很大程度的接近数据库表结构。在活动记录模式对象中一般也包含操做对象的CRUD行为,数据验证等业务规则。对于业务不是很复杂,对象关系与关系模型映射不具备很大差别状况,活动记录模式会运用的很好。活动模式比较简单化设计,在上现行的不少如Linq to sql,ActiveRecord框架的辅助下,将针对问题领域不是太过复杂的项目十分有用。可是其模式和数据库表结构的相互依赖,致使若你修改数据库结构,你不得不一样时修改对象以及相关逻辑。若是不能保证数据库关系模型和对象模式的很大程度的类似这就进入的困境。

4:领域模型:在前面的几种模式都是项目开始站在了以数据为中心的角度,而不是业务自己的问题领域。而领域模型关注系统问题领域,首先开始为领域对象设计。与活动记录模式来讲,领域模型彻底站在了问题领域业务概念模型一边,与数据库,持久化完成独立,其推崇持久化透明(POCO)。其能够充分利用面向对象设计,不受持久化机制的任何约束。其实彻底又业务驱动出来的。可是其最大的优点如上各个模式同样也是其最大的劣势对象模型和关系模型具备自然的阻抗,咱们的领域实体迟早须要映射到持久化机制。还好的是当前有NHibearnate,EF,Fluent NHibearnate这类ORM框架辅助。在DDD中包含UOW,仓储,值类型和聚合根,领域事件,领域跟踪一类的概念,这将在之后具体说明。

模式的选择在与架构师的决定,这也是架构师具备挑战意义的职责,须要根据具体的项目需求,团队,我的等外界因素最终决定,不存在万能的模式,也不存在完美的设计。

7—设计箴言理解

今天和师弟聊天聊到他们项目开发,有些同事老是提早考虑性能优化,需求变动又是一大堆的重写,让我想起了Donald Knuth 提到的:对软件的过早地优化是万恶的根源。这里就简单的说几条重要的软件名人哲学。

1:软件中惟一不变的就是变化。

在软件开发过程当中需求是不停的变化,随着客户对系统的认识,和现有开发功能和软件的认识,也许以开始他提出的需求就是背离的。记得网上有一句笑话,师说需求变化的:

程序员XX遭遇车祸成植物人,医生说活下来的但愿只有万分之一,唤醒更为渺茫。可他的Lead和亲人没有放弃,他们根据XX工做如命的做风,天天都在他身边念:“XX,需求又改了,该干活了,你快来呀!”,奇迹终于发生了,XX醒来了,第一句话:“需求又改了

在设计和架构中,凡事无绝对,做为架构师或者项目负责人你必须永远的清晰认识到没有完美的架构和设计,没有万能的软件。只存在当前环境,需求方案,团队人员素质,物理环境,安全等综合因素下的合适方案,因为总总缘由你的解决方案可能不是某一个单一因素下的最优解。站在这个位置你须要作的是找到这个综合下的最优解,权衡。不要只从表面说某我的某个团队的解决方案怎么查怎么很差,或者这就是当时综合因素的最优解,站在一样的位置环境你不必定作得更好。在架构设计和人生,在我看来很类似,老是有一堆抉择,每一次的抉择都会带来得和失,权衡得失取舍。

2:KISS:(Keep It Simple,Stupid):

保持简单,但不过于太简单。在《UNIX下的编程哲学》中提到不少保持设计简单,咱们能清晰看到这条原则。如今视觉设计,都崇尚简约设计,简单而不庸俗,而不是一大堆的豪华奢侈打造。VB编程开始的可视化设计,可见便可得,google的首页,商业风格。在咱们的软件设计中也须要简洁的设计,用户须要的是可见可量化的功能的正确性,而是你运用了多牛b的技术模式,但毫不是一味的太过于简单。你想把意见简单的事情作复杂化是很容易的事情,可是把一件复杂的事情简单化却不那么容易。简单的人生就是幸福。可是这里须要说明的是简单是优秀的,但简单是有底线边界的,超过底线的简单也有变得稚幼。好比事务性脚本模式比其余3中常见模式都简单,但每每复杂的需求它不是最优解,由于他太过于简单了(若是你还不了解是事务性脚本能够参见这里架构设计-业务逻辑层简述)。

3:面向抽象编程。

在设计模式,架构模式,OO中都是一条彻底的主线,做为oo第一原则存在。我不起那个软件牛人曾说过:请牢记没有接口的话就不要开始实现。这句话也许过于偏激,可是若是你接口理解为不变或者不易变的话,理解或契约(公司和你的合同)更贴切些吧(多是一个不变的类,若是你能确定的说出你的这个实如今之后,在项目开发维护中是不会变得,我以为这也是接口,接口在于不变和不易变),你也许会赞成这句话。对于目前的需求你确定可以没有抽象没够接口彻底写出完美的代码,可是第一条中咱们说明的软件中惟一不变的就是变化,在将来的需求中你可以很好的同样的优秀吗?若是不能,那么我认为面对当前需求就该为之后提供扩展延伸。

我我的理解23中设计模式中大多数基本都是围绕着这个Program to an interface, not an implementation(依赖接口而不是实现)第一原则为目的。固然咱们也不能不说还有第二原则:组合优先于继承。之后的什么DIP(依赖倒置,IOC的原则),LSP(里氏替换),OCP(开闭原则)等等都是他们的延伸和扩展。在追溯的话这一些列都是为了软件系统“高内聚,低耦合”(能够简叙述为:功能完备(高内聚)的对象之间是靠接口(低耦合)通信交互的),内聚是描述的功能性完备程度,耦合是表述模块间的依赖程度。这里插一句话某同事给我说依赖接口不是还有依赖嘛,我但愿的是没有耦合,个人回答是:计算机二八原则说明了这一切,既然事务出如今一块儿了,那毫不是偶然状况,因此他们之间一定存在依赖,在软件设计中咱们所能作的就是引入中间对象使其变为间接依赖,而减小他们之间的依赖,而咱们但愿这个中间对象是个相对稳定的,设计中一切都是一个词:间接,分层,mvc,mvp,soa,中间件等等都是体现直接依赖变为间接依赖。说这个话题的缘由是引出咱们“高内聚,低耦合”行之有效的方法SOC(分离关注点),这不仅是OO的任然对面向过程编程行之有效,他是在20年前 SP(结构化编程)中提出来的。

若是你想对设计原则有更多的了解,能够参见这里《java与模式》读书心得。

4:首先考虑可维护,延伸性,过后优化

这里也是本文的原由,正如开篇所说,Donald Knuth 提到的:对软件的过早地优化是万恶的根源。在开发的时候咱们不须要进行任何性能的优化,即便你认为这里可能存在性能的瓶颈,你须要考虑的更多的是设计的扩展和延伸性,之后的继续添加新功能和维护。对于用户需话要的需求,性能优化不少时候只是做为一个更好的体验存在。只有当真正出现性能瓶颈的时候,你才须要作性能的优化。一个可延伸可扩展,井井有条,代码清晰的模块,对于你的优化也是件容易的事情,在对项目后期对于项目的整体需求明白下你也有获得更多的优化方案。在重构模式中一样也提倡时候优化。过早的优化致使你的项目会越陷越深,到最后才知道用户其实根本不须要这么高的需求,或者是用户根本不经常使用的功能模块。优化也须要有标准,多少时间是用户能忍受的,目前是多少时间。每每用户对性能要求的只有那个少许经常使用的操做,而对于功能性需求的变动倒是无止境的,维护成本倒是高昂的。

最后说一句,常常有人说反射性能低下,对咱们必须认可反射比其余方案性能是很差,可是咱们有解决方案:缓存。在则说性能低下,是以什么什么标准?用户的接受程度?反射咱们能够有其替代方案Emit,Expression tree。从反射,Expression tree,Emit的选择,其使用难度在提高,开发效率在增长,性能在改善。本人通常却倾向于Expression tree,两种剧中吧。

5:继承是为了多态而不是重用

OOP中能够编写一个类,而后我能够不断的继承重用去扩展新需求。这是类的重用,是所有的重用?重用这个词看上去也许更加的微妙。多态是面向对象的核心特征之一,也不记不清那里听到的:重用只是继承的附带功能。在咱们的继承体系中不宜庞大若是一个拥有4,5层的继承体系,对你的理解也增长难度,并且集成体系必须是个干净的继承体系,知足LSP(里氏替换原则):在全部用到父类的地方均可以替换为子类,还能正常准确工做。这就要求你继承更多的是修改扩展父类的行为,尽可能避免状态。继承只是不要为了重用的为目的,在恰当的时机更好的办法是实现一个彻底的类来替换不能知足现有需求的类。这也是oo原则第二原则吧,组合优先于继承。组合好比设计模式中的策略模式,你获得的是一个算法组合功能个数是一个笛卡尔积。但也是绝对的组合,只是优先,不是取代,软件和现实世界都是充满了矛盾的,就如开篇第一条“软件中惟一不变的就是变化”就是最大的矛盾,来自辩证惟物主义,你要作的是权衡。组合表述的是总体的替换,如策略模式模式的算法总体替换。继承是部分的少许的扩展修改行为,好比设计模式中的模版方案,在父类的流程控制下,部分步骤的修改,数据,事务的流转控制权在父类。这条在最后说一句:设计模式不是万能的,只是前人的优秀经验,是依赖于场景存在的,了解设计模式我以为更重要的是其使用场景,在碰见同类场景的时候知道能够有这种模式做为解决方案或许更好,仅做为供你选择的解决问题方案。

6:用户的一切输入都是万恶的

用户的输入是属于咱们系统以外的,是没法控制的,是不可罗列的。对于用户来讲软件只是一个黑盒子,不须要,也不必了解具体内在实现。对于汽车销售人员不须要了解发动机螺栓是怎么上的同样,他了解宣传的是能有什么优点,能给用户带来那些方面的知足,价格?性能?速度?豪华?….对于门户网站来讲你对应的用户不只是可信任的用户,可能还有竞争对手黑客攻击行为。若是你的系统信任于用户的输入,迟早一天总会“纸包不住火的”,用户有意无心的一次输入就可能致使你系统的功能性的全盘崩溃,你不该该限制用户的操做,你是不能命令用户该输入什么不能输入什么,好比某天某人使用用户可能降工资了或者挨批了,心情很差,你也许会潜意思的对你的系统进行挑战。

说到这里随便说一句,之前项目组有人层提过因为自动化测试服务器运行时间太长了,把部分验证等逻辑移到单元测试中保证。对于个人理解来讲自动化测试近似于集成测试吧,功能性测试,应该是黑盒子。在单元测试中咱们老是假设输入是正确的,某个依赖也是正确的,验证输出的正确。而集成测试重点在于这一些都是层次的组合,贯通,不存在假设的正确性,只有来自测试人员的测试用例获得预期的输出。

今天就写到这里吧,还有不少可是一下想不起来,后续有机会的话对于重要的也会继续补上。

现实是矛盾的,没有完美的设计,也没有绝对的简单。生活也是如此就如:简单就是幸福,快乐就是幸福。那么简单的标准是什么?怎样才是快乐?这在于你本身的抉择,权衡。想起了某次面试和小公司面试官谈话,面试官说ORM存在性能问题,并且一直在纠结的说反对DDD,反对模式。本人先说了若是存在了性能问题有什么解决方案,首先怎么作若是不能知足再怎么作,从索引缓存到分表服务集群,再总结性的一句话:架构如人生,老是要面临获得取舍。

8—数据访问层简述

在前面简单描述了下服务层,SOA面向服务架构,架构设计-业务逻辑层,以及一些面面向设计原则理解和软件架构设计箴言。这篇博客咱们将继续进入咱们的下一层:数据访问层。不管你用的是什么开发模式或者是业务模式,到最后最必须具备持久化机制,持久化到持久化介质,并能对数据进行读取和写入CRUD。这就是数据访问层。你多是利用xml等文件格式磁盘存储,经常使用的关系数据库存储,或者NoSql(not only sql)的内存存储或文档存储等等存储介质。而这里我只关心关系数据库存储。

数据层须要提供的职责有:

1:CRUD服务。做为惟一能够与存储介质交互的中间层出现,负责业务对象的增长,修改,删除,加载。

2:查询服务。这不一样于CRUD中的R(read),read倾向于的单个对象,元组。而这里的查询针对复杂查询,好比一个国内电商的客户为四川的订单。这里会涉及仓储层。所谓仓储模式指的是一个提供业务对象查询的类,他隐藏了数据查询的解析步骤,封装sql解析逻辑。

3:事务管理。这里所说的是业务事务,在一个应用系统中每次请求都会产生屡次的多数据对象的新增,修改,删除操做。若是咱们每次都依次代开数据库链接,准备数据包,操做数据库,关闭数据链接。这些将会给咱们带来不少没必要要的性能开销。数据库管理员常常会要求“尽可能少的与数据库交互”,这也必须成为咱们的开发原则。更好的操做是咱们在内存中创建一个和数据仓库,维护变化的对象,在业务操做完成一次性提交到数据存储介质,提供业务事务。业务事务有个很好听的名字工做单元(UOW),在微软给咱们提供的DataSet,orm框架都回必须存在业务事务。

4:并发处理。UOW应避免业务数据链接的屡次提交打开而出现,但在内存离线操做,这就可能致使数据一致性问题。在多用户的环境,对数据并发处理须要制定一个策略。通常咱们会采用乐观并发处理:用户能够任意的离线修改,在修改更新时候检查对象是否被修改,若是被修改者本次更新失败。简单的说就是防止丢失修改。防止丢失修改,咱们能够采用where 加上一系列原值,或者加上修改时间戳或者版本号标记。同时还有许多其余的并发解决模式,但乐观并发锁用到更广泛。

5:数据上下文:整和全部职责。在数据访问层概念职责都会有一个共同的暴露给外部的接口。咱们须要一个高层次的组件,来同一提供对数据存储介质的访问操做。,同一访问数据库CRUD,事务,并发服务的高层次类,叫作数据上下文(Context)。EF中的ObjectContext,NHibernate的session,linq to sql 的DataContext等等。

数据访问层的一些概念(这里不会是所有,仅一些我的以为重要的概念):

1: 数据映射器:将内存中修改的对象提交至存储介质,则须要要映射逻辑来完成,数据映射器就是就是一个实现将某种类型的业务对象持久化的类(数据映射器模式定义如《P of EAA》)。

2:仓储层(Repository):在上面提到:所谓仓储模式指的是一个提供业务对象查询的类,他隐藏了数据查询的解析步骤,封装sql解析逻辑。在面向对象的世界里咱们用对象进行查询,返回结果为对象集。这里的查询多是从数据库,或者来至缓存,这取决你的策略,你仓储层的实现。

3:工做单元(UOW):Martin Fowler《P of EAA》 定义:工做单元记录在业务事务过程当中对数据库有影响的全部变化。操做结束后,做为一种结果,工做单元了解全部须要对数据库作的改变。在上面第3点业务事务讲的差很少,这里不是累述。

4:标示映射(Identity Map):其做用在于:便于跟踪业务对象,调用者在一个业务事务中使用的是同一个实例,而不是每次执行产生一个新的对象。表示映射为一个散列表存储(散列具备快速定位O(1))。相似于缓存的实现方式,保证了在同一个业务事务数据上下文引用修改同一个业务对象。但毫不同于缓存。从持续时间来讲,标示映射生命周期为业务事务内。实现上等同于数据上下文期。但比起缓存来讲其周期过短,根本不能对性能有多大的改善。缓存更重要的命中率,而标示映射保证同一数据上下文采用修改同一个业务对象的引用。

5:乐观并发锁:在上面也曾提到,其保证离线操做数据的对数据一致性的冲突解决方法。首先乐观在于容许离线操做,容忍冲突,防止数据丢失修改,可利用原读取数据值得where条件或者时间戳,版本号解决。

6:延时加载:对象并非一次性加载完成,而是按照需求屡次加载数据,到用时加载。业务对象太多关联,数据量太多余庞大,而咱们每次业务事务须要操做的对象都只会是部分,不须要太多的数据对象。同事业务对象中还存在循环引用,这样不适于对象总体的一次性加载。延时加载提供了优化,意图在“尽量的少加载,并按需加载,只加载须要的数据部分”。

7:持久化透明对象(PI或POCO):当对象模型不存在任何外部依赖,特别是对于数据访问层的依赖,那么这个模型就是持久化透明的,POCO。一个POCO的对象不须要继承至某个特定的类,实现特定的接口,或提供专门的构造函数。一个非持久化透明的对象这觉得者存在外部的依赖,而咱们更喜欢领域对象只是一个简单额c#类,能够在持久化层等独立切换。这就致使实现的时候咱们没法很直接的跟踪业务对象,这就是面向方面编程(AOP)或者代理模式的大显身手。惋惜AOP在.net中不是那么直接,不少ORM框架如NHibernate之类的利用代理模式Emit动态注入IL实现跟踪,添加新的行为。因此NHibernate中要求领域对象的全部字段属性方法都必须是虚方法可重写的。:

8:CQRS(Command Query Responsibility Segregation,命令查询职责分离):CQRS是在DDD的实践中引入CQS理论而出现的一种体系结构模式,命令和查询被分离。

9—存储过程传言

在google搜了下“存储过程 优劣”关键字,资料并很少,出现了一篇关于来至51cto的关于存储过程的优缺点的文章,具体这里也不指出了。看见文章中对存储过程的几个辩解,我的不敢苟同,我的已经很仔细的看了文章的时间是2011年,若是在更前写年成的话,我的以为彻底可以理解。因此有了这篇,存储过程的一些传言。

1:存储过程只在创造时进行编译,之后每次执行存储过程都不需再从新编译,而通常SQL 语句每执行一次就编译一次,因此使用存储过程可提升数据库执行速度。

在sql server 2000版本,这个观点没错,倒是如此。可是在sql server2005文档中很清晰的写到 sql server2005的执行任何sql,关系引擎会首先查看缓存,判断是有有其执行计划。若是有,则将会重用该执行计划,以减小从新编译sql语句生成执行计划的影响。包括Oracle也是这么作的,因此在咱们常见的数据库中不存在这所谓的问题。

2:当对数据库进行复杂操做时(如对多个表进行Update,Insert,Query,Delete 时),可将此复杂操做用存储过程封装起来与数据库提供的事务处理结合一块儿使用。这些操做,若是用程序来完成,就变成了一条条的SQL语句,可能要屡次链接数据库。而换成存储,只须要链接一次数据库就能够了。

这个问题在前面的架构设计-数据访问层简述中说过,DBA老是告诉咱们减小数据库链接次数,这是彻底无争议的,我表述很赞同。可是必定是存储过程的优点?或者说除了存储过程就没其余方式?在架构设计-数据访问层简述中介绍了来自Martin Fowler《P of EAA》的UOW(工做单元)模式,定义为工做单元记录在业务事务过程当中对数据库有影响的全部变化。操做结束后,做为一种结果,工做单元了解全部须要对数据库作的改变。其主旨是在内存中创建一个和数据仓库,维护变化的对象,业务对象变化跟踪,在业务操做完成一次性提交到数据存储介质,提供业务事务。这模式已经在咱们常见的ORM(EntityFramework,Nhibernate等)中很好的支持了,或许这么说这也是ORM框架的一个重要特征。在好比微软的DataSet也支持,批量更新。

3:存储过程能够重复使用,可减小数据库开发人员的工做量。

在项目中咱们糜烂的重复代码仅仅在于数据层?更多或许在于业务逻辑的处理,复杂的条件判断,数据操做的组合。存储过程是由开发人员开发,仍是数据库开发人员?每一个公司的数据库开发人员就仅仅那几个吧,我见过的公司。存储过程是否包含业务规则?若是有的话,业务的不停变化,会不会不停的修改关系模型,修改存储过程,sql的编写和调试虽然如今工具备必定的支持,可是我以为没有开发语言这么智能方便吧,至少我还没看见。若是没有至少简单的查询语句,那和普通的sql有什么差异?减小开发量为何不选择ORM之类的动态sql,采用彻底的对象模型开发,只在少部分ORM失效的业务返璞归真。

4:若是把全部的数据逻辑都放在存储过程当中,那么asp.net只须要负责界面的显示功能,出错的可能性最大就是在存储过程。通常状况下就是这样。升级、维护方便。

这句话更离谱。逻辑放在存储过程,便于维护,我也进入过这样的公司参与过这样的项目,因为刚开始新员工,不能全盘否认,我看见的是恼人的存储过程,恼人是sql,没看过那个开发人员喜欢sql,特别在每次项目需求变动的时候。后来慢慢接受ddd模式,把业务从sql中挣脱出来。asp.net只须要负责界面的显示功能,逻辑层次未免太简单了,我猜想这应是 事务性脚本开发模式,其优劣点在架构设计-业务逻辑层简述中说过,只能实用于简单小型的项目。在加上可移植性差,若是你的客户须要数据库的升级,sql server到Oracle会怎么样。

5:安全性高,可设定只有某此用户才具备对指定存储过程的使用权。

安全对于项目来讲不只仅在于数据库,而应是分布于咱们系统各处。安全关注点应该从表现层到数据库各层之间都应该有处理。通常比较灵活有效基于角色(域)安全和数据库安全,物理服务器安全共同使用,这和不适用存储过程,使用sql并没什么冲突。虽然你可能说存储过程能够做为数据库内部资源实施安全策越。

6:还有些:存储过程能够防止sql注入

这个是固然的,毫无争议。由于用的是参数化方式,你不能随意拼接字符串,参数化方式可以帮助咱们防止大多数的sql注入。在ado.net中为咱们提供了很好的参数化支持,使用sql咱们一样能够作到,再加上一切开源的安全组件的过滤。

最后存储过程并非万恶的,他有他的应用场景,对于复杂逻辑如报表的场景,我会绝不犹豫的放弃ORM,选择它,由于orm不能知足这种复杂查询,可是准确的说我选择的是大量的T-SQL或者是P-SQL,存储过程就是一堆sql子程序的固定格式。我以为能够彻底采用ibatis.net方式的xml配置,更爽些。选择存储过程是因为复杂查询业务,我相信你们也不会为了一些复杂的统计把全表数据加载到内存吧。存储过程开发技术流行与2005前数据为中心的开发模式,在如今的模式,工具,技术下显得有些苍老,但并非一无可取。你也能够彻底采用基于存储过程的开发模式开发出很好的系统。

10—表现层模式-MVC

在前面简述了从服务层到数据层。剩下了表现层,一个再好的中间层表现也必须有一个用户界面,提供和用户交互,将用户行为输入转化为系统操做,进入后台逻辑。在当下RAD(快速应用开发)工具的支持下,咱们能够比较快速的完成UI设计,RAD追求所见即所得的快速反馈,快速应用。表现层也有必定其固定的逻辑(格式化,数据绑定,转化等等,称为UI逻辑)和界面展示。这里UI逻辑指的是全部用来处理数据显示在UI界面的逻辑和,将UI用户输入行为转化为中间层指令的逻辑,负责UI和中间层数据流和行为的转化。不少时候UI是最容易变化的以及最不易测试的逻辑(我一直相信,1:一段好的代码必定要易于测试。2:重构的前提也必须有足够的测试保证,才能让咱们的重构更有节奏更自信),而很大部分UI逻辑却每每比较稳定的。这Matin Fowler提出的分离表现层模式。表现层模式主要分为3种大类:MVC,MVP,PM(微软在sl和wpf起名为MVVM),这3类模式下延伸了不少变异体mvc在web的 model2(asp.net mvc,主要特征基于web特有uri路由)。mvp的变种:Passive View(被动视图)和Supervising Controller(不清楚怎么翻译比较好),PM延伸MVVM。其目的都在于将多变的View和UI逻辑分离。

今天要说的是MVC(model-view-controller:模型-视图-控制器)。在咱们一开始就说结构和编程或者面向对象原则都是为了实现模块的高内聚低耦合,而高内聚低耦合行之有效的方式就是分离关注点(SOC)。为了实现表现UI和表现逻辑的分离,使得他们之间更灵活,而且自治视图(包含全部表现层代码的类)。在30年前Trygve Reenskaug提出的MVC模式,或者更确切的说模范。其将表现层分为3类:model:是视图展示数据,view用户交互界面,Controller:将用户输入转化为中间层操做。

模型(Model):在MVC中模型保持着一个应用程序的状态,和相应视图中来自用户交互的状态变化。在上图中咱们能够看到model会接受来之控制器的状态变化响应和视图的显示状态查询view渲染的数据来源。同时model还有经过事件机制(观察者模式)通知view状态的改变要求view渲染响应。view和模型之间存在必定的耦合,view必须了解model,这也是MVP模式出现缘由之一。正在这里的模型model能够是来之分布式soap或者resetfull的dto(数据传输对象),也能够直接是咱们的领域对象(do)或者数据层返回的数据集,并不严格的 要求。

控制器(Controller):控制器是又view触发,响应用户界面的交互,并根据表现层逻辑改变model状态,以及中间层的交互,最终修改model,Controller不会关心视图的渲染,是经过修改model,model的事件通知机制,触发view的刷新渲染。控制器和模型的交互式一种“发出即忘”,或者分布式OneWay的调用。控制器Controller不会主动了了解view和view交互,除了惟一的视图选择外,控制器须要选择下一次显示的视图view是什么。通常控制器会请求全局应用程序路由下一个须要显示的view,在使其呈现出来。

视图(View):视图时表现层模式出现的缘由,由于他的多样性和变化的频繁性,不易测试(太多外界环境依赖),因此理想的视图应该尽量的哑,被动,视图只负责渲染呈现给用户交互。视图由一些列GUI组件组成,响应用户行为触发控制器逻辑,修改model状态使其保持view同步。视图并须要相应model的变化被动的接受model状态变化刷新相应给用户。

MVC最早兴起于桌面,但没有流行起来,知道在web兴起后,其变异体Model2在Web中流行起来,.net 下的ASP.NTE MVC。Model2中,未来自客户端(浏览器)的请求,被服务端拦截器(asp.net中HttpModule)根据url格式请求方式等转发到固定的控制器,调用固定的Action,在Action中队模型状态进行修改,并选择view,view并根据控制器传来的最新model生产html,css,js前段代码,并输出到前段渲染。

在Model2中和原始MVC最大的差异在于:

1:视图view和模型model之间没有直接依赖,model并不知道view也不须要事件通知view,view也不需知道model,view操做的都是ViewModel(asp.net mvc 中ViewData容器)。2:控制器显示传入视图数据给view,相应用户的操做不是来自view,而是出于服务端应用程序前段的拦截器,捕获url并转发到相应的控制器,已经调用相应的action方法。

在如今说的MVC每每指的就是Web中Model2模式。在Model2中view是被动的,哑的,简单。

相关文章
相关标签/搜索