Java设计模式
来自:http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html
一、设计模式的分类
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
其实还有两类:并发型模式和线程池模式。
二、设计模式的六大原则
1、开闭原则(Open Close Principle)
开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类。
2、里氏代换原则(Liskov Substitution Principle)
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
3、依赖倒转原则(Dependence Inversion Principle)
这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface Segregation Principle)
使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
5、迪米特法则(最少知道原则)(Demeter Principle)
一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则(Composite Reuse Principle)
原则是尽量使用合成/聚合的方式,而不是使用继承。
三、Java的23中设计模式
从这一块开始,我们详细介绍Java中23种设计模式的概念,应用场景等情况,并结合他们的特点及设计模式的原则进行分析。
1、工厂方法模式(Factory Method)
工厂方法模式分为三种:
1.1、普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。
1.2、多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。
1.3、静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。
2、抽象工厂模式(Abstract Factory)
工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了开闭原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,而不是对原有的工厂类进行修改。
3、单例模式(Singleton)
单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处:
1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。
3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。
实例的创建有两种方式:一、创建成员变量的时候new一个实例。二、在getInstance()的函数里面new一个实例。第一种方法存在启动比较慢,而第二种存在创建实例同步的问题,需要加同步锁。
4、建造者模式(Builder)
建造者模式则是将各种产品集中起来进行管理,就是创建多个产品,并将产品放在容器(如List、Map、Vector等)中,而工厂类只负责创建产品而交由外部的类进行管理调用。
5、原型模式(Prototype)
原型模式虽然是创建型的模式,但是与工程模式没有关系。由于创建对象需要大量的资源和时间,于是可以使用原型模式,该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。
5.1、适用性
当一个系统应该独立于他的产品创建,构成和表示时。
当要实例化的类是在运行时刻指定的,或者为了避免创建一个与产品类层次平行的工厂类层次时。
当一个类的实例只能有几个不同状态组合的一种时。建立相应数目的原型并克隆比每次用合适的状态手工实例化该类更方便。
5.2、浅度克隆
只负责克隆按值传递的数据(比如基本数据类型),而不复制它所引用的对象,换言之,所有的对其他对象的引用都仍然指向原来的对象。
Java语言提供的Cloneable接口只起一个作用,就是在运行时期通知Java虚拟机可以安全地在这个类上使用clone()方法。通过调用这个clone()方法可以得到一个对象的复制。由于Object类本身并不实现Cloneable接口,因此如果所考虑的类没有实现Cloneable接口时,调用clone()方法会抛出CloneNotSupportedException异常。
clone()方法将对象复制了一份并返还给调用者。所谓“复制”的含义与clone()方法是怎么实现的。一般而言,clone()方法满足以下的描述:
(1)对任何的对象x,都有:x.clone()!=x。换言之,克隆对象与原对象不是同一个对象。
(2)对任何的对象x,都有:x.clone().getClass() == x.getClass(),换言之,克隆对象与原对象的类型一样。
(3)如果对象x的equals()方法定义其恰当的话,那么x.clone().equals(x)应当成立的。
在JAVA语言的API中,凡是提供了clone()方法的类,都满足上面的这些条件。JAVA语言的设计师在设计自己的clone()方法时,也应当遵守着三个条件。一般来说,上面的三个条件中的前两个是必需的,而第三个是可选的。
public class Prototype implements Cloneable {
public Object clone() throws CloneNotSupportedException {
Prototype proto = (Prototype) super.clone();
return proto;
}
}
5.3、深度克隆
把对象写到流里的过程是序列化(Serialization)过程;而把对象从流中读出来的过程则叫反序列化(Deserialization)过程。应当指出的是,写到流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。在Java语言里深度克隆一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的拷贝)写到一个流里(序列化),再从流里读回来(反序列化),便可以重建对象。这样做的前提就是对象以及对象内部所有引用到的对象都是可序列化的,否则,就需要仔细考察那些不可序列化的对象可否设成transient,从而将之排除在复制过程之外。
1 public Object deepClone() throws IOException, ClassNotFoundException{
2 //将对象写到流里
3 ByteArrayOutputStream bos = new ByteArrayOutputStream();
4 ObjectOutputStream oos = new ObjectOutputStream(bos);
5 oos.writeObject(this);
6 //从流里读回来
7 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
8 ObjectInputStream ois = new ObjectInputStream(bis);
9 return ois.readObject();
10 }
6、适配器模式(Adapter)
适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式.
6.1、类的适配器模式:有一个源类,拥有一个待适配的方法,有一个目标接口,有与源类待适配的方法相同名称。通过Adapter类继承源类和实现目标接口,将源类的功能扩展到目标接口里。
6.2、对象的适配器模式:实现方式,适配器持有源类型的引用,并继承目标类型的接口。
6.3、接口的适配器模式:一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,所以写一个继承接口的抽象类,然后实现抽象类重写需要的方法就行。
6.4、总结——三种适配器适用的场景
类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。
对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。
接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。
7、装饰器模式(Decorator)
装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例,
装饰器模式的应用场景:
1、需要扩展一个类的功能。
2、动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)
缺点:产生过多相似的对象,不易排错!
8、代理模式(Proxy)
代理模式就是多一个代理类出来,替原对象进行一些操作,也就是说我们不需要对被代理的类进行操作,而是操作代理类就够了。代理类持有被代理类的引用,在执行被代理类某些功能的前后增加一些处理。为了是代码有更好的扩展性,代理类和被代理类实现同一个核心接口(含有目标功能的函数接口)。
代理模式的应用场景:
如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。
使用代理模式,可以将功能划分的更加清晰,有助于后期维护!
9、外观模式(Facade)
外观模式是为了解决类与类之间的依赖关系的,像spring一样,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度,该模式中没有涉及到接口。
Facade对象持有功能类的引用,Facade对象协调功能类之间依赖、执行等关系。
10、桥接模式(Bridge)
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。