设计模式四人组
GoF(“四人帮”,又称Gang of Four,即Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides四人)
GoF的《Design Patterns: Elements of Reusable Object-Oriented Software》(1995年出版,出版社:Addison Wesly Longman.Inc),第一次将设计模式提升到理论高度,并将之规范化。该书提出了23种基本设计模式。时至今日,在可复用面向对象软件的发展过程中,新的设计模式仍然不断出现。
wikipedia维基百科 - Design_Patterns设计模式
为什么要提倡“Design Pattern呢?
根本原因是为了代码复用(Reusable),增加可维护性。
那么怎么才能实现代码复用呢?
首先,面向对象有几个基本原则:
开闭原则(Open Closed Principle,OCP)
里氏代换原则(Liskov Substitution Principle,LSP)
依赖倒转原则(Dependency Inversion Principle,DIP)
接口隔离原则(Interface Segregation Principle,ISP)
合成/聚合复用原则(Composite/Aggregate Reuse Principle,CARP)
最小知识原则(Principle of Least Knowledge,PLK,也叫迪米特法则)
其中,开闭原则具有理想主义的色彩,它是面向对象设计的终极目标。其他几条,则可以看做是开闭原则的实现方法。
设计模式就是实现了这些原则,从而达到了代码复用、增加可维护性的目的。
此原则是由Bertrand Meyer提出的,
原文是:“Software entities should be open for extension, but closed for modification”
开放括展,拒绝修改
本原则是面向对象设计中“可复用设计”的基石,是最重要的原则,其它原则是实现开闭原则的手段
具体到项目中来说:就是对组件功能的扩展开放,而对修改原有代码的关闭。模块应尽量在不修改原(是“原”,指原来的代码)代码的情况下进行扩展。
举一个其工厂模式(factory pattern)实现例子
假设中关村有一个卖毛片盘和盗版盘(PirateDisc)的小贩(Vendor),我们给他设计一个“光盘销售管理软件”。
我们应该先设计一“光盘(Disc)”接口。而毛片盘和盗版盘是其子类。小贩通过“DiscFactory”来管理这些光盘。代码为:
public class DiscFactory{
public static Disc getDisc(String name){
return (Disc)Class.forName(name).newInstance();
}
}
有人要买盗版盘,怎么实现呢?
public class Vendor{
public static void main(String[] args){
Disc d = DiscFactory.getDisc("PirateDisc");
d.sell();
}
}
如果有一天,这小子良心发现了,开始卖正版盘(GenuineDisc)怎么办?
没关系,我们只要再创建一个“光盘”的子类“正版盘(GenuineDisc)”就可以了,不需要修改原结构和代码
以上
怎么样?对扩展开放,对修改关闭——“开闭原则”
工厂模式是对具体产品进行扩展,有的项目可能需要更多的扩展性,要对这个“工厂”也进行扩展,那就成了“抽象工厂模式”
OCP具有理想主义色彩,它是面向对象设计的终极目标,但因为其要求在开发前就将所有可预见情况按照原则设计是不可能的。所以,针对开闭原则的实现方法,一直都有大师费尽心机去实现,后面的原则都可以看做其实现。
里氏代换原则是由Barbara Liskov提出的,就是说是Liskov**重点内容**提出的置换原则
如果一个面向对象设计是遵守本原则的,那么有:任何基类可以出现的地方,子类一定可以出现
一句话:这个原则很严格,再用一句话说就是:“子类必须能够替换基类,否则不应当设计其为子类”
如果调用的是父类的话,那么换成子类也完全可以运行。比如:
Disc d = new PirateDisc();
d.sell();
此时,可以将“盗版盘”类改为“毛片”类吗?
没问题,完全可以运行。Java编译器会检查程序是否符合里氏代换原则。
还记得java继承的一个原则吗?
子类override的方法的访问权限不能小于父类对应方法的访问权限
现在知道里氏代换原则就明白了:如果子类权限小的话就不能保证代换原则了
比如“Disc”中的方法“sell()”访问权限是“public”,那么子类“PirateDisc”和“毛片”中的“sell()”方法就不能是protected或private,编译不能通过
为什么要这样呢?你想啊:如果“盗版盘”的“卖”方法是private。那么上面的这段代码就不能执行了:
Disc d = new PirateDisc();
d.sell();
可以说:里氏代换原则是继承复用的一个基础。
在传统结构化编程中,最上层的模块通常都要依赖下面的子模块来实现,那么依赖倒置就是说:逆转这种依赖,高层不应该依赖于低层。
高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。抽象不应该依赖于具体,具体应当依赖于抽象
一句话:设计要依赖于抽象而不是具体。不要一上来就设计具体的类,要抽象的想。
如果没有任何继承关系存在,就会导致具体业务代码(高层)依赖于底层具体模块。此时,只要具体模块要增删改变,高层也要改变。
应该:
传递参数,或者在组合聚合关系中,尽量引用层次高(祖先)的类。
主要是在构造对象时可以动态的创建各种具体对象,当然如果一些具体类比较稳定,就不必在弄一个抽象类做它的父类,这样有画蛇添足的感觉。
要使用多个专门的接口。每一个接口对应且仅对应一种角色。
如果不是接口隔离,那么就会出现这种情况:
使用此接口的客户会面临由于这些不使用的方法的改变所带来的改变。(此处的接口要理解为调用一个WebService接口就能明白)
合成/聚合复用原则(Composite/Aggregate Reuse Principle,CARP)经常又叫做合成复用原则。
合成/聚合复用原则就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用已有功能的目的。就是说要少用继承,多用合成关系来实现。
一句话:设计者首先应当考虑合成/聚合,而不是继承。实践中合成/聚合会带来比继承更大的利益。
有几个类要与数据库打交道,就写了一个数据库操作的类,然后别的跟数据库打交道的类都继承这个。结果后来,我修改了数据库操作类的一个方法,各个类都需要改动。“牵一发而动全身”!面向对象是要把波动限制在尽量小的范围。
在Java中,应尽量针对Interface编程,而非实现类。这样,更换子类不会影响调用它方法的代码。要让各个类尽可能少的跟别人联系,“不要与陌生人说话”。扩展性和维护性才能提高。
也叫迪米特法则。不要和陌生人说话,即一个对象应对其他对象有尽可能少的了解。这是一个如何低耦合(Loosely-Coupled)的法则。
一句话:一个对象应当尽可能少的去了解其他对象。
设计模式就是应用了上面所说的设计原则来设计的,有很多种设计模式,但出名的有4人帮设计的23种,下面介绍一下他们的设计:
创建型模式:
结构型模式:
行为型模式:
设计模式可谓招数,如果先学通了各种模式,又忘掉了所有模式而随心所欲,可谓OO(Object-Oriented)之最高境界
设计模式使人们可以更加简单方便地复用成功的设计和体系结构。将已证实的技术表述成设计模式也会使新系统开发者更加容易理解其设计思路
说完了设计模式以及其设计原则,接下来我会将基本的23种设计模式都用博文介绍一遍,敬请期待