本节内容html
设计模式(Design Patterns)python
——可复用面向对象软件的基础程序员
设计模式(Design pattern)是一套被反复使用、多数人知晓的、通过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石同样。项目中合理的运用设计模式能够完美的解决不少问题,每种模式在如今中都有相应的原理来与之对应,每个模式描述了一个在咱们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被普遍应用的缘由。算法
经典的《设计模式》一书概括出23种设计模式,这23种模式又可归为,建立型、结构型和行为型3大类spring
前面讲过,社会化的分工愈来愈细,天然在软件设计方面也是如此,所以对象的建立和对象的使用分开也就成为了必然趋势。由于对象的建立会消耗掉系统的不少资源,因此单独对对象的建立进行研究,从而可以高效地建立对象就是建立型模式要探讨的问题。这里有6个具体的建立型模式可供研究,它们分别是:编程
简单工厂模式(Simple Factory);设计模式
工厂方法模式(Factory Method);安全
抽象工厂模式(Abstract Factory);架构
建立者模式(Builder);工具
原型模式(Prototype);
单例模式(Singleton)。
说明:严格来讲,简单工厂模式不是GoF总结出来的23种设计模式之一。
在解决了对象的建立问题以后,对象的组成以及对象之间的依赖关系就成了开发人员关注的焦点,由于如何设计对象的结构、继承和依赖关系会影响到后续程序的维护性、代码的健壮性、耦合性等。对象结构的设计很容易体现出设计人员水平的高低,这里有7个具体的结构型模式可供研究,它们分别是:
外观模式(Facade);
适配器模式(Adapter);
代理模式(Proxy);
装饰模式(Decorator);
桥模式(Bridge);
组合模式(Composite);
享元模式(Flyweight)
在对象的结构和对象的建立问题都解决了以后,就剩下对象的行为问题了,若是对象的行为设计的好,那么对象的行为就会更清晰,它们之间的协做效率就会提升,这里有11个具体的行为型模式可供研究,它们分别是:
模板方法模式(Template Method);
观察者模式(Observer);
状态模式(State);
策略模式(Strategy);
职责链模式(Chain of Responsibility);
命令模式(Command);
访问者模式(Visitor);
调停者模式(Mediator);
备忘录模式(Memento);
迭代器模式(Iterator);
解释器模式(Interpreter)。
一、开闭原则(Open Close Principle)
开闭原则就是说对扩展开放,对修改关闭。在程序须要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。因此一句话归纳就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,咱们须要使用接口和抽象类,后面的具体设计中咱们会提到这点。
二、里氏代换原则(Liskov Substitution Principle)
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类能够出现的地方,子类必定能够出现。 LSP是继承复用的基石,只有当衍生类能够替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也可以在基类的基础上增长新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,因此里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科
三、依赖倒转原则(Dependence Inversion Principle)
这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。
四、接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。仍是一个下降类之间的耦合度的意思,从这儿咱们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。因此上文中屡次出现:下降依赖,下降耦合。
五、迪米特法则(最少知道原则)(Demeter Principle)
为何叫最少知道原则,就是说:一个实体应当尽可能少的与其余实体之间发生相互做用,使得系统功能模块相对独立。
六、合成复用原则(Composite Reuse Principle)
原则是尽可能使用合成/聚合的方式,而不是使用继承。
工厂模式(Factory Pattern)是 Java 中最经常使用的设计模式之一。这种类型的设计模式属于建立型模式,它提供了一种建立对象的最佳方式。
在工厂模式中,咱们在建立对象时不会对客户端暴露建立逻辑,而且是经过使用一个共同的接口来指向新建立的对象。
意图:
定义一个用于建立对象的接口,让子类决定实例化哪个类。Factory Method 使一个类的实例化延迟到其子类。
适用性:
当一个类不知道它所必须建立的对象的类的时候。
当一个类但愿由它的子类来指定它所建立的对象的时候。
当类将建立对象的职责委托给多个帮助子类中的某一个,而且你但愿将哪个帮助子类是代理者这一信息局部化的时候。
优势:客户端不须要修改代码。
缺点: 当须要增长新的运算类的时候,不只需新加运算类,还要修改工厂类,违反了开闭原则。
这个和简单工厂有区别,简单工厂模式只有一个工厂,工厂方法模式对每个产品都有相应的工厂
好处:增长一个运算类(例如N次方类),只须要增长运算类和相对应的工厂,两个类,不须要修改工厂类。
缺点:增长运算类,会修改客户端代码,工厂方法只是把简单工厂的内部逻辑判断移到了客户端进行。
每个模式都是针对必定问题的解决方案。抽象工厂模式与工厂方法模式的最大区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则须要面对多个产品等级结构。
在学习抽象工厂具体实例以前,应该明白两个重要的概念:产品族和产品等级。
所谓产品族,是指位于不一样产品等级结构中,功能相关联的产品组成的家族。好比AMD的主板、芯片组、CPU组成一个家族,Intel的主板、芯片组、CPU组成一个家族。而这两个家族都来自于三个产品等级:主板、芯片组、CPU。一个等级结构是由相同的结构的产品组成,示意图以下:
显然,每个产品族中含有产品的数目,与产品等级结构的数目是相等的。产品的等级结构与产品族将产品按照不一样方向划分,造成一个二维的坐标系。横轴表示产品的等级结构,纵轴表示产品族,上图共有两个产品族,分布于三个不一样的产品等级结构中。只要指明一个产品所处的产品族以及它所属的等级结构,就能够惟一的肯定这个产品。
上面所给出的三个不一样的等级结构具备平行的结构。所以,若是采用工厂方法模式,就势必要使用三个独立的工厂等级结构来对付这三个产品等级结构。因为这三个产品等级结构的类似性,会致使三个平行的工厂等级结构。随着产品等级结构的数目的增长,工厂方法模式所给出的工厂等级结构的数目也会随之增长。以下图:
那么,是否可使用同一个工厂等级结构来对付这些相同或者极为类似的产品等级结构呢?固然能够的,并且这就是抽象工厂模式的好处。同一个工厂等级结构负责三个不一样产品等级结构中的产品对象的建立。
能够看出,一个工厂等级结构能够建立出分属于不一样产品等级结构的一个产品族中的全部对象。显然,这时候抽象工厂模式比简单工厂模式、工厂方法模式更有效率。对应于每个产品族都有一个具体工厂。而每个具体工厂负责建立属于同一个产品族,可是分属于不一样等级结构的产品。
抽象工厂模式是对象的建立模式,它是工厂方法模式的进一步推广。
假设一个子系统须要一些产品对象,而这些产品又属于一个以上的产品等级结构。那么为了将消费这些产品对象的责任和建立这些产品对象的责任分割开来,能够引进抽象工厂模式。这样的话,消费产品的一方不须要直接参与产品的建立工做,而只须要向一个公用的工厂接口请求所须要的产品。
经过使用抽象工厂模式,能够处理具备相同(或者类似)等级结构中的多个产品族中的产品对象的建立问题。以下图所示:
因为这两个产品族的等级结构相同,所以使用同一个工厂族也能够处理这两个产品族的建立问题,这就是抽象工厂模式。
根据产品角色的结构图,就不难给出工厂角色的结构设计图。
能够看出,每个工厂角色都有两个工厂方法,分别负责建立分属不一样产品等级结构的产品对象。
抽象工厂的功能是为一系列相关对象或相互依赖的对象建立一个接口。必定要注意,这个接口内的方法不是任意堆砌的,而是一系列相关或相互依赖的方法。好比上面例子中的主板和CPU,都是为了组装一台电脑的相关对象。不一样的装机方案,表明一种具体的电脑系列。
因为抽象工厂定义的一系列对象一般是相关或相互依赖的,这些产品对象就构成了一个产品族,也就是抽象工厂定义了一个产品族。
这就带来很是大的灵活性,切换产品族的时候,只要提供不一样的抽象工厂实现就能够了,也就是说如今是以一个产品族做为一个总体被切换。
1.一个系统不该当依赖于产品类实例如何被建立、组合和表达的细节,这对于全部形态的工厂模式都是重要的。
2.这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
3.同属于同一个产品族的产品是在一块儿使用的,这一约束必须在系统的设计中体现出来。(好比:Intel主板必须使用Intel CPU、Intel芯片组)
4.系统提供一个产品类的库,全部的产品以一样的接口出现,从而使客户端不依赖于实现。
抽象工厂模式的起源或者最先的应用,是用于建立分属于不一样操做系统的视窗构建。好比:命令按键(Button)与文字框(Text)都是视窗构建,在UNIX操做系统的视窗环境和Windows操做系统的视窗环境中,这两个构建有不一样的本地实现,它们的细节有所不一样。
在每个操做系统中,都有一个视窗构建组成的构建家族。在这里就是Button和Text组成的产品族。而每个视窗构件都构成本身的等级结构,由一个抽象角色给出抽象的功能描述,而由具体子类给出不一样操做系统下的具体实现。
客户端使用抽象工厂来建立须要的对象,而客户端根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已。也就是说,客户端从具体的产品实现中解耦。
由于一个具体的工厂实现表明的是一个产品族,好比上面例子的从Intel系列到AMD系列只须要切换一下具体工厂。
若是须要给整个产品族添加一个新的产品,那么就须要修改抽象工厂,这样就会致使修改全部的工厂实现类。
意图:
将一个复杂对象的构建与它的表示分离,使得一样的构建过程能够建立不一样的表示。
适用性:
当建立复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
当构造过程必须容许被构造的对象有不一样的表示时。
意图:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
适用性:
当类只能有一个实例并且客户能够从一个众所周知的访问点访问它时。
当这个惟一实例应该是经过子类化可扩展的,而且客户应该无需更改代码就能使用一个扩展的实例时。
意图
将一个类的接口转换成客户但愿的另一个接口。Adapter 模式使得本来因为接口不兼容而不能一块儿工做的那些类能够一块儿工做。
适用性:你想使用一个已经存在的类,而它的接口不符合你的需求。
你想建立一个能够复用的类,该类能够与其余不相关的类或不可预见的类(即那些接口可能不必定兼容的类)协同工做
参考:http://www.cnblogs.com/houleixx/archive/2008/02/23/1078877.html
生活中的一个例子:
就拿汽车在路上行驶的来讲。即有小汽车又有公共汽车,它们都不但能在市区中的公路上行驶,也能在高速公路上行驶。这你会发现,对于交通工具(汽车)有不一样的类型,然而它们所行驶的环境(路)也在变化,在软件系统中就要适应两个方面的变化?怎样实现才能应对这种变化呢?
概述:
在软件系统中,某些类型因为自身的逻辑,它具备两个或多个维度的变化,那么如何应对这种“多维度的变化”?如何利用面向对象的技术来使得该类型可以轻松的沿着多个方向进行变化,而又不引入额外的复杂度?这就要使用Bridge模式。
意图:
将抽象部分与实现部分分离,使它们均可以独立的变化。
——《设计模式》GOF
效果及实现要点:
1.Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现能够沿着各自的维度来变化。
2.所谓抽象和实现沿着各自维度的变化,即“子类化”它们,获得各个子类以后,即可以任意它们,从而得到不一样路上的不一样汽车。
3.Bridge模式有时候相似于多继承方案,可是多继承方案每每违背了类的单一职责原则(即一个类只有一个变化的缘由),复用性比较差。Bridge模式是比多继承方案更好的解决方法。
4.Bridge模式的应用通常在“两个很是强的变化维度”,有时候即便有两个变化的维度,可是某个方向的变化维度并不剧烈——换言之两个变化不会致使纵横交错的结果,并不必定要使用Bridge模式。
适用性:
在如下的状况下应当使用桥梁模式:
1.若是一个系统须要在构件的抽象化角色和具体化角色之间增长更多的灵活性,避免在两个层次之间创建静态的联系。
2.设计要求实现化角色的任何改变不该当影响客户端,或者说实现化角色的改变对客户端是彻底透明的。
3.一个构件有多于一个的抽象化角色和实现化角色,系统须要它们之间进行动态耦合。
4.虽然在系统中使用继承是没有问题的,可是因为抽象化角色和具体化角色须要独立变化,设计要求须要独立管理这二者。
总结:
Bridge模式是一个很是有用的模式,也很是复杂,它很好的符合了开放-封闭原则和优先使用对象,而不是继承这两个面向对象原则
应用设计模式:
桥接模式(Bridge)来作(多维度变化);
结合上面的例子,增长一个维度"人",不一样的人开着不一样的汽车在不一样的路上行驶(三个维度);
结合上面增长一个类"人",并从新调用.
代码实现:
意图:
将对象组合成树形结构以表示“部分-总体”的层次结构。C o m p o s i t e 使得用户对单个对象和组合对象的使用具备一致性。
适用性:你想表示对象的部分-总体层次结构。
你但愿用户忽略组合对象与单个对象的不一样,用户将统一地使用组合结构中的全部对象。
意图:
为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
适用性:
当你要为一个复杂子系统提供一个简单接口时。子系统每每由于不断演化而变得愈来愈复杂。大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不须要定制子系统的用户带来一些使用上的困难。Facade 能够提供一个简单的缺省视图,这一视图对大多数用户来讲已经足够,而那些须要更多的可定制性的用户能够越过facade层。
客户程序与抽象类的实现部分之间存在着很大的依赖性。引入facade 将这个子系统与客户以及其余的子系统分离,能够提升子系统的独立性和可移植性。
当你须要构建一个层次结构的子系统时,使用facade模式定义子系统中每层的入口点。若是子系统之间是相互依赖的,你可让它们仅经过facade进行通信,从而简化了它们之间的依赖关系。
意图:
运用共享技术有效地支持大量细粒度的对象。
适用性:
一个应用程序使用了大量的对象。
彻底因为使用大量的对象,形成很大的存储开销。
对象的大多数状态均可变为外部状态。
若是删除对象的外部状态,那么能够用相对较少的共享对象取代不少组对象。
应用程序不依赖于对象标识。因为Flyweight 对象能够被共享,对于概念上明显有别的对象,标识测试将返回真值。
意图:为其余对象提供一种代理以控制对这个对象的访问。
主要解决:在直接访问对象时带来的问题,好比说:要访问的对象在远程的机器上。在面向对象系统中,有些对象因为某些缘由(好比对象建立开销很大,或者某些操做须要安全控制,或者须要进程外的访问),直接访问会给使用者或者系统结构带来不少麻烦,咱们能够在访问此对象时加上一个对此对象的访问层。
什么时候使用:想在访问一个类时作一些控制。
如何解决:增长中间层。
关键代码:实现与被代理类组合。
应用实例: 一、Windows 里面的快捷方式。 二、猪八戒去找高翠兰结果是孙悟空变的,能够这样理解:把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒访问高翠兰的时候看不出来这个是孙悟空,因此说孙悟空是高翠兰代理类。 三、买火车票不必定在火车站买,也能够去代售点。 四、一张支票或银行存单是帐户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人帐号上资金的控制。 五、spring aop。
优势: 一、职责清晰。 二、高扩展性。 三、智能化。
缺点: 一、因为在客户端和真实主题之间增长了代理对象,所以有些类型的代理模式可能会形成请求的处理速度变慢。 二、实现代理模式须要额外的工做,有些代理模式的实现很是复杂。
使用场景:按职责来划分,一般有如下使用场景: 一、远程代理。 二、虚拟代理。 三、Copy-on-Write 代理。 四、保护(Protect or Access)代理。 五、Cache代理。 六、防火墙(Firewall)代理。 七、同步化(Synchronization)代理。 八、智能引用(Smart Reference)代理。
在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类能够按须要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。
意图:定义一个操做中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类能够不改变一个算法的结构便可重定义该算法的某些特定步骤。
主要解决:一些方法通用,却在每个子类都从新写了这一方法。
什么时候使用:有一些通用的方法。
如何解决:将这些通用算法抽象出来。
关键代码:在抽象类实现,其余步骤在子类实现。
应用实例: 一、在造房子的时候,地基、走线、水管都同样,只有在建筑的后期才有加壁橱加栅栏等差别。 二、西游记里面菩萨定好的 81 难,这就是一个顶层的逻辑骨架。 三、Spirng 中对 Hibernate 的支持,将一些已经定好的方法封装起来,好比开启事务、获取 Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就能够保存。
优势: 一、封装不变部分,扩展可变部分。 二、提取公共代码,便于维护。 三、行为由父类控制,子类实现。
缺点:每个不一样的实现都须要一个子类来实现,致使类的个数增长,使得系统更加庞大。
使用场景: 一、有多个子类共有的方法,且逻辑相同。 二、重要的、复杂的方法,能够考虑做为模板方法。
意图:
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
适用性:
有多个的对象能够处理一个请求,哪一个对象处理该请求运行时刻自动肯定。
你想在不明确指定接收者的状况下,向多个对象中的一个提交一个请求。
可处理一个请求的对象集合应被动态指定。
意图:
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 全部依赖于它的对象都获得通知并被自动更新。
适用性:
当一个抽象模型有两个方面, 其中一个方面依赖于另外一方面。将这两者封装在独立的对象中以使它们能够各自独立地改变和复用。
当对一个对象的改变须要同时改变其它对象, 而不知道具体有多少对象有待改变。
当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不但愿这些对象是紧密耦合的。
意图:定义一系列的算法,把它们一个个封装起来, 而且使它们可相互替换。
主要解决:在有多种算法类似的状况下,使用 if...else 所带来的复杂和难以维护。
什么时候使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
如何解决:将这些算法封装成一个一个的类,任意地替换。
关键代码:实现同一个接口。
应用实例: 一、诸葛亮的锦囊妙计,每个锦囊就是一个策略。 二、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。 三、JAVA AWT 中的 LayoutManager。
优势: 一、算法能够自由切换。 二、避免使用多重条件判断。 三、扩展性良好。
缺点: 一、策略类会增多。 二、全部策略类都须要对外暴露。
使用场景: 一、若是在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式能够动态地让一个对象在许多行为中选择一种行为。 二、一个系统须要动态地在几种算法中选择一种。 三、若是一个对象有不少的行为,若是不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
#_*_coding:utf-8_*_
__author__
=
'Alex Li'
class
TravelStrategy(
object
):
'''
出行策略
'''
def
travelAlgorithm(
self
):
pass
class
AirplaneStrategy(TravelStrategy):
def
travelAlgorithm(
self
):
print
(
"坐飞机出行...."
)
class
TrainStrategy(TravelStrategy):
def
travelAlgorithm(
self
):
print
(
"坐高铁出行...."
)
class
CarStrategy(TravelStrategy):
def
travelAlgorithm(
self
):
print
(
"自驾出行...."
)
class
BicycleStrategy(TravelStrategy):
def
travelAlgorithm(
self
):
print
(
"骑车出行...."
)
class
TravelInterface(
object
):
def
__init__(
self
,travel_strategy):
self
.travel_strategy
=
travel_strategy
def
set_strategy(
self
,travel_strategy):
self
.travel_strategy
=
travel_strategy
def
travel(
self
):
return
self
.travel_strategy.travelAlgorithm()
#坐飞机
travel
=
TravelInterface(AirplaneStrategy())
travel.travel()
#改开车
travel.set_strategy(TrainStrategy())
travel.travel()
|