Java开发中的二十三种设计模式简介
2017-05-10 17:00
459 查看
原文链接:http://blog.csdn.net/zhangerqing/article/details/8194653
一、设计模式简介
设计模式(Design Pattern)描述了软件开发过程中若干重复出现的问题的解决方案,这些方案不是由过程、算法等底层程序构造实体实现,而是由软件系统中类与类之间或不同类的对象之间的共生关系组成。
设计模式可以帮助软件设计人员学习、重用前人的经验和成果。
设计模式分为三大类共23种:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
其实还有两类:并发型模式和线程池模式
二、设计模式的六大原则
1、开闭原则(Open Close Principle)
开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、里氏代换原则(Liskov Substitution Principle)
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科
3、依赖倒转原则(Dependence Inversion Principle)
这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
5、迪米特法则(最少知道原则)(Demeter Principle)
为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则(Composite Reuse Principle)
原则是尽量使用合成/聚合的方式,而不是使用继承。
三、设计模式详解
设计模式之创建型模式
1、工厂方法模式(Factory Method)
是一些动态创建对象的方法。是在抽象工厂中声明的一组虚函数,它们负责组合对象中成员对象的创建,其实现代码在实现工厂中定义。
工厂方法模式分为三种:
(1)普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。
接口
实现类
工厂类
测试类
(2)多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。
针对普通工厂方法模式的示例中的代码改进如下
工厂类
测试类
(3)静态工厂方法模式,将上面的多个工厂方法模式里的工厂类里的方法置为静态的,测试类就不需要创建实例,直接调用即可。
工厂类
测试类
总体来说,工厂模式适合:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。在以上的三种模式中,第一种如果传入的字符串有误,不能正确创建对象,第三种相对于第二种,不需要实例化工厂类,所以,大多数情况下,我们会选用第三种——静态工厂方法模式。
2、抽象工厂模式(Abstract Factory)
工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则。抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。
具体做法就是把之前的工厂类改写成一个通用的工厂类接口,然后分别实现多个工厂类。
工厂类接口
工厂类
测试类
3、单例模式(Singleton)
java中单例模式是一种常见的设计模式,在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在,系统中只有一个实例,因此构造方法应该为私有,单例模式的写法有好几种,这里主要介绍两种:懒汉式单例、饿汉式单例。
(1)懒汉式单例
以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance()这个方法进行改造,保证了懒汉式单例的线程安全。
1)在getInstance()方法上加同步
synchronized关键字锁住的是这个对象,这样的用法,在性能上会有所下降,因为每次调用getInstance(),都要对对象上锁,事实上,只有在第一次创建对象的时候需要加锁,之后就不需要了。
2)双重检查锁定
将synchronized关键字加在了内部,也就是说当调用的时候是不需要加锁的,只有在 singleton 为null,并创建对象的时候才需要加锁,性能有一定的提升。但是,这样的情况,还是有可能出问题的。看下面的情况:在Java指令中创建对象和赋值操作是分开进行的,也就是说 singleton = new Singleton() 语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给 singleton 成员变量,然后再去初始化这个Singleton实例。如果多个线程的话,可能某个线程就会获取到为null的实例,这样就出错了。
3)静态内部类
这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。
其实说它完美,也不一定,如果在构造函数中抛出异常,实例将永远得不到创建,也会出错。所以说,十分完美的东西是没有的,我们只能根据实际情况,选择最适合自己应用场景的实现方法。
(2)饿汉式单例
饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。
(3)懒汉式和饿汉式的区别
1)线程安全方面:
饿汉式在类加载时直接创建静态实例,天生就是线程安全的,可以直接用于多线程而不会出现问题。
懒汉式第一次需要时才创建一个实例,本身是非线程安全的,为了实现线程安全有几种写法,分别是上面的(1)中的1)、2)、3),这三种实现在资源加载和性能方面有些区别。
2)资源加载和性能:
饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。
而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。
至于1)、2)、3)这三种实现又有些区别:
第1种,在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的。
第2种,在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗。
第3种,利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗,所以这一种是相对最好的。
4、建造者模式(Builder)
工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性。
建造者模式将很多功能集成到一个类里,这个类可以创造出比较复杂的东西。所以与工厂类模式的区别就是:工厂类模式关注的是创建单个产品,而建造者模式则关注创建复合对象的多个部分。因此,是选择工厂类模式还是建造者模式,依实际情况而定。
以下代码只是一个简单的示例,没有使用接口,实际应用一般都会考虑使用接口,增强扩展性。
对象类
建造者类
测试类
5、原型模式(Prototype)
原型模式虽然是创建型的模式,但是与工厂类模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。
简单原型类
编写一个原型类,只需要实现Cloneable接口(只是一个标志接口,表示该类是可复制的,否则会抛出CloneNotSupportedException异常),覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法,而在Object类中,clone()是native的。
复制又分为浅复制和深复制。
浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。
简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。
完整的原型类
测试类
设计模式之结构型模式
对象的适配器模式是各种模式的起源
![](https://oscdn.geek-share.com/Uploads/Images/Content/202002/23/2f5f7185ce13ae63c870aabef95bac05.png)
1、适配器模式(Adapter)
适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。
主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。
(1)类的适配器模式
源类
目标接口
适配器
测试类
这样TargetAble接口的实现类就具有了Source类的功能。
(2)对象的适配器模式
基本思路和类的适配器模式相同,只是将Adapter类作修改,这次不继承Source类,而是持有Source类的实例,以达到解决兼容性的问题。
适配器
测试类
(3)接口的适配器模式
有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显比较浪费时间,因为并不是所有的方法都是我们需要的,有时只需要某一些方法,为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,并实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。
源接口
抽象类
目标类
测试类
三种适配器模式的应用场景:
类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个适配器类,继承原有的类,实现新的接口即可。
对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个适配器类,持有原类的一个实例,在适配器类的方法中,调用实例的方法就行。
接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类作为适配器类,实现所有方法,我们写别的类的时候,继承该抽象类即可。
2、装饰模式(Decorator)
装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
接口
被装饰类
装饰类
测试类
装饰器模式的应用场景:
1、需要扩展一个类的功能。
2、动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)
缺点:产生过多相似的对象,不易排错!
3、代理模式(Proxy)
代理模式就是多一个代理类出来,替原对象进行一些操作。
接口
被代理类
代理类
测试类
代理模式的应用场景:
如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制,这种方法就是代理模式。
使用代理模式,可以将功能划分的更加清晰,有助于后期维护!
4、外观模式(Facade)
外观模式是为了解决类与类之间的依赖关系,将类与类之间的关系放在一个Facade类中,降低了类与类之间的耦合度,该模式中没有涉及到接口。
下面代码使用电脑来进行说明,cpu类,power类,disk类,都集成到computer类中,最后user类来调用。
局部类
整体类
测试类
5、桥接模式(Bridge)
桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化。桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化,像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。
接口
两个实现类
使用抽象类定义一个桥
定义一个可实例化的桥
测试类
这样,就通过对Bridge类的调用,实现了对接口SourceAble的实现类Source1和Source2的调用。这就是桥接模式。
6、组合模式(Composite)
组合模式有时又叫部分-整体模式,在处理类似树形结构的问题时比较方便。
递归组合模式简称组合模式,通过对象递归组合,形成“对象树”,以表示“整体-部分”关系的层次结构。与体现继承性的“类树”结构不同。组合模式提供了一种构造结构复杂的大对象的手段。
组合模式中含有两种类型的对象:基元对象和组合对象。组合模式使基元对象和组合对象具有一致的使用方式。
使用场景:将多个对象组合在一起进行操作,常用于表示树形结构中,例如二叉树,数等。
7、享元模式(Flyweight)
享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。
一提到共享池,我们很容易联想到Java里面的JDBC连接池,想想每个连接的特点,我们不难总结出:适用于作共享的一些个对象,他们有一些共有的属性,就拿数据库连接池来说,url、driverClassName、username、password及dbname,这些属性对于每个连接来说都是一样的,所以就适合用享元模式来处理,建一个工厂类,将上述类似属性作为内部数据,其它的作为外部数据,在方法调用时,当做参数传进来,这样就节省了空间,减少了实例的数量。
通过连接池的管理,实现了数据库连接的共享,不需要每一次都重新创建连接,节省了数据库重新创建的开销,提升了系统的性能!
设计模式之行为型模式
![](https://oscdn.geek-share.com/Uploads/Images/Content/202002/23/c8f346871a28651543aa2e9419e00dbb.jpg)
1、策略模式(strategy)
策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数。
统一接口
抽象类
三个实现类
测试类
策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。
2、模板方法模式(Template Method)
一个抽象类中,有一个主方法,再定义1...n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用。
抽象类
继承类
测试类
3、观察者模式(Observer)
当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系。
接口
两个实现类
接口
抽象实现类
子类
测试类
MySubject类是主对象,Observer1和Observer2是依赖于MySubject的对象,当MySubject变化时,Observer1和Observer2必然变化。AbstractSubject类中定义着需要监控的对象列表,可以对其进行修改:增加或删除被监控对象,且当MySubject变化时,负责通知在列表内存在的对象。
4、迭代子模式(Iterator)
迭代器模式就是顺序访问聚集中的对象。一般来说,集合中非常常见,如果对集合类比较熟悉的话,理解本模式会十分轻松。这句话包含两层意思:一是需要遍历的对象,即聚集对象,二是迭代器对象,用于对聚集对象进行遍历访问。
下面代码模拟了一个集合
集合类接口
集合类
迭代器接口
迭代器
测试类
MyCollection中定义了集合的一些操作,MyIterator中定义了一系列迭代操作,且持有Collection实例。
5、责任链模式(Chain of Responsibility)
有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象决定处理该请求。但是发出者并不清楚到底最终哪个对象会处理该请求,所以,责任链模式可以实现,在隐瞒客户端的情况下,对系统进行动态的调整。
链接上的请求可以是一条链,可以是一个树,还可以是一个环,模式本身不约束这个,需要我们自己去实现,同时,在一个时刻,命令只允许由一个对象传给另一个对象,而不允许传给多个对象。
接口
抽象类
继承、实现类
测试类
6、命令模式(Command)
命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行。这个过程好在,三者相互解耦,任何一方都不用去依赖其他人,只需要做好自己的事儿就行,司令员要的是结果,不会去关注到底士兵是怎么实现的。
命令模式又称事务(Transaction)模式,命令模式用于封装向某个对象的请求。所谓请求,就是应用程序的操作人员通过图形用户界面(GUI)构件如按钮、图标、菜单项等发出的操作命令。命令模式通过在请求调用对象和请求的执行对象之间增加一个Command中间对象,用以消解多对多复杂性。
Invoker是调用者(司令员),Receiver是被调用者(士兵),MyCommand是命令,实现了Command接口,持有接收对象,看实现代码:
测试类
7、备忘录模式(Memento)
主要目的是保存一个对象的某个状态,以便在适当的时候恢复对象,个人觉得叫备份模式更形象些,通俗的讲下:假设有原始类A,A中有各种属性,A可以决定需要备份的属性,备忘录类B是用来存储A的一些内部状态,类C呢,就是一个用来存储备忘录的,且只能存储,不能修改等操作
原始类
备忘录类
仓库类
测试类
Original类是原始类,里面有需要保存的属性value及创建一个备忘录类,用来保存value值。Memento类是备忘录类,Storage类是存储备忘录的类,持有Memento类的实例。
新建原始类时,value被初始化为wang,后经过修改,将value的值置为bo,最后倒数第二行进行恢复状态,结果成功恢复了。其实我觉得这个模式叫“备份-恢复”模式最形象。
8、状态模式(State)
核心思想就是:当对象的状态改变时,同时改变其行为,很好理解!就拿QQ来说,有几种状态,在线、隐身、忙碌等,每个状态对应不同的操作,而且你的好友也能看到你的状态,所以,状态模式就两点:1、可以通过改变状态来获得不同的行为。2、你的好友能同时看到你的变化。
State类是个状态类,Context类可以实现切换,我们来看看代码:
状态类
切换类
测试类
根据这个特性,状态模式在日常开发中用的挺多的,尤其是做网站的时候,我们有时希望根据对象的某一属性,区别开他们的一些功能,比如说简单的权限控制等。
9、访问者模式(Visitor)
访问者模式就是一种分离对象数据结构与行为的方法,通过这种分离,可达到为一个被访问者动态添加新的操作而无需做其它的修改的效果。
访问者类
主题类
测试类
该模式适用场景:如果我们想为一个现有的类增加新功能,不得不考虑几个事情:
1、新功能会不会与现有功能出现兼容性问题?
2、以后会不会再需要添加?
3、如果类不允许修改代码怎么办?
面对这些问题,最好的解决方法就是使用访问者模式,访问者模式适用于数据结构相对稳定的系统,把数据结构和算法解耦。
10、中介者模式(Mediator)
中介者模式也是用来降低类类之间的耦合的,因为如果类类之间有依赖关系的话,不利于功能的拓展和维护,因为只要修改一个对象,其它关联的对象都得进行修改。如果使用中介者模式,只需关心和中介类的关系,具体类类之间的关系及调度交给中介就行,这有点像spring容器的作用。
User类统一接口,User1和User2分别是不同的对象,二者之间有关联,如果不采用中介者模式,则需要二者相互持有引用,这样二者的耦合度很高,为了解耦,引入了Mediator类,提供统一接口,MyMediator为其实现类,里面持有User1和User2的实例,用来实现对User1和User2的控制。这样User1和User2两个对象相互独立,他们只需要保持好和Mediator之间的关系就行,剩下的全由MyMediator类来维护!
中介接口
中介类
user对象类
测试类
11、解释器模式(Interpreter)
一般主要应用在OOP开发中的编译器的开发中,所以适用面比较窄。
Context类是一个上下文环境类,Plus和Minus分别是用来计算的实现,代码如下:
测试类
原文链接:http://blog.csdn.net/zhangerqing/article/details/8194653
一、设计模式简介
设计模式(Design Pattern)描述了软件开发过程中若干重复出现的问题的解决方案,这些方案不是由过程、算法等底层程序构造实体实现,而是由软件系统中类与类之间或不同类的对象之间的共生关系组成。
设计模式可以帮助软件设计人员学习、重用前人的经验和成果。
设计模式分为三大类共23种:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
其实还有两类:并发型模式和线程池模式
二、设计模式的六大原则
1、开闭原则(Open Close Principle)
开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、里氏代换原则(Liskov Substitution Principle)
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科
3、依赖倒转原则(Dependence Inversion Principle)
这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
5、迪米特法则(最少知道原则)(Demeter Principle)
为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则(Composite Reuse Principle)
原则是尽量使用合成/聚合的方式,而不是使用继承。
三、设计模式详解
设计模式之创建型模式
1、工厂方法模式(Factory Method)
是一些动态创建对象的方法。是在抽象工厂中声明的一组虚函数,它们负责组合对象中成员对象的创建,其实现代码在实现工厂中定义。
工厂方法模式分为三种:
(1)普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。
接口
public interface Sender { public void send(); }
实现类
public class MailSender implements Sender { @Override public void send() { System.out.println("this is MailSender!"); } }
public class SmsSender implements Sender { @Override public void send() { System.out.println("this is SmsSender!"); } }
工厂类
public class SenderFactory { public Sender produce(String type){ if ("mail".equals(type)) { return new MailSender(); } else if("sms".equals(type)) { return new SmsSender(); } else { System.out.println("请输入正确的类型!"); return null; } } }
测试类
public class FactoryTest { public static void main(String[] args) { SenderFactory factory = new SenderFactory(); Sender sender = factory.produce("mail"); sender.send(); } }
(2)多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。
针对普通工厂方法模式的示例中的代码改进如下
工厂类
public class SenderFactory { public Sender produceMail(){ return new MailSender(); } public Sender produceSms(){ return new SmsSender(); } }
测试类
public class FactoryTest { public static void main(String[] args) { SenderFactory factory = new SenderFactory(); Sender sender = factory.produceMail(); sender.send(); } }
(3)静态工厂方法模式,将上面的多个工厂方法模式里的工厂类里的方法置为静态的,测试类就不需要创建实例,直接调用即可。
工厂类
public class SenderFactory { public static Sender produceMail(){ return new MailSender(); } public static Sender produceSms(){ return new SmsSender(); } }
测试类
public class FactoryTest { public static void main(String[] args) { Sender sender = SenderFactory.produceMail(); sender.send(); } }
总体来说,工厂模式适合:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。在以上的三种模式中,第一种如果传入的字符串有误,不能正确创建对象,第三种相对于第二种,不需要实例化工厂类,所以,大多数情况下,我们会选用第三种——静态工厂方法模式。
2、抽象工厂模式(Abstract Factory)
工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则。抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。
具体做法就是把之前的工厂类改写成一个通用的工厂类接口,然后分别实现多个工厂类。
工厂类接口
public interface Provider { public Sender produce(); }
工厂类
public class MailSenderFactory implements Provider{ @Override public Sender produce() { return new MailSender(); } }
public class SmsSenderFactory implements Provider{ @Override public Sender produce() { return new SmsSender(); } }
测试类
public class FactoryTest { public static void main(String[] args) { Provider provider = new MailSenderFactory(); Sender sender = provider.produce(); sender.send(); } }
3、单例模式(Singleton)
java中单例模式是一种常见的设计模式,在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在,系统中只有一个实例,因此构造方法应该为私有,单例模式的写法有好几种,这里主要介绍两种:懒汉式单例、饿汉式单例。
(1)懒汉式单例
/** * 懒汉式单例,在第一次调用的时候进行实例化 * @author wangbo */ public class Singleton { //静态实例私有化,防止在外部被引用 private static Singleton singleton = null; //构造方法私有化,防止在外部被实例化 private Singleton(){} //提供公开的静态方法,用来在外部获取实例 public static Singleton getInstance(){ if (singleton == null) { singleton = new Singleton(); } return singleton; } }
以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance()这个方法进行改造,保证了懒汉式单例的线程安全。
1)在getInstance()方法上加同步
synchronized关键字锁住的是这个对象,这样的用法,在性能上会有所下降,因为每次调用getInstance(),都要对对象上锁,事实上,只有在第一次创建对象的时候需要加锁,之后就不需要了。
/** * 懒汉式单例,在第一次调用的时候进行实例化 * @author wangbo */ public class Singleton { //静态实例私有化,防止在外部被引用 private static Singleton singleton = null; //构造方法私有化,防止在外部被实例化 private Singleton(){} //提供公开的静态方法,用来在外部获取实例 public static synchronized Singleton getInstance(){ if (singleton == null) { singleton = new Singleton(); } return singleton; } }
2)双重检查锁定
将synchronized关键字加在了内部,也就是说当调用的时候是不需要加锁的,只有在 singleton 为null,并创建对象的时候才需要加锁,性能有一定的提升。但是,这样的情况,还是有可能出问题的。看下面的情况:在Java指令中创建对象和赋值操作是分开进行的,也就是说 singleton = new Singleton() 语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给 singleton 成员变量,然后再去初始化这个Singleton实例。如果多个线程的话,可能某个线程就会获取到为null的实例,这样就出错了。
/** * 懒汉式单例,在第一次调用的时候进行实例化 * @author wangbo */ public class Singleton { //静态实例私有化,防止在外部被引用 private static Singleton singleton = null; //构造方法私有化,防止在外部被实例化 private Singleton(){} //提供公开的静态方法,用来在外部获取实例 public static Singleton getInstance(){ if (singleton == null) {//第一重检查 synchronized(Singleton.class){//锁定 if (singleton == null) {//第二重检查 singleton = new Singleton(); } } } return singleton; } }
3)静态内部类
这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。
其实说它完美,也不一定,如果在构造函数中抛出异常,实例将永远得不到创建,也会出错。所以说,十分完美的东西是没有的,我们只能根据实际情况,选择最适合自己应用场景的实现方法。
/** * 懒汉式单例,在第一次调用的时候进行实例化 * @author wangbo */ public class Singleton { //构造方法私有化,防止在外部被实例化 private Singleton(){} //使用静态内部类维护单例 private static class SingletonFactory{ private static Singleton singleton = new Singleton(); } //提供公开的静态方法,用来在外部获取实例 public static Singleton getInstance(){ return SingletonFactory.singleton; } }
(2)饿汉式单例
饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。
/** * 饿汉式单例,在类初始化的时候进行实例化 * @author wangbo */ public class Singleton { //使用静态内部类维护单例(直接初始化) private static final Singleton singleton = new Singleton(); //构造方法私有化,防止在外部被实例化 private Singleton(){} //提供公开的静态方法,用来在外部获取实例 public static Singleton getInstance(){ return singleton; } }
(3)懒汉式和饿汉式的区别
1)线程安全方面:
饿汉式在类加载时直接创建静态实例,天生就是线程安全的,可以直接用于多线程而不会出现问题。
懒汉式第一次需要时才创建一个实例,本身是非线程安全的,为了实现线程安全有几种写法,分别是上面的(1)中的1)、2)、3),这三种实现在资源加载和性能方面有些区别。
2)资源加载和性能:
饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。
而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。
至于1)、2)、3)这三种实现又有些区别:
第1种,在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的。
第2种,在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗。
第3种,利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗,所以这一种是相对最好的。
4、建造者模式(Builder)
工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性。
建造者模式将很多功能集成到一个类里,这个类可以创造出比较复杂的东西。所以与工厂类模式的区别就是:工厂类模式关注的是创建单个产品,而建造者模式则关注创建复合对象的多个部分。因此,是选择工厂类模式还是建造者模式,依实际情况而定。
以下代码只是一个简单的示例,没有使用接口,实际应用一般都会考虑使用接口,增强扩展性。
对象类
public class Person { private String head; private String body; private String foot; public String getHead() { return head; } public void setHead(String head) { this.head = head; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } public String getFoot() { return foot; } public void setFoot(String foot) { this.foot = foot; } }
建造者类
public class Builder { Person person; public Builder(){ person = new Person(); } public void buildHead(){ person.setHead("头"); } public void buildBody(){ person.setBody("身体"); } public void buildFoot(){ person.setFoot("脚"); } }
测试类
public class BuilderTest { public static void main(String[] args) { Builder builder = new Builder(); //开始建造对象 builder.buildHead(); builder.buildBody(); builder.buildFoot(); //获取到建造的对象 Person person = builder.person; } }
5、原型模式(Prototype)
原型模式虽然是创建型的模式,但是与工厂类模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。
简单原型类
public class ProtoType implements Cloneable{ public Object clone() throws CloneNotSupportedException{ ProtoType protoType = (ProtoType)super.clone(); return protoType; } }
编写一个原型类,只需要实现Cloneable接口(只是一个标志接口,表示该类是可复制的,否则会抛出CloneNotSupportedException异常),覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法,而在Object类中,clone()是native的。
复制又分为浅复制和深复制。
浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。
简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。
完整的原型类
public class ProtoType implements Cloneable, Serializable{ private static final long serialVersionUID = 1L; public String str; public SerializableObject obj; //浅复制 public Object simpleClone() throws CloneNotSupportedException{ ProtoType protoType = (ProtoType)super.clone(); return protoType; } //深复制 public Object deepClone() throws IOException, ClassNotFoundException{ //写入当前对象的二进制流 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); //读出二进制产生的新对象 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return ois.readObject(); } } class SerializableObject implements Serializable{ private static final long serialVersionUID = 1L; }
测试类
public class Test { public static void main(String[] args) { ProtoType protoType = new ProtoType(); System.out.println(protoType);//com.wb.pd.原型.ProtoType@15db9742 try { ProtoType protoType1 = (ProtoType) protoType.simpleClone(); System.out.println(protoType1);//com.wb.pd.原型.ProtoType@6d06d69c ProtoType protoType2 = (ProtoType) protoType.deepClone(); System.out.println(protoType2);//com.wb.pd.原型.ProtoType@eed1f14 } catch (Exception e) { e.printStackTrace(); } } }
设计模式之结构型模式
对象的适配器模式是各种模式的起源
![](https://oscdn.geek-share.com/Uploads/Images/Content/202002/23/2f5f7185ce13ae63c870aabef95bac05.png)
1、适配器模式(Adapter)
适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。
主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。
(1)类的适配器模式
源类
public class Source { public void method1(){ System.out.println("这是方法1"); } }
目标接口
public interface TargetAble { //与源类中的方法相同 public void method1(); //新类中的方法 public void method2(); }
适配器
public class Adapter extends Source implements TargetAble{ @Override public void method2() { System.out.println("这是方法2"); } }
测试类
public class Test { public static void main(String[] args) { TargetAble targetAble = new Adapter(); targetAble.method1(); targetAble.method2(); } }
这样TargetAble接口的实现类就具有了Source类的功能。
(2)对象的适配器模式
基本思路和类的适配器模式相同,只是将Adapter类作修改,这次不继承Source类,而是持有Source类的实例,以达到解决兼容性的问题。
适配器
public class Adapter implements TargetAble{ private Source source; public Adapter(Source source) { this.source = source; } @Override public void method1() { source.method1(); } @Override public void method2() { System.out.println("这是方法2"); } }
测试类
public class Test { public static void main(String[] args) { Source source = new Source(); TargetAble targetAble = new Adapter(source); targetAble.method1(); targetAble.method2(); } }
(3)接口的适配器模式
有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显比较浪费时间,因为并不是所有的方法都是我们需要的,有时只需要某一些方法,为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,并实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。
源接口
public interface SourceAble { public void method1(); public void method2(); }
抽象类
public abstract class Adapter implements SourceAble{ @Override public void method1() {} @Override public void method2() {} }
目标类
public class Target extends Adapter{ @Override public void method1() { System.out.println("这是方法1"); } }
测试类
public class Test { public static void main(String[] args) { SourceAble sourceAble = new Target(); sourceAble.method1();//这是方法1 sourceAble.method2();//无输出 } }
三种适配器模式的应用场景:
类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个适配器类,继承原有的类,实现新的接口即可。
对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个适配器类,持有原类的一个实例,在适配器类的方法中,调用实例的方法就行。
接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类作为适配器类,实现所有方法,我们写别的类的时候,继承该抽象类即可。
2、装饰模式(Decorator)
装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
接口
public interface SourceAble { public void method(); }
被装饰类
public class Source implements SourceAble { @Override public void method() { System.out.println("这是被装饰者"); } }
装饰类
public class Decorator implements SourceAble { private SourceAble sourceAble; public Decorator(SourceAble sourceAble) { this.sourceAble = sourceAble; } @Override public void method() { System.out.println("前装饰"); sourceAble.method(); System.out.println("后装饰"); } }
测试类
public class Test { public static void main(String[] args) { SourceAble sourceAble1 = new Source(); SourceAble sourceAble2 = new Decorator(sourceAble1); sourceAble2.method(); } }
装饰器模式的应用场景:
1、需要扩展一个类的功能。
2、动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)
缺点:产生过多相似的对象,不易排错!
3、代理模式(Proxy)
代理模式就是多一个代理类出来,替原对象进行一些操作。
接口
public interface SourceAble { public void method(); }
被代理类
public class Source implements SourceAble { @Override public void method() { System.out.println("这是被代理类"); } }
代理类
public class Proxy implements SourceAble { private Source source; public Proxy() { source = new Source(); } @Override public void method() { before(); source.method(); after(); } public void before(){ System.out.println("代理前"); } public void after(){ System.out.println("代理后"); } }
测试类
public class Test { public static void main(String[] args) { SourceAble sourceAble = new Proxy(); sourceAble.method(); } }
代理模式的应用场景:
如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制,这种方法就是代理模式。
使用代理模式,可以将功能划分的更加清晰,有助于后期维护!
4、外观模式(Facade)
外观模式是为了解决类与类之间的依赖关系,将类与类之间的关系放在一个Facade类中,降低了类与类之间的耦合度,该模式中没有涉及到接口。
下面代码使用电脑来进行说明,cpu类,power类,disk类,都集成到computer类中,最后user类来调用。
局部类
public class Power { public void startUp(){ System.out.println("电源启动了"); } public void shutDown(){ System.out.println("电源关闭了"); } }
public class CPU { public void startUp(){ System.out.println("CPU启动了"); } public void shutDown(){ System.out.println("CPU关闭了"); } }
public class Disk { public void startUp(){ System.out.println("硬盘启动了"); } public void shutDown(){ System.out.println("硬盘关闭了"); } }
整体类
public class Computer { private Power power; private CPU cpu; private Disk disk; public Computer(){ power = new Power(); cpu = new CPU(); disk = new Disk(); } public void startUp(){ System.out.println("开始启动电脑"); power.startUp(); cpu.startUp(); disk.startUp(); System.out.println("电脑启动了"); } public void shutDown(){ System.out.println("开始关闭电脑"); power.shutDown(); cpu.shutDown(); disk.shutDown(); System.out.println("电脑关闭了"); } }
测试类
public class User { public static void main(String[] args) { Computer computer = new Computer(); computer.startUp(); System.out.println("=========="); computer.shutDown(); } }
5、桥接模式(Bridge)
桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化。桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化,像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。
接口
public interface SourceAble { public void method(); }
两个实现类
public class Source1 implements SourceAble { @Override public void method() { System.out.println("这是Source1"); } }
public class Source2 implements SourceAble { @Override public void method() { System.out.println("这是Source2"); } }
使用抽象类定义一个桥
public abstract class Bridge { private SourceAble sourceAble; public void method(){ sourceAble.method(); } public SourceAble getSourceAble() { return sourceAble; } public void setSourceAble(SourceAble sourceAble) { this.sourceAble = sourceAble; } }
定义一个可实例化的桥
public class MyBridge extends Bridge{ @Override public void method() { getSourceAble().method(); } }
测试类
public class Test { public static void main(String[] args) { Bridge bridge = new MyBridge(); //调用第一个对象 SourceAble sourceAble1 = new Source1(); bridge.setSourceAble(sourceAble1); bridge.method(); //调用第二个对象 SourceAble sourceAble2 = new Source2(); bridge.setSourceAble(sourceAble2); bridge.method(); } }
这样,就通过对Bridge类的调用,实现了对接口SourceAble的实现类Source1和Source2的调用。这就是桥接模式。
6、组合模式(Composite)
组合模式有时又叫部分-整体模式,在处理类似树形结构的问题时比较方便。
递归组合模式简称组合模式,通过对象递归组合,形成“对象树”,以表示“整体-部分”关系的层次结构。与体现继承性的“类树”结构不同。组合模式提供了一种构造结构复杂的大对象的手段。
组合模式中含有两种类型的对象:基元对象和组合对象。组合模式使基元对象和组合对象具有一致的使用方式。
使用场景:将多个对象组合在一起进行操作,常用于表示树形结构中,例如二叉树,数等。
public class TreeNode { private String name; private TreeNode parent; private Vector<TreeNode> childern = new Vector<TreeNode>(); public TreeNode(String name){ this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public TreeNode getParent() { return parent; } public void setParent(TreeNode parent) { this.parent = parent; } //添加子节点 public void add(TreeNode node){ childern.add(node); } //删除子节点 public void remove(TreeNode node){ childern.remove(node); } //获取子节点 public String getChildern(){ StringBuffer sbf = new StringBuffer(); for (TreeNode treeNode : childern) { sbf.append(treeNode.name); } return sbf.toString(); } @Override public String toString() { return "name="+name+",parent="+parent.name+",childern="+getChildern(); } }
public class Tree { TreeNode root = null; public Tree(String name){ root = new TreeNode(name); } public static void main(String[] args) { Tree tree = new Tree("ROOT"); TreeNode nodeA = new TreeNode("A"); TreeNode nodeB = new TreeNode("B"); TreeNode nodeC = new TreeNode("C"); tree.root.setParent(nodeA);//添加父节点 nodeB.add(nodeC);//添加子节点 tree.root.add(nodeB);//添加子节点 System.out.println(tree.root); } }
7、享元模式(Flyweight)
享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。
一提到共享池,我们很容易联想到Java里面的JDBC连接池,想想每个连接的特点,我们不难总结出:适用于作共享的一些个对象,他们有一些共有的属性,就拿数据库连接池来说,url、driverClassName、username、password及dbname,这些属性对于每个连接来说都是一样的,所以就适合用享元模式来处理,建一个工厂类,将上述类似属性作为内部数据,其它的作为外部数据,在方法调用时,当做参数传进来,这样就节省了空间,减少了实例的数量。
通过连接池的管理,实现了数据库连接的共享,不需要每一次都重新创建连接,节省了数据库重新创建的开销,提升了系统的性能!
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Vector; public class ConnectionPool { private Vector<Connection> pool; //公有属性 private String url = "jdbc:mysql://localhost:3306/test"; private String username = "root"; private String password = "123456"; private String driverClassName = "com.mysql.jdbc.Driver"; private int poolSize = 100; private static ConnectionPool instance = null; Connection conn = null; //构造方法,做一些初始化工作 private ConnectionPool() { pool = new Vector<Connection>(poolSize); for (int i = 0; i < poolSize; i++) { try { Class.forName(driverClassName); conn = DriverManager.getConnection(url, username, password); pool.add(conn); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } } //返回连接到连接池 public synchronized void release() { pool.add(conn); } //返回连接池中的一个数据库连接 public synchronized Connection getConnection() { if (pool.size() > 0) { Connection conn = pool.get(0); pool.remove(conn); return conn; } else { return null; } } }
设计模式之行为型模式
![](https://oscdn.geek-share.com/Uploads/Images/Content/202002/23/c8f346871a28651543aa2e9419e00dbb.jpg)
1、策略模式(strategy)
策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数。
统一接口
public interface ICalculator { public int calculate(String exp); }
抽象类
public abstract class AbstractCalculator { public int[] split(String exp,String opt){ String array[] = exp.split(opt); int arrayInt[] = new int[2]; arrayInt[0] = Integer.parseInt(array[0]); arrayInt[1] = Integer.parseInt(array[1]); return arrayInt; } }
三个实现类
public class Minus extends AbstractCalculator implements ICalculator { @Override public int calculate(String exp) { int arrayInt[] = split(exp,"-"); return arrayInt[0]-arrayInt[1]; } }
public class Plus extends AbstractCalculator implements ICalculator { @Override public int calculate(String exp) { int arrayInt[] = split(exp,"\\+"); return arrayInt[0]+arrayInt[1]; } }
public class Multiply extends AbstractCalculator implements ICalculator { @Override public int calculate(String exp) { int arrayInt[] = split(exp,"\\*"); return arrayInt[0]*arrayInt[1]; } }
测试类
public class StrategyTest { public static void main(String[] args) { String exp = "2+8"; ICalculator cal = new Plus(); int result = cal.calculate(exp); System.out.println(result); } }
策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。
2、模板方法模式(Template Method)
一个抽象类中,有一个主方法,再定义1...n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用。
抽象类
public abstract class AbstractCalculator { //主方法,实现对本类其它方法的调用 public final int calculate(String exp,String opt){ int array[] = split(exp,opt); return calculate(array[0],array[1]); } //被子类重写的方法 abstract public int calculate(int num1,int num2); public int[] split(String exp,String opt){ String array[] = exp.split(opt); int arrayInt[] = new int[2]; arrayInt[0] = Integer.parseInt(array[0]); arrayInt[1] = Integer.parseInt(array[1]); return arrayInt; } }
继承类
public class Plus extends AbstractCalculator { @Override public int calculate(int num1,int num2) { return num1 + num2; } }
测试类
public class StrategyTest { public static void main(String[] args) { String exp = "8+8"; AbstractCalculator cal = new Plus(); int result = cal.calculate(exp, "\\+"); System.out.println(result); } }
3、观察者模式(Observer)
当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系。
接口
public interface Observer { public void update(); }
两个实现类
public class Observer1 implements Observer { @Override public void update() { System.out.println("observer1 has received!"); } }
public class Observer2 implements Observer { @Override public void update() { System.out.println("observer2 has received!"); } }
接口
public interface Subject { /*增加观察者*/ public void add(Observer observer); /*删除观察者*/ public void del(Observer observer); /*通知所有的观察者*/ public void notifyObservers(); /*自身的操作*/ public void operation(); }
抽象实现类
public abstract class AbstractSubject implements Subject { private Vector<Observer> vector = new Vector<Observer>(); @Override public void add(Observer observer) { vector.add(observer); } @Override public void del(Observer observer) { vector.remove(observer); } @Override public void notifyObservers() { Enumeration<Observer> enumo = vector.elements(); while(enumo.hasMoreElements()){ enumo.nextElement().update(); } } }
子类
public class MySubject extends AbstractSubject { @Override public void operation() { System.out.println("update self!"); notifyObservers(); } }
测试类
public class ObserverTest { public static void main(String[] args) { Subject sub = new MySubject(); sub.add(new Observer1()); sub.add(new Observer2()); sub.operation(); } }
MySubject类是主对象,Observer1和Observer2是依赖于MySubject的对象,当MySubject变化时,Observer1和Observer2必然变化。AbstractSubject类中定义着需要监控的对象列表,可以对其进行修改:增加或删除被监控对象,且当MySubject变化时,负责通知在列表内存在的对象。
4、迭代子模式(Iterator)
迭代器模式就是顺序访问聚集中的对象。一般来说,集合中非常常见,如果对集合类比较熟悉的话,理解本模式会十分轻松。这句话包含两层意思:一是需要遍历的对象,即聚集对象,二是迭代器对象,用于对聚集对象进行遍历访问。
下面代码模拟了一个集合
集合类接口
public interface Collection { //迭代器 public Iterator iterator(); //取得集合元素 public Object get(int i); //取得集合大小 public int size(); }
集合类
public class MyCollection implements Collection { public String string[] = {"A","B","C","D","E"}; @Override public Iterator iterator() { return new MyIterator(this); } @Override public Object get(int i) { return string[i]; } @Override public int size() { return string.length; } }
迭代器接口
public interface Iterator { //前移 public Object previous(); //后移 public Object next(); //是否有下一个元素 public boolean hasNext(); //取得第一个元素 public Object first(); }
迭代器
public class MyIterator implements Iterator { private Collection collection; private int pos = -1; public MyIterator(Collection collection){ this.collection = collection; } @Override public Object previous() { if(pos > 0){ pos--; } return collection.get(pos); } @Override public Object next() { if(pos<collection.size()-1){ pos++; } return collection.get(pos); } @Override public boolean hasNext() { if(pos<collection.size()-1){ return true; }else{ return false; } } @Override public Object first() { pos = 0; return collection.get(pos); } }
测试类
public class Test { public static void main(String[] args) { Collection collection = new MyCollection(); Iterator it = collection.iterator(); while(it.hasNext()){ System.out.println(it.next()); } } }
MyCollection中定义了集合的一些操作,MyIterator中定义了一系列迭代操作,且持有Collection实例。
5、责任链模式(Chain of Responsibility)
有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象决定处理该请求。但是发出者并不清楚到底最终哪个对象会处理该请求,所以,责任链模式可以实现,在隐瞒客户端的情况下,对系统进行动态的调整。
链接上的请求可以是一条链,可以是一个树,还可以是一个环,模式本身不约束这个,需要我们自己去实现,同时,在一个时刻,命令只允许由一个对象传给另一个对象,而不允许传给多个对象。
接口
public interface Handler { public void operator(); }
抽象类
public abstract class AbstractHandler { private Handler handler; public Handler getHandler() { return handler; } public void setHandler(Handler handler) { this.handler = handler; } }
继承、实现类
public class MyHandler extends AbstractHandler implements Handler { private String name; public MyHandler(String name) { this.name = name; } @Override public void operator() { System.out.println(name+"deal!"); if(getHandler()!=null){ getHandler().operator(); } } }
测试类
public class Test { public static void main(String[] args) { MyHandler h1 = new MyHandler("h1"); MyHandler h2 = new MyHandler("h2"); MyHandler h3 = new MyHandler("h3"); h1.setHandler(h2); h2.setHandler(h3); h1.operator(); } }
6、命令模式(Command)
命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行。这个过程好在,三者相互解耦,任何一方都不用去依赖其他人,只需要做好自己的事儿就行,司令员要的是结果,不会去关注到底士兵是怎么实现的。
命令模式又称事务(Transaction)模式,命令模式用于封装向某个对象的请求。所谓请求,就是应用程序的操作人员通过图形用户界面(GUI)构件如按钮、图标、菜单项等发出的操作命令。命令模式通过在请求调用对象和请求的执行对象之间增加一个Command中间对象,用以消解多对多复杂性。
Invoker是调用者(司令员),Receiver是被调用者(士兵),MyCommand是命令,实现了Command接口,持有接收对象,看实现代码:
public interface Command { public void exe(); }
public class MyCommand implements Command { private Receiver receiver; public MyCommand(Receiver receiver) { this.receiver = receiver; } @Override public void exe() { receiver.action(); } }
public class Receiver { public void action(){ System.out.println("command received!"); } }
public class Invoker { private Command command; public Invoker(Command command) { this.command = command; } public void action(){ command.exe(); } }
测试类
public class Test { public static void main(String[] args) { Receiver receiver = new Receiver(); Command cmmand = new MyCommand(receiver); Invoker invoker = new Invoker(cmmand); invoker.action(); } }
7、备忘录模式(Memento)
主要目的是保存一个对象的某个状态,以便在适当的时候恢复对象,个人觉得叫备份模式更形象些,通俗的讲下:假设有原始类A,A中有各种属性,A可以决定需要备份的属性,备忘录类B是用来存储A的一些内部状态,类C呢,就是一个用来存储备忘录的,且只能存储,不能修改等操作
原始类
public class Original { private String value; public String getValue() { return value; } public void setValue(String value) { this.value = value; } public Original(String value) { this.value = value; } //创建备忘录 public Memento createMemento(){ return new Memento(value); } //获取备忘录内容 public void restoreMemento(Memento memento){ this.value = memento.getValue(); } }
备忘录类
public class Memento { private String value; public Memento(String value) { this.value = value; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } }
仓库类
public class Storage { private Memento memento; public Storage(Memento memento) { this.memento = memento; } public Memento getMemento() { return memento; } public void setMemento(Memento memento) { this.memento = memento; } }
测试类
public class Test { public static void main(String[] args) { // 创建原始类 Original origi = new Original("wang"); // 创建并保存备忘录 Storage storage = new Storage(origi.createMemento()); // 修改原始类的状态 System.out.println("初始化状态为:" + origi.getValue()); origi.setValue("bo"); System.out.println("修改后的状态为:" + origi.getValue()); // 恢复原始类的状态 origi.restoreMemento(storage.getMemento()); System.out.println("恢复后的状态为:" + origi.getValue()); } }
Original类是原始类,里面有需要保存的属性value及创建一个备忘录类,用来保存value值。Memento类是备忘录类,Storage类是存储备忘录的类,持有Memento类的实例。
新建原始类时,value被初始化为wang,后经过修改,将value的值置为bo,最后倒数第二行进行恢复状态,结果成功恢复了。其实我觉得这个模式叫“备份-恢复”模式最形象。
8、状态模式(State)
核心思想就是:当对象的状态改变时,同时改变其行为,很好理解!就拿QQ来说,有几种状态,在线、隐身、忙碌等,每个状态对应不同的操作,而且你的好友也能看到你的状态,所以,状态模式就两点:1、可以通过改变状态来获得不同的行为。2、你的好友能同时看到你的变化。
State类是个状态类,Context类可以实现切换,我们来看看代码:
状态类
public class State { private String value; public String getValue() { return value; } public void setValue(String value) { this.value = value; } public void method1(){ System.out.println("execute the first opt!"); } public void method2(){ System.out.println("execute the second opt!"); } }
切换类
public class Context { private State state; public Context(State state) { this.state = state; } public State getState() { return state; } public void setState(State state) { this.state = state; } public void method() { if (state.getValue().equals("state1")) { state.method1(); } else if (state.getValue().equals("state2")) { state.method2(); } } }
测试类
public class Test { public static void main(String[] args) { State state = new State(); Context context = new Context(state); //设置第一种状态 state.setValue("state1"); context.method(); //设置第二种状态 state.setValue("state2"); context.method(); } }
根据这个特性,状态模式在日常开发中用的挺多的,尤其是做网站的时候,我们有时希望根据对象的某一属性,区别开他们的一些功能,比如说简单的权限控制等。
9、访问者模式(Visitor)
访问者模式就是一种分离对象数据结构与行为的方法,通过这种分离,可达到为一个被访问者动态添加新的操作而无需做其它的修改的效果。
访问者类
public interface Visitor { public void visit(Subject sub); }
public class MyVisitor implements Visitor { @Override public void visit(Subject sub) { System.out.println("visit the subject:"+sub.getSubject()); } }
主题类
public interface Subject { public void accept(Visitor visitor); public String getSubject(); }
public class MySubject implements Subject { @Override public void accept(Visitor visitor) { visitor.visit(this); } @Override public String getSubject() { return "love"; } }
测试类
public class Test { public static void main(String[] args) { Visitor visitor = new MyVisitor(); Subject sub = new MySubject(); sub.accept(visitor); } }
该模式适用场景:如果我们想为一个现有的类增加新功能,不得不考虑几个事情:
1、新功能会不会与现有功能出现兼容性问题?
2、以后会不会再需要添加?
3、如果类不允许修改代码怎么办?
面对这些问题,最好的解决方法就是使用访问者模式,访问者模式适用于数据结构相对稳定的系统,把数据结构和算法解耦。
10、中介者模式(Mediator)
中介者模式也是用来降低类类之间的耦合的,因为如果类类之间有依赖关系的话,不利于功能的拓展和维护,因为只要修改一个对象,其它关联的对象都得进行修改。如果使用中介者模式,只需关心和中介类的关系,具体类类之间的关系及调度交给中介就行,这有点像spring容器的作用。
User类统一接口,User1和User2分别是不同的对象,二者之间有关联,如果不采用中介者模式,则需要二者相互持有引用,这样二者的耦合度很高,为了解耦,引入了Mediator类,提供统一接口,MyMediator为其实现类,里面持有User1和User2的实例,用来实现对User1和User2的控制。这样User1和User2两个对象相互独立,他们只需要保持好和Mediator之间的关系就行,剩下的全由MyMediator类来维护!
中介接口
public interface Mediator { public void createMediator(); public void workAll(); }
中介类
public class MyMediator implements Mediator { private User user1; private User user2; public User getUser1() { return user1; } public User getUser2() { return user2; } @Override public void createMediator() { user1 = new User1(this); user2 = new User2(this); } @Override public void workAll() { user1.work(); user2.work(); } }
user对象类
public abstract class User { private Mediator mediator; public Mediator getMediator(){ return mediator; } public User(Mediator mediator) { this.mediator = mediator; } public abstract void work(); }
public class User1 extends User { public User1(Mediator mediator){ super(mediator); } @Override public void work() { System.out.println("user1 exe!"); } }
public class User2 extends User { public User2(Mediator mediator){ super(mediator); } @Override public void work() { System.out.println("user2 exe!"); } }
测试类
public class Test { public static void main(String[] args) { Mediator mediator = new MyMediator(); mediator.createMediator(); mediator.workAll(); } }
11、解释器模式(Interpreter)
一般主要应用在OOP开发中的编译器的开发中,所以适用面比较窄。
Context类是一个上下文环境类,Plus和Minus分别是用来计算的实现,代码如下:
public interface Expression { public int interpret(Context context); }
public class Plus implements Expression { @Override public int interpret(Context context) { return context.getNum1()+context.getNum2(); } }
public class Minus implements Expression { @Override public int interpret(Context context) { return context.getNum1()-context.getNum2(); } }
public class Context { private int num1; private int num2; public Context(int num1, int num2) { this.num1 = num1; this.num2 = num2; } public int getNum1() { return num1; } public void setNum1(int num1) { this.num1 = num1; } public int getNum2() { return num2; } public void setNum2(int num2) { this.num2 = num2; } }
测试类
public class Test { public static void main(String[] args) { // 计算9+2-8的值 int result = new Minus().interpret( (new Context(new Plus().interpret(new Context(9, 2)) , 8))); System.out.println(result); } }
原文链接:http://blog.csdn.net/zhangerqing/article/details/8194653
相关文章推荐
- JAVA设计模式系列之Adapter(适配器)-Java基础-Java-编程开发
- GOF以及java的23种设计模式简介
- Android开发之Java设计模式
- Java开发中常见的23种设计模式概述
- Java中的DAO设计模式应用举例 ------> 来自Java WEB 开发实战经典
- Android开发之Java设计模式
- Java开发下的设计模式简单说明
- java 开发中的23中设计模式
- Java开发中的23种设计模式
- Java EE WEB工程师培训-JDBC+Servlet+JSP整合开发之09.JDBC DAO设计模式
- Java开发中的23种设计模式
- java设计模式简介(2)
- Android开发之Java设计模式入门篇
- GOF以及java的23种设计模式简介
- GOF以及java的23种设计模式简介
- java设计模式简介(4)
- WEB 开发与Java框架及设计模式
- Android开发之Java设计模式
- Java开发下的设计模式简单说明
- 使用Java开发Domino业务应用的设计模式 (cont. 2)