类设计原则及设计模式
类设计的六大原则
在阿里巴巴的设计规范中有提到类的设计原则,内容就是以下几点
- 开闭原则(Open Close Principle)
开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。 - 里氏代换原则(Liskov Substitution Principle)
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。 - 依赖倒转原则(Dependence Inversion Principle)
这个是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。 - 接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。 - 迪米特法则(最少知道原则)(Demeter Principle)
为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。 - 合成复用原则(Composite Reuse Principle)
原则是尽量使用合成/聚合的方式,而不是使用继承。
设计模式定义
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。本章系Java之美[从菜鸟到高手演变]系列之设计模式,我们会以理论与实践相结合的方式来进行本章的学习,希望广大程序爱好者,学好设计模式,做一个优秀的软件工程师!
23种模式java实现源码下载地址 http://pan.baidu.com/share/link?shareid=372668&uk=4076915866#dir/path=%2F%E5%AD%A6%E4%B9%A0%E6%96%87%E4%BB%B6
设计模式的分类
总体来说设计模式分为三大类:
- 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
- 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
- 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
其实还有两类:并发型模式和线程池模式。用一个图片来整体描述一下:
创建型模式
1. 简单工厂和工厂方法模式
定义和分类
工厂方法模式实现了创建者和调用者的分离。
详细分类:
- 简单工厂模式(静态工厂模式,对新增产品无能为力,不修改代码是无法扩展的)
- 工厂方法模式(比静态工厂要好,扩展性比简单工厂模式好,但是增加类的维护)
- 抽象工厂模式
工厂方法模式包含的角色:
Product:抽象产品
ConcreteProduct:具体产品
Factory:抽象工厂
ConcreteFactory:具体工厂
说明:在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是把这个过程交给子类去做。从而更好地符合“开闭原则”。
应用举例:在实际开发中,一般将具体工厂类的实例化过程进行改进,不直接使用new 关键字来创建对象,而是将具体类的类名写入配置文件中,再通过java的反射机制,读取XML格式的配置文件,根据存储在XML文件中的类名字符串声称对象。
延伸:java反射(java reflection)
是指在程序运行时获取已知名称的类或已有对象的相关信息的一种机制,包括类的方法、属性、超类等信息。还包括实例的创建和实例类型的判断等等。可通过Class类的forName()方法返回与带有给定字符串名的类或者接口相关联的Class对象,再通过newInstance()方法创建此对象所表示的类的一个新实例。即:通过一个类名字符串得到类的实例。
Class c = Class.forName("String");
Object obj = c.newInstance();
return obj;
优点:完全符合“开闭原则"。
缺点:需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加。增加了系统的抽象性和理解难度。
使用情况:
(1)一个类不知道它所需要的对象的类。
(2)一个类通过其子类来指定创建哪个对象。
(3)将创建的对象的任务委托给多个工厂子类中某一个。
2. 抽象工厂模式
模式动机:
(1)多个位于不同产品等级结构中属于不同类型的具体产品时需要使用抽象工厂模式。
(2)它是工厂模式中最为抽象和最具一般性的一种形态。
(3)工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构。
抽象工厂模式包含的角色:
AbstractFactory:抽象工厂
ConcreteFactory:具体工厂
AbstractProduct:抽象产品
Product:具体产品
优点:
(1)抽象工厂模式隔离了具体类的生成,只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。
(2)可以实现高内聚低耦合的设计目的。
(3)增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。
缺点:
(1)难以扩展抽象工厂来生产新种类的产品。
(2)开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)
使用环境:
(1)有多于一个的产品族。属于同一个产品族的产品将在一起使用。
(2)所有的产品以同样的接口出现。
3. 单例模式
定义
定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实。
单例模式:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
单例模式要点:
(1)某个类只能有一个实例。
(2)它必须自行创建这个实例。
(3)它必须自行向整个系统提供这个实例。
模式分析:
(1)单例类的构造函数为私有。
(2)提供一个自身的静态私有成员变量。
(3)提供一个公有的静态工厂方法。
优缺点
优点:
(1)提供了对唯一实例的受控访问。
(2)可以节约系统资源。
(3)允许可变数目的实例。
缺点:
(1)由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
(2)单例类的职责过重,在一定程度上违背了“单一职责原则”。
饿汉式单例与懒汉式单例类比较
饿汉式单例类在自己被加载时就将自己实例化。
(1)从资源利用,比懒汉式差。
(2)从速度和反应时间来讲,则比懒汉式稍微好些。
(3)懒汉式单例类在实例化时,必须处理好多个线程同时首次引用此类时的访问限制问题。
通过单例模式的学习告诉我们:
1、单例模式理解起来简单,但是具体实现起来还是有一定的难度。
2、synchronized关键字锁定的是对象,在用的时候,一定要在恰当的地方使用(注意需要使用锁的对象和过程,可能有的时候并不是整个对象及整个过程都需要锁)。
到这儿,单例模式基本已经讲完了,结尾处,笔者突然想到另一个问题,就是采用类的静态方法,实现单例模式的效果,也是可行的,此处二者有什么不同?
首先,静态类不能实现接口。(从类的角度说是可以的,但是那样就破坏了静态了。因为接口中不允许有static修饰的方法,所以即使实现了也是非静态的)
其次,单例可以被延迟初始化,静态类一般在第一次加载是初始化。之所以延迟加载,是因为有些类比较庞大,所以延迟加载有助于提升性能。
再次,单例类可以被继承,他的方法可以被覆写。但是静态类内部方法都是static,无法被覆写。
最后一点,单例类比较灵活,毕竟从实现上只是一个普通的Java类,只要满足单例的基本需求,你可以在里面随心所欲的实现一些其它功能,但是静态类不行。从上面这些概括中,基本可以看出二者的区别,但是,从另一方面讲,我们上面最后实现的那个单例模式,内部就是用一个静态类来实现的,所以,二者有很大的关联,只是我们考虑问题的层面不同罢了。两种思想的结合,才能造就出完美的解决方案,就像HashMap采用数组+链表来实现一样,其实生活中很多事情都是这样,单用不同的方法来处理问题,总是有优点也有缺点,最完美的方法是,结合各个方法的优点,才能最好的解决问题!
代码实现
package com.swad.designpattern.singleton;
/**
* Created by gonghaiyu on 13/02/2017.
* 测试饿汉式单例模式
*/
public class SingletonDemo1 {
//加载类时,线程是安全的
private static SingletonDemo1 instance = new SingletonDemo1();//类初始化时立即加载
private SingletonDemo1(){
}
//方法没有同步,效率高
public static SingletonDemo1 getInstance(){
return instance;
}
}
package com.swad.designpattern.singleton;
/**
* Created by gonghaiyu on 13/02/2017.
* 测试懒汉式单例模式
*/
public class SingletonDemo2 {
private static SingletonDemo2 instance;
private SingletonDemo2(){}
//方法同步调用效率低!synchronized 可以移动到函数内部,实现双重锁单例模式
public static synchronized SingletonDemo2 getInstance(){
if(instance==null){
instance = new SingletonDemo2();
}
return instance;
}
}
package com.swad.designpattern.singleton;
/**
* Created by gonghaiyu on 13/02/2017.
* 测试静态内部类实现单例模式
* 这种方式:线程安全,调用效率高,并且实现类延迟加载!
*/
public class SingletonDemo4 {
private static class SingletonClassInstance{
private static final SingletonDemo4 instance = new SingletonDemo4();
}
private SingletonDemo4(){}
public static SingletonDemo4 getInstance(){
return SingletonClassInstance.instance;
}
}
package com.swad.designpattern.singleton;
/**
* Created by gonghaiyu on 13/02/2017.
* 测试懒汉式单例模式(如何防止反射和凡序列化漏洞)
*/
public class SingletonDemo6 {
private static SingletonDemo6 instance;
private SingletonDemo6(){
if(instance!=null){
throw new RuntimeException();
}
}
//方法同步调用效率低!synchronized 可以移动到函数内部,实现双重锁单例模式
public static synchronized SingletonDemo6 getInstance(){
if(instance==null){
instance = new SingletonDemo6();
}
return instance;
}
}
package com.swad.designpattern.singleton;
import java.lang.reflect.Constructor;
/**
* Created by gonghaiyu on 13/02/2017.
*/
public class Client2 {
public static void main(String[] args) throws Exception{
SingletonDemo1 s1 = SingletonDemo1.getInstance();
SingletonDemo1 s2 = SingletonDemo1.getInstance();
System.out.println(s1);
System.out.println(s2);
Class<SingletonDemo6> clazz = (Class<SingletonDemo6>) Class.forName("com.swad.singleton.SingletonDemo6");
Constructor<SingletonDemo6> c = clazz.getDeclaredConstructor(null);
c.setAccessible(true);
SingletonDemo6 s3 =c.newInstance();
SingletonDemo6 s4 =c.newInstance();
System.out.println(s3);
System.out.println(s4);
}
}
4. 建造者模式
模式动机:复杂对象相当于一辆有待建设的汽车,而对象的属性相当于汽车的部件。建造者返还给客户端的是一个已经建造完毕的完整产品对象,而用户无须关心该对象所包含的属性已经它们的组装方式。
建造者模式的角色:
Builder:抽象建造者
ConcreteBuilder:具体建造者
Director:指挥者
Product:产品角色
优点:
(1)客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
(2)新增的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,符合开闭原则。
缺点:
(1)建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
建设者模式与抽象工厂模式的比较:
与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族。
在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象。
工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性,其实建造者模式就是前面抽象工厂模式和最后的Test结合起来得到的。我们看一下代码:
还和前面一样,一个Sender接口,两个实现类MailSender和SmsSender。最后,建造者类如下:
public class Builder {
private List<Sender> list = new ArrayList<Sender>();
public void produceMailSender(int count) {
for (int i = 0; i < count; i++) {
list.add(new MailSender());
}
}
public void produceSmsSender(int count) {
for (int i = 0; i < count; i++) {
list.add(new SmsSender());
}
}
}
测试类:
public class Test {
public static void main(String[] args) {
Builder builder = new Builder();
builder.produceMailSender(10);
}
}
从这点看出,建造者模式将很多功能集成到一个类里,这个类可以创造出比较复杂的东西。所以与工程模式的区别就是:工厂模式关注的是创建单个产品,而建造者模式则关注创建符合对象,多个部分。因此,是选择工厂模式还是建造者模式,依实际情况而定。
5. 原型模式
(1)提供的Ctrl+C与Ctrl+V操作就是原型模式的应用,复制得到的对象与原型对象是两个类型但是内存地址不同的对象,通过原型模式可以大大提高对象的创建效率。
(2)在Struts2中为了保证线程的安全性,Action对象的创建使用了原型模式。访问一个已经存在的Action对象时将通过克隆的方式创建出一个新的对象,从而保证其中定义的变量无须进行加锁实现同步,每一个Action中都有自己的成员变量,避免Struts1因使用单例模式而导致的并发和同步问题。
(3)Spring中,也采用原型模式来创建新的bean实例。
原型模式虽然是创建型的模式,但是与工程模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。本小结会通过对象的复制,进行讲解。在Java中,复制对象是通过clone()实现的,先创建一个原型类:
public class Prototype implements Cloneable {
public Object clone() throws CloneNotSupportedException {
Prototype proto = (Prototype) super.clone();
return proto;
}
}
很简单,一个原型类,只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法,而在Object类中,clone()是native的,具体怎么实现,我会在另一篇文章中,关于解读Java中本地方法的调用,此处不再深究。在这儿,我将结合对象的浅复制和深复制来说一下,首先需要了解对象深、浅复制的概念:
浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。
此处,写一个深浅复制的例子:
public class Prototype implements Cloneable, Serializable {
private static final long serialVersionUID = 1L;
private String string;
private SerializableObject obj;
/* 浅复制 */
public Object clone() throws CloneNotSupportedException {
Prototype proto = (Prototype) super.clone();
return proto;
}
/* 深复制 */
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();
}
public String getString() {
return string;
}
public void setString(String string) {
this.string = string;
}
public SerializableObject getObj() {
return obj;
}
public void setObj(SerializableObject obj) {
this.obj = obj;
}
}
class SerializableObject implements Serializable {
private static final long serialVersionUID = 1L;
}
要实现深复制,需要采用流的形式读入当前对象的二进制输入,再写出二进制数据对应的对象。
结构型模式
7种结构型模式:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式。其中对象的适配器模式是各种模式的起源,我们看下面的图:
适配器模式
-
适配器模式
将一份类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能在一起工作。 -
模式中的角色
目标接口(Target):客户所期待的接口。目标可以是具体的或抽象的类,也可是接口。
需要适配的类(Adaptee):需要适配的类或适配者类。
适配器(Adapter):通过包装一个需要适配的对象,把原接口转换为目标接口。 -
分类
主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。
类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。
对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。
接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。 -
工作场景
3.1 经常用来做旧系统改造和升级
3.2 如果我们的系统开发之后再也不需要维护,那么很多模式都是没有必要的,但是不幸的是,事实却是维护一个系统的代价往往是开发一个系统的数倍。
我们学习中见过的场景
1.java.io.InputStreamReader(InputStream)
2.java.io.OutputStreamWriter(OutputStream)
3.JDBC给出了一个客户端通用的抽象接口,每一个具体数据引擎的JDBC驱动软件都是一个介于JDBC接口和数据库引擎接口之间的适配器软件。
4.在Spring AOP框架中,对BeforeAdvice,AfterAdvice,ThrowsAdvice三种通知类借助于适配器来实现。 -
代码实现
package com.swad.designpattern.adapter;
public interface Target {
void handleReq();
}
package com.swad.designpattern.adapter;
/**
* 被适配的类
* @author gonghaiyu
*
*/
public class Adaptee {
public void request(){
System.out.println("可以完成客户需要的功能!");
}
}
package com.swad.designpattern.adapter;
/**
* 适配器本身(类适配器方式,对象组合的方式)
* @author gonghaiyu
*
*/
public class Adapter extends Adaptee implements Target{
@Override
public void handleReq() {
super.request();
}
}
package com.swad.designpattern.adapter;
public class Adapter2 implements Target{
private Adaptee adaptee;
@Override
public void handleReq() {
adaptee.request();
}
public Adapter2(Adaptee adaptee) {
super();
this.adaptee = adaptee;
}
}
package com.swad.designpattern.adapter;
/**
* 客户端类
* (相当于例子中的笔记本,只有usb接口)
* @author gonghaiyu
*
*/
public class Client {
public void test1(Target t){
t.handleReq();
}
public static void main(String[] args){
Client c = new Client();
Adaptee a = new Adaptee();
//1.第一种方式采用类适配器的方式
// Target t =new Adapter();
//2.第二种方式采用组合的方式
Target t = new Adapter2(a);
c.test1(t);
}
}
代理模式
核心角色
1.抽象角色
定义代理角色和真是角色的公共对外方法
2.真实角色
实现抽象角色、定义真实角色所要实现的业务逻辑,供代理角色调用。
3.代理角色
实现抽象角色,是真是角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。将统一的流程控制放到代理角色中处理!
分类
静态代理:(代码写死的代理类)
动态代理:(动态生成代理类,代码自动生成代理类)
1.JDK自带的动态代理
2.javaassist字节码操作库实现
3.CGLIB
4.ASM(底层使用指令,可维护性较差)
动态代理:
JDK自带的动态代理
1.java.lang.reflect.Proxy
作用:动态生成代理类和对象
2.java.lang.reflect.InvocationHandler(处理器接口)
2.1可以通过invoke方法实现对真实角色的代理访问。
2.2每次通过Proxy生成代理类对象时都要指定对应的处理器对象
代理模式的应用场景
如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。
使用代理模式,可以将功能划分的更加清晰,有助于后期维护!
代码实现
- 静态代理
package com.swad.designpattern.proxy.staticProxy;
public interface Star {
/**
* 面谈
*/
void confer();
/**
* 签合同
*/
void signContract();
/**
* 订票
*/
void bookTicket();
/**
* 唱歌
*/
void sing();
/**
* 收钱
*/
void collectMoney();
}
package com.swad.designpattern.proxy.staticProxy;
public class RealStar implements Star{
@Override
public void confer() {
System.out.println("RealStar.confer()");
}
@Override
public void signContract() {
System.out.println("RealStar.signContract()");
}
@Override
public void bookTicket() {
System.out.println("RealStar.bookTicket()");
}
@Override
public void sing() {
System.out.println("RealStar(Joy).sing()");
}
@Override
public void collectMoney() {
System.out.println("RealStar.collectMoney()");
}
}
package com.swad.designpattern.proxy.staticProxy;
public class ProxyStar implements Star{
private Star star;
public ProxyStar(Star star) {
super();
this.star = star;
}
@Override
public void confer() {
System.out.println("RealStar.confer()");
}
@Override
public void signContract() {
System.out.println("RealStar.signContract()");
}
@Override
public void bookTicket() {
System.out.println("RealStar.bookTicket()");
}
@Override
public void sing() {
star.sing();
}
@Override
public void collectMoney() {
System.out.println("RealStar.collectMoney()");
}
}
package com.swad.designpattern.proxy.staticProxy;
public class Client {
public static void main(String[] args) {
Star real = new RealStar();
Star proxy = new ProxyStar(real);
proxy.confer();
proxy.signContract();
proxy.bookTicket();
proxy.sing();
proxy.collectMoney();
}
}
- 动态代理
package com.swad.designpattern.proxy.dynamicProxy;
public interface Star {
/**
* 面谈
*/
void confer();
/**
* 签合同
*/
void signContract();
/**
* 订票
*/
void bookTicket();
/**
* 唱歌
*/
void sing();
/**
* 收钱
*/
void collectMoney();
}
package com.swad.designpattern.proxy.dynamicProxy;
public class RealStar implements Star{
@Override
public void confer() {
System.out.println("RealStar.confer()");
}
@Override
public void signContract() {
System.out.println("RealStar.signContract()");
}
@Override
public void bookTicket() {
System.out.println("RealStar.bookTicket()");
}
@Override
public void sing() {
System.out.println("RealStar(Joy).sing()");
}
@Override
public void collectMoney() {
System.out.println("RealStar.collectMoney()");
}
}
package com.swad.designpattern.proxy.dynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class StarHandler implements InvocationHandler{
Star realStar;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("+++++++");
//可以统一处理其他
method.invoke(realStar, args);
return null;
}
public StarHandler(Star realStar) {
super();
this.realStar = realStar;
}
}
package com.swad.designpattern.proxy.dynamicProxy;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
Star real = new RealStar();
StarHandler handler = new StarHandler(real);
Star proxy = (Star)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[]{Star.class},
handler);
proxy.bookTicket();
proxy.sing();
}
}
桥接模式
桥接模式:就像一座桥,将两个变化维度连接起来,各个维度都可以独立的变化。故称:桥接模式。问题由商品分类问题引入。
桥接模式核心要点
处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继承结构,使各个维度可以独立的扩展在抽象层建立关联。
桥接模式总结
桥接模式可以取代多层继承的方案。多层继承违背了单一职责原则,复用性较差,类的个数也非常多。桥接模式可以极大的减少子类的个数,从而降低管理和维护成本。桥接模式极大的提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有的系统,符合开闭原则。
桥接模式实际开发中应用场景
-JDBC驱动程序
- AWT中的Peer架构
-银行日志管理:
格式分类:操作日志、交易日志、异常日志
距离分类:本地记录日志、异地记录日志
-人力资源系统中的奖金计算模块:
奖金分类:个人奖金、团体奖金、激励奖金
部门分类:人事部门、销售部门、研发部门
-OA系统中的消息处理:
业务类型:普通消息、加急消息、特急消息
发送消息方式:系统内消息、手机短信、邮件
代码实现
package com.swad.designpattern.proxy.bridge;
public class Computer {
protected Brand brand;
public Computer(Brand b){
this.brand = b;
}
public void sale(){
brand.sale();
}
}
class Desktop extends Computer{
public Desktop(Brand b){
super(b);
}
public void sale(){
super.sale();
System.out.println("销售台式机");
}
}
class Laptop extends Computer{
public Laptop(Brand b){
super(b);
}
public void sale(){
super.sale();
System.out.println("销售笔记本");
}
}
package com.swad.designpattern.proxy.bridge;
public interface Brand {
void sale();
}
class Lenovo implements Brand{
@Override
public void sale() {
System.out.println("销售联想电脑");
}
}
class Dell implements Brand{
@Override
public void sale() {
System.out.println("销售Dell电脑");
}
}
package com.swad.designpattern.proxy.bridge;
public class Client {
public static void main(String[] args) {
//销售联想笔记本电脑
Computer c = new Laptop(new Lenovo());
c.sale();
}
}
组合模式
使用组合模式的场景
把部分和整体的关系用树形结构来表示,从而使客户端可以使用统一的方式处理部分对象和整体对象。
组合模式的核心
抽象构件(Component)角色:定义了叶子和容器构件的共同点叶子(Leaf)构件角色:无子节点容器(Composite)构件角色:有容器特征,可以包含子节点。
开发中的应用场景
-操作系统的资源管理器
-GUI中的容器层次图
-XML文件解析
-OA系统中,组织结构的处理
-Junit单元测试框架
-将多个对象组合在一起进行操作,常用于表示树形结构中,例如二叉树,数等。
代码实现
package com.swad.designpattern.composite;
import java.util.ArrayList;
import java.util.List;
//抽象构件
public interface AbstractFile {
void killVirus();//杀毒
}
class ImageFile implements AbstractFile{
private String name;
public ImageFile(String name){
super();
this.name = name;
}
@Override
public void killVirus() {
System.out.println("--图像文件杀毒--"+name+",进行查杀!");
}
}
class TextFile implements AbstractFile{
private String name;
public TextFile(String name){
super();
this.name = name;
}
@Override
public void killVirus() {
System.out.println("--文本文件杀毒--"+name+",进行查杀!");
}
}
class Folder implements AbstractFile{
private String name;
//定义容器,用来存放不同容器构件下的子节点
private List<AbstractFile> list = new ArrayList<AbstractFile>();
@Override
public void killVirus() {
System.out.println("---文件夹:"+name+",进行查杀!");
for(AbstractFile file:list){
file.killVirus();
}
}
public Folder(String name) {
super();
this.name = name;
}
public void add(AbstractFile file){
list.add(file);
}
public void remove(AbstractFile file){
list.remove(file);
}
public AbstractFile getChild(int index){
return list.get(index);
}
}
package com.swad.designpattern.composite;
public class Client {
public static void main(String[] args) {
AbstractFile f2,f3,f4,f5,f6;
Folder f1 = new Folder("我的收藏");
f2 = new ImageFile("老高的大头像.jpg");
f3 = new TextFile("Hello.txt");
f1.add(f2);
f1.add(f3);
// f2.killVirus();
f1.killVirus();
}
}
装饰模式
实现细节
-Component抽象构件角色
真实对象和装饰对象有相同的接口。这样,客户端对象就能够以与真实对象相同的方式同装饰对象交互。
-ConcreteComponent具体构件角色(真实对象):
.io流中的FileInputStream、FileOutputStream
-Decorator装饰角色:
.持有一个抽象构件的引用。装饰对象接受所有客户端的请求,并把这些请求转发给真实的对象。这样,就能在真实对象调用前后增加新的功能。
-ConcreteDecorator具体装饰角色:
.负责给构件对象增加新的责任。
职责
-动态的为一个对象增加新的功能。
-装饰模式是一种用于代替继承的技术,无须通过继承增加子类就能扩展对象的新功能。动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)
使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀。
开发中使用的场景:
-IO中输入流和输出流的设计
-Swing包中图形界面构件功能
-Servlet API中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper,HttpServletRequestWrapper类,增强了request对象的功能。
-Struts2中,request,response,session对象的处理。
装饰器模式和桥接模式的区别
两个模式都是为了解决过多子类对象问题。但他们的诱因不一样。桥接模式是对象自身现有机制沿着多个维度变化,是既有部分不稳定。装饰模式是为了增加新的功能。
代码实现
package com.swad.designpattern.decorator;
public interface ICar {
void move();
}
//真实对象
class Car implements ICar{
@Override
public void move() {
System.out.println("陆地上跑");
}
}
class SuperCar implements ICar{
private ICar car;
public SuperCar(ICar car) {
super();
this.car = car;
}
@Override
public void move() {
car.move();
}
}
class FlyCar extends SuperCar{
public FlyCar(ICar car) {
super(car);
}
public void fly(){
System.out.println("天上飞");
}
@Override
public void move(){
super.move();
fly();
}
}
class WaterCar extends SuperCar{
public WaterCar(ICar car) {
super(car);
}
public void swin(){
System.out.println("水上游");
}
@Override
public void move(){
super.move();
swin();
}
}
package com.swad.designpattern.decorator;
public class Client {
public static void main(String[] args) {
Car car = new Car();
car.move();
System.out.println("增加新的功能,飞行。。。");
FlyCar flycar = new FlyCar(car);
flycar.move();
System.out.println("增加两个新的功能,飞行,水里游。。。");
WaterCar watercar = new WaterCar(new FlyCar(car));
watercar.move();
}
}
外观模式
外观模式核心
为子系统提供统一的入口,封装子系统的复杂性,便于客户端调用。
开发中常见的场景
频率很高,哪里都会遇到。各种技术和框架中,都有外观模式的使用。如:
JDBC封装后的,commons提供的DBUtils类,Hibernate提供的工具类、Spring JDBC 工具类等。
代码实现
如果我们没有Computer类,那么,CPU、Memory、Disk他们之间将会相互持有实例,产生关系,这样会造成严重的依赖,修改一个类,可能会带来其他类的修改,这不是我们想要看到的,有了Computer类,他们之间的关系被放在了Computer类里,这样就起到了解耦的作用,这,就是外观模式!
享元模式
场景
内存属于稀缺资源,不要随便浪费。如果有很多个完全相同或相似对象,我们可以通过享元模式,节省内存。通常与工厂模式一起使用。
核心
-享元模式以共享的方式高效地支持大量细粒度对象的重用。
-享元对象能做到共享的关键是区分了内部状态和外部状态。
内部状态:可以共享,不会随环境变化而改变。
外部状态:不可以共享,会随环境变化而改变。
享元模式实现
-FlyweightFactory享元工厂类
创建并管理享元对象,享元池一般设计成键值对
-FlyWeigth抽象享元类
通常是一个接口或抽象类,声明公共方法,这些方法可以向外界提供对象的内部状态,设置外部状态。
-ConcreteFlyWeight具体享元类
为内部状态提供成员变量进行存储
-UnsharedConcreteFlyWeight非共享享元类
不能被共享的子类可以设计为非共享享元类
享元模式开发中应用的场景
-享元模式由于其共享的特性,可以在任何“池”中操作,比如:线程池、数据库连接池。
-String类的设计也是享元模式
代码实现
package com.swad.designpattern.facade;
/**
* 外部状态UnShared
* @author gonghaiyu
*
*/
public class Coordinate {
private int x,y;
public Coordinate(int x, int y) {
super();
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
package com.swad.designpattern.facade;
public interface ChessFlyWeight {
void setColor(String c);
String getColor();
void display(Coordinate c);
}
class ConcreteChess implements ChessFlyWeight{
private String color;
public ConcreteChess(String color) {
super();
this.color = color;
}
@Override
public void setColor(String c) {
this.color = c;
}
@Override
public String getColor() {
return color;
}
@Override
public void display(Coordinate c) {
System.out.println("棋子颜色:"+color);
System.out.println("棋子位置:"+c.getX()+"----"+c.getY());
}
}
package com.swad.designpattern.facade;
import java.util.HashMap;
import java.util.Map;
/**
* 享元工厂类
* @author gonghaiyu
*
*/
public class ChessFlyWeightFactory {
//享元池
private static Map<String,ChessFlyWeight> map = new HashMap<String,ChessFlyWeight>();
public static ChessFlyWeight getChess(String color){
if(map.get(color)!=null){
return map.get(color);
}else{
ChessFlyWeight cfw = new ConcreteChess(color);
map.put(color, cfw);
return cfw;
}
}
}
package com.swad.designpattern.facade;
public class Client {
public static void main(String[] args) {
ChessFlyWeight chess1 = ChessFlyWeightFactory.getChess("黑色");
ChessFlyWeight chess2 = ChessFlyWeightFactory.getChess("黑色");
System.out.println(chess1);
System.out.println(chess2);
System.out.println("增加外部状态的处理========");
chess1.display(new Coordinate(10,10));
chess2.display(new Coordinate(20,20));
}
}
行为型模式
行为型模式共11种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
先来张图,看看这11中模式的关系:
第一类:通过父类与子类的关系进行实现。第二类:两个类之间。第三类:类的状态。第四类:通过中间类。
策略模式
场景
-某个市场人员接到单后的报价策略(CRM系统中常见问题)。报价策略很复杂,可以简单作如下的分类:
普通客户小批量报价
普通客户大批量报价
老客户小批量报价
老客户大批量报价
-具体选用哪个报价策略,这需要根据实际情况来确定。这时候,我们采用策略模式即可。
本质
-分离算法,选择实现
开发中常见的场景
-JAVASE中GUI编程中,布局管理
-spring框架中,resource接口,资源访问策略
-javax.servlet.http.HttpServlet#service()
代码实现
package com.swad.designpattern.strategy;
public interface Strategy {
public double getPrice(double standardPrice);
}
package com.swad.designpattern.strategy;
/**
* 负责和具体的策略类交互
* 这样的话,具体的算法和直接的客户端调用分离了,
* 使得算法可以独立于客户端独立的变化
* @author gonghaiyu
*
*/
public class Context {
private Strategy strategy;//当前采用的算法对象
//可以通过构造器来注入
public Context(Strategy strategy) {
super();
this.strategy = strategy;
}
//可以通过set方法注入
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
//打印价格
public void printPrice(double s){
System.out.println("您该报价:"+strategy.getPrice(s));
}
}
package com.swad.designpattern.strategy;
public class NewCustomerManyStrategy implements Strategy{
@Override
public double getPrice(double standardPrice) {
System.out.println("打九折");
return standardPrice*0.9;
}
}
package com.swad.designpattern.strategy;
public class NewCustomerStrategy implements Strategy{
@Override
public double getPrice(double standardPrice) {
System.out.println("不打折");
return standardPrice;
}
}
package com.swad.designpattern.strategy;
public class OldCustomerManyStrategy implements Strategy{
@Override
public double getPrice(double standardPrice) {
System.out.println("打八折");
return standardPrice*0.8;
}
}
package com.swad.designpattern.strategy;
public class OldCustomerStrategy implements Strategy{
@Override
public double getPrice(double standardPrice) {
System.out.println("打八五折");
return standardPrice*0.85;
}
}
package com.swad.designpattern.strategy;
public class Client {
public static void main(String[] args) {
Strategy s1 = new OldCustomerManyStrategy();
Context ctx = new Context(s1);
ctx.printPrice(1000);
}
}
package com.swad.designpattern.strategy;
/**
* 实现起来比较容易,符合一般开发人员的思路
* 假如,类型比较多,算法比较复杂时,整个条件语句的代码就变得很长,难于维护
* 如果有新增类型,就需要频繁的修改此处的代码!
* 不符合开闭原则!
* @author gonghaiyu
*
*/
public class TestStrategy {
public double getPrice(String type, double price){
if (type.equals("普通客户小批量")){
System.out.println("不打折");
return price;
}else if(type.equals("普通客户大批量")){
System.out.println("打九折");
return price * 0.9;
}else if(type.equals("老客户小批量")){
System.out.println("打八五折");
return price * 0.85;
}else if(type.equals("老客户大批量")){
System.out.println("打八折");
return price * 0.8;
}
return price;
}
}
模版方法模式
核心
-处理某个流程的代码已经都具备,但是其中某个节点的代码暂时不能确定。因此,我们采用工厂方法模式,将这个节点的代码实现转移给子类完成。即:处理步骤父类中定义好,具体实现延迟到子类中定义。
什么时候用到模版方法模式
-实现一个算法时,整体步骤很固定,但是,某些部分易变。易变部分可以抽象出来,供子类实现。
开发中常见的场景
-非常频繁。各个框架、类库中都有他的影子。比如常见的有:
数据库访问的封装
JUNIT单元测试
Servlet关于doGet/doPost方法使用
Hibernate模版程序
Spring中JDBCTemplate、HibernateTemplate等。
代码实现
package com.swad.designpattern.templateMethod;
public abstract class BankTemplateMethod {
//具体方法
public void takeNumber(){
System.out.println("取号排队");
}
public abstract void transact();//办理具体的业务,钩子方法
public void evaluate(){
System.out.println("反馈评分");
}
public final void process(){//模版方法
this.takeNumber();
this.transact();
this.evaluate();
}
}
package com.swad.designpattern.templateMethod;
public class Client {
public static void main(String[] args) {
BankTemplateMethod btm = new DrawMoney();
btm.process();
//采用内部类
BankTemplateMethod btm2 = new BankTemplateMethod() {
@Override
public void transact() {
System.out.println("我要存钱!");
}
};
btm2.process();
}
}
class DrawMoney extends BankTemplateMethod {
@Override
public void transact() {
System.out.println("我要取款!");
}
}
观察者模式
广播机制的核心,我们把多个订阅者、客户称为观察者,需要同步给多个订阅者的数据封装到对象中,称之为目标。类似于邮件订阅和RSS订阅,当我们浏览一些博客或wiki时,经常会看到RSS图标,就这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。
核心
-观察者模式主要用于1:N的通知。当一个对象(目标对象Subject或Objserver)-消息发布的状态变化时,他需要及时告知一系列对象(观察者对象,Observer),令他们做出响应。
通知观察者的方式
推
-每次都会把通知以广播方式发送给所有的观察者,所有观察者只能被动接收。
拉
-观察者只要直到有情况即可。至于什么时候获取内容,获取什么内容,都可以自主决定。
开发中常见的场景
-聊天室程序的,服务器转发给所有客户端
-网络游戏(多人联机对战)场景中,服务器将客户端的状态进行分发
-邮件订阅
-servlet中,监听器的实现
-Android中,广播机制
-JDK的AWT中事件处理模型,基于观察者模式的委派事件模型(Delegation Event Model)
事件源----目标对象
事件监听器----观察者
-京东商城中,群发某商品打折信息
代码实现
package com.swad.designpattern.observer;
import java.util.ArrayList;
import java.util.List;
//订阅的主题类
public class Subject {
protected List<Observer> list = new ArrayList<Observer>();
public void register(Observer obs){
list.add(obs);
}
public void removeObserver(Observer obs){
list.remove(obs);
}
public void notifyAllObservers(){
for(Observer obs:list){
obs.update(this);
}
}
}
package com.swad.designpattern.observer;
/**
* 具体主题类
* @author gonghaiyu
*
*/
public class ConcreteSubject extends Subject{
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
//主题对象(目标对象)发生变化,请通知所有的观察者
this.notifyAllObservers();
}
}
package com.swad.designpattern.observer;
public interface Observer {
void update(Subject sub);
}
package com.swad.designpattern.observer;
public class ObserverA implements Observer{
private int myState;//myState需要跟目标对象的state值保持一致
@Override
public void update(Subject sub) {
myState = ((ConcreteSubject)sub).getState();
}
public int getMyState() {
return myState;
}
public void setMyState(int myState) {
this.myState = myState;
}
}
package com.swad.designpattern.observer;
public class Client {
public static void main(String[] args) {
//目标对象
ConcreteSubject subject = new ConcreteSubject();
//创建多个观察者
ObserverA obs1 = new ObserverA();
ObserverA obs2 = new ObserverA();
ObserverA obs3 = new ObserverA();
//将这三个观察者添加到subject对象的观察者队伍中
subject.register(obs1);
subject.register(obs2);
subject.register(obs3);
//改变subject的状态
subject.setState(3000);
System.out.println("###########");
//查看观察者的状态是不是发生了变化
System.out.println(obs1.getMyState());
System.out.println(obs2.getMyState());
System.out.println(obs3.getMyState());
}
}
迭代子模式
场景
-提供一种可以遍历聚合对象的方式。又称为:游标cursor模式
-聚合对象:存储数据
-迭代器:遍历数据
结构
-聚合对象:存储数据
-迭代器:遍历数据
开发中常见的场景
-jdk内置的迭代器(List、Set)
-正向/逆向遍历的迭代器
代码实现
package com.swad.designpattern.iterator;
/**
* 兹定于游标接口
* @author gonghaiyu
*
*/
public interface MyIterator {
void first();//将游标指向第一个
void next();//将游标指向下一个
boolean hasNext();//判断是否存在下一个元素
boolean isFirst();
boolean isLast();
Object getCurrentObj();//获取当前游标指向的对象
}
package com.swad.designpattern.iterator;
import java.util.ArrayList;
import java.util.List;
/**
* 自定义的聚合类
* @author gonghaiyu
*
*/
public class ConcreteMyAggregate {
private List<Object> list = new ArrayList<Object>();
public void addObject(Object obj){
this.list.add(obj);
}
public void removeObject(Object obj){
this.list.remove(obj);
}
public List<Object>getList(){
return list;
}
public void setList(List<Object>list){
this.list = list;
}
public MyIterator createIterator(){
return new ConcreteIterator();
}
//使用内部类定义迭代器,可以直接使用外部类的属性
private class ConcreteIterator implements MyIterator{
private int cursor;//定义游标用于记录遍历的位置
@Override
public void first() {
cursor = 0;
}
@Override
public void next() {
if(cursor<list.size())
cursor++;
}
@Override
public boolean hasNext() {
if(cursor<list.size()){
return true;
}
return false;
}
@Override
public boolean isFirst() {
return cursor==0?true:false;
}
@Override
public boolean isLast() {
return cursor==(list.size()-1)?true:false;
}
@Override
public Object getCurrentObj() {
return list.get(cursor);
}
}
}
package com.swad.designpattern.iterator;
public class Client {
public static void main(String[] args) {
ConcreteMyAggregate cma = new ConcreteMyAggregate();
cma.addObject("aa");
cma.addObject("bb");
cma.addObject("cc");
MyIterator iter = cma.createIterator();
while(iter.hasNext()){
System.out.println(iter.getCurrentObj());
iter.next();
}
}
}
责任链模式
定义:将能够处理同一类请求的对象连成一条链,所提交的请求沿着链传递,链上的对象逐个判断是否有能力处理该请求,如果能则处理,如果不能则传递给链上的下一个对象。
场景:
-打牌时,轮流出牌
-接力赛跑
-大学中,奖学金审批
-公司中,公文审批
开发中常见的场景
-java中,异常机制就是一种责任链模式,一个try可以对应多个catch,当第一个catch不匹配类型,则自动跳到第二个catch。
-javascript语言中,事件的 和捕获机制。Java语言中,事件的处理采用观察者模式。
-servlet开发中,过滤器的链试处理
-struts2中,拦截器的调用也是典型的责任链模式
代码实现
package com.swad.designpattern.chainOfResp;
/**
* 封装请假的基本信息
* @author gonghaiyu
*
*/
public class LeaveRequest {
private String empName;
private int leaveDays;
private String reason;
public LeaveRequest(String empName, int leaveDays, String reason) {
super();
this.empName = empName;
this.leaveDays = leaveDays;
this.reason = reason;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public int getLeaveDays() {
return leaveDays;
}
public void setLeaveDays(int leaveDays) {
this.leaveDays = leaveDays;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
}
package com.swad.designpattern.chainOfResp;
public abstract class Leader {
protected String name;
protected Leader nextLeader;//责任链上的后继对象
public Leader(String name) {
super();
this.name = name;
}
//设定责任链上的后缀对象
public void setNextLeader(Leader nextLeader) {
this.nextLeader = nextLeader;
}
/**
* 抽象方法,让子类处理请求的核心的业务方法
* @param request
*/
public abstract void handleRequest(LeaveRequest request);
}
package com.swad.designpattern.chainOfResp;
/**
* 小于3天
* 先主任审批
* @author gonghaiyu
*
*/
public class Director extends Leader{
public Director(String name) {
super(name);
}
@Override
public void handleRequest(LeaveRequest request) {
if(request.getLeaveDays()<3){
System.out.println("员工:"+request.getEmpName()+"请假,天数:"+request.getLeaveDays()+",理由:"+request.getReason());
System.out.println("主任:"+this.name+",审批通过!");
}else{
if(this.nextLeader!=null){
this.nextLeader.handleRequest(request);
}
}
}
}
package com.swad.designpattern.chainOfResp;
/**
* 大于等于3天,小于10天
* 经理审批
* @author gonghaiyu
*
*/
public class Manager extends Leader{
public Manager(String name) {
super(name);
}
@Override
public void handleRequest(LeaveRequest request) {
if(request.getLeaveDays()<30){
System.out.println("员工:"+request.getEmpName()+"请假,天数:"+request.getLeaveDays()+",理由:"+request.getReason());
System.out.println("经理:"+this.name+",审批通过!");
}else{
if(this.nextLeader!=null){
this.nextLeader.handleRequest(request);
}
}
}
}
package com.swad.designpattern.chainOfResp;
/**
* 大于10天
* 总经理审批
* @author gonghaiyu
*
*/
public class GeneralManager extends Leader{
public GeneralManager(String name) {
super(name);
}
@Override
public void handleRequest(LeaveRequest request) {
if(request.getLeaveDays()<10){
System.out.println("员工:"+request.getEmpName()+"请假,天数:"+request.getLeaveDays()+",理由:"+request.getReason());
System.out.println("总经理:"+this.name+",审批通过!");
}else{
System.out.println("莫非"+request.getEmpName()+"想辞职,居然请假"+request.getLeaveDays()+"天");
}
}
}
package com.swad.designpattern.chainOfResp;
public class Client {
public static void main(String[] args) {
Leader a = new Director("张三");
Leader b = new Director("李四");
Leader c = new GeneralManager("王五");
//组织责任链的关系
a.setNextLeader(b);
b.setNextLeader(c);
//开始请假操作
LeaveRequest reql = new LeaveRequest("TOM", 9, "回家相亲");
a.handleRequest(reql);
}
}
命令模式
举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行。这个过程好在,三者相互解耦,任何一方都不用去依赖其他人,只需要做好自己的事儿就行,司令员要的是结果,不会去关注到底士兵是怎么实现的。
结构
-Command抽象命令
-ConcreteCommand具体命令类
-Invoker调用者/请求者
请求的发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联。在程序运行时,将调用命令对象的execute(),间接调用接收者的相关操作。
-Receiver接收者
接收者执行与请求相关的操作,具体实现对请求的业务处理。
未抽象前,实际执行操作内容的对象。
-Client客户类
在客户类中需要创建调用者对象、具体命令类对象,在创建具体命令对象时指定对象的接收者。发送者和接收者之间没有直接关系,都通过命令对象间接调用。
开发中常见的场景
-struts2中,action的整个调用过程中就有命令模式
-数据库事务机制的底层实现
-命令的撤销和恢复(与备忘录模式一起使用)
代码实现
package com.swad.designpattern.command;
public interface Command {
void execute();
}
class ConcreteCommand implements Command{
private Receiver receiver;//命令真正的执行者
@Override
public void execute() {
//命令真正执行前或后,执行相关的处理
receiver.action();
}
public ConcreteCommand(Receiver receiver) {
super();
this.receiver = receiver;
}
}
package com.swad.designpattern.command;
public class Receiver {
public void action(){
System.out.println("Receiver.action()");
}
}
package com.swad.designpattern.command;
//调用者-发起者
public class Invoke {
//可以通过容器(List)很多命令进行批处理
private Command command;
public Invoke(Command command) {
super();
this.command = command;
}
//业务方法,用于调用命令类的方法
public void call(){
command.execute();
}
}
package com.swad.designpattern.command;
public class Client {
public static void main(String[] args) {
Command c = new ConcreteCommand(new Receiver());
Invoke i = new Invoke(c);
i.call();
//上面类似于:new Receiver().action();
}
}
备忘录模式
核心
-就是保存某个对象内部状态的拷贝,这样以后就可以将该对象恢复到原先的状态
结构
-源发起类Originator
-备忘录类Memento
-负责人类CareTake
负责保存好的备忘录对象;可以通过增加容器(list,stack),设置多个“备忘点”;将多个备忘录对象,序列化和持久化。
开发中常见的应用场景
-棋类游戏中的,悔棋
-普通软件中的,撤销操作
-数据库软件中的事务管理回滚操作
-photoshop 软件中的历史记录
代码实现
package com.swad.designpattern.memento;
/**
* 源发起类
* @author gonghaiyu
*
*/
public class Emp {
private String ename;
private int age;
private double salary;
//进行备忘操作,并返回备忘录对象
public EmpMemento memento(){
return new EmpMemento(this);
}
//进行数据恢复,恢复成指定备忘录对象的值
public void recovery(EmpMemento mmt){
this.ename = mmt.getEname();
this.age = mmt.getAge();
this.salary = mmt.getSalary();
}
public Emp(String ename, int age, double salary) {
super();
this.ename = ename;
this.age = age;
this.salary = salary;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
package com.swad.designpattern.memento;
/**
* 备忘录类
* @author gonghaiyu
*
*/
public class EmpMemento {
private String ename;
private int age;
private double salary;
public EmpMemento(Emp e){
this.ename = e.getEname();
this.age = e.getAge();
this.salary = e.getSalary();
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
package com.swad.designpattern.memento;
/**
* 负责人类
* 管理备忘录对象
* @author gonghaiyu
*
*/
public class CareTaker {
private EmpMemento memento;
public EmpMemento getMemento() {
return memento;
}
public void setMemento(EmpMemento memento) {
this.memento = memento;
}
}
package com.swad.designpattern.memento;
public class Client {
public static void main(String[] args) {
CareTaker taker = new CareTaker();
Emp emp = new Emp("高某某",18,900);
System.out.println("第一次打印对象:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary());
taker.setMemento(emp.memento());//备忘一次
emp.setAge(38);
emp.setEname("搞起");
emp.setSalary(9000);
System.out.println("第二次打印对象:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary());
emp.recovery(taker.getMemento());
System.out.println("第三次打印对象:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary());
}
}
状态模式
场景
电梯的运行
维修、正常、自动关闭、自动开门、向上运行、向下运行、消防状态
红绿灯
红灯、绿灯、黄灯
酒店系统中,房间的状态变化
已预定、已入住、空闲
核心:
-用于解决系统中复杂对象的状态转换及不同状态下行为的封装。
核心思想就是:当对象的状态改变时,同时改变其行为,很好理解!就拿QQ来说,有几种状态,在线、隐身、忙碌等,每个状态对应不同的操作,而且你的好友也能看到你的状态,所以,状态模式就两点:1、可以通过改变状态来获得不同的行为。2、你的好友能同时看到你的变化。
结构
-Context环境类
环境类中维护一个State对象,他是定义了当前的状态。
-State抽象状态类
-ConcreteState具体状态类
每一个类封装了一个状态对象的行为
开发中常见的场景
-银行系统中账号状态的管理
-OA系统中公文状态的管理
-酒店系统中,房间状态的管理
-线程对象各状态之间的切换
代码实现
package com.swad.designpattern.state;
/**
* 空闲状态
* Created by gonghaiyu on 05/03/2017.
*/
public interface State {
void handle();
}
package com.swad.designpattern.state;
/**
* Created by gonghaiyu on 05/03/2017.
*/
public class FreeState implements State {
@Override
public void handle() {
System.out.println("房间空闲没有人住");
}
}
package com.swad.designpattern.state;
/**
* 已入住状态
* Created by gonghaiyu on 05/03/2017.
*/
public class BookedState implements State{
@Override
public void handle() {
System.out.println("房间已预定!别人不能定!");
}
}
package com.swad.designpattern.state;
/**
* Created by gonghaiyu on 05/03/2017.
*/
public class CheckedInState implements State{
@Override
public void handle() {
System.out.println("房间已入住!请勿打扰!");
}
}
package com.swad.designpattern.state;
/**
* Created by gonghaiyu on 05/03/2017.
*/
public class Context {
//如果是银行系统,这个Context类就是账号,根据金额不同,切换不同的状态
private State state;
public void setState(State s){
System.out.println("修改状态!");
state = s;
state.handle();
}
}
package com.swad.designpattern.state;
/**
* Created by gonghaiyu on 05/03/2017.
*/
public class Client {
public static void main(String[] args){
Context ctx = new Context();
ctx.setState(new FreeState());
}
}
访问者模式
模式动机:
-对于存储在一个集合中的对象,他们可能具有不同的类型(即使有一个公共的接口),
对于该集合的对象,可以接受一类称为访问者的对象来访问。不同的访问者其访问
方式也有所不同。
定义:
-表示一个作用于某对象结构中的各元素的操作,它使我们可以在不改变该元素
的类的前提下定义作用于这些元素的新操作。
简单来说,访问者模式就是一种分离对象数据结构与行为的方法,通过这种分离,可达到为一个被访问者动态添加新的操作而无需做其它的修改的效果。
开发中的场景(应用范围非常少,了解即可)
-xml文档解析器设计
-编译器的设计
-复杂集合对象的处理
中介者模式
核心
-如果一个系统中对象之间的联系呈现为网状结构,对象之间存在大量
多对多关系,将导致关系及其复杂,这些对象称为"同事对象"
-我们可以引入一个中介者对象,使各个同事对象只跟中介者对象打教导道,
将复杂的网络结构化解为如下的星形结构。
中介者模式的本质
-解耦多个同事对象之间的交互关系。每个对象都持有中介者对象的引用,
只跟中介者对象打交道。我们通过中介者对象统一管理这些交互关系
开发中常见的场景
-MVC模式(其中的C,控制器就是一个中介对象。M和V都和他打交道。)
-窗口游戏程序,窗口软件开发中窗口对象也是一个中介者对象
-图形界面开发GUI中,多个组建之间的交互,可以通过引入一个中介者对象来
解决,可以是整体的窗口对象或者DOM对象
-Java.lang.reflect.Method#invoke()
代码实现
package com.swad.designpattern.mediator;
/**
* Created by gonghaiyu on 05/03/2017.
*/
public interface Mediator {
void register(String dname,Department d);
void command(String dname);
}
package com.swad.designpattern.mediator;
import java.util.HashMap;
import java.util.Map;
/**
* Created by gonghaiyu on 05/03/2017.
*/
public class President implements Mediator {
private Map<String,Department> map = new HashMap<String,Department>();
@Override
public void register(String dname, Department d) {
map.put(dname,d);
}
@Override
public void command(String dname) {
map.get(dname).selfAction();
}
}
package com.swad.designpattern.mediator;
/**
* 同事类的接口
* Created by gonghaiyu on 05/03/2017.
*/
public interface Department {
void selfAction();//做本部门的事情
void outAction();//向总经理发出申请
}
package com.swad.designpattern.mediator;
/**
* Created by gonghaiyu on 05/03/2017.
*/
public class Development implements Department{
private Mediator m;//持有中介者(总经理)的引用
public Development(Mediator m) {
super();
this.m = m;
m.register("development",this);
}
@Override
public void selfAction() {
System.out.println("汇报工作!没钱了,需要资金支持!");
}
@Override
public void outAction() {
System.out.println("专心科研,开发项目!");
}
}
package com.swad.designpattern.mediator;
/**
* Created by gonghaiyu on 05/03/2017.
*/
public class Finacial implements Department{
private Mediator m;//持有中介者(总经理)的引用
public Finacial(Mediator m) {
super();
this.m = m;
m.register("finacial",this);
}
@Override
public void selfAction() {
System.out.println("数钱!");
}
@Override
public void outAction() {
System.out.println("汇报工作!没钱了,钱太多了!怎么花?");
}
}
package com.swad.designpattern.mediator;
/**
* Created by gonghaiyu on 05/03/2017.
*/
public class Market implements Department{
private Mediator m;//持有中介者(总经理)的引用
public Market(Mediator m) {
super();
this.m = m;
m.register("finacial",this);
}
@Override
public void selfAction() {
System.out.println("跑去接项目!");
}
@Override
public void outAction() {
System.out.println("汇报工作!项目的进度,需要资金支持!");
m.command("finacial");
}
}
package com.swad.designpattern.mediator;
/**
* Created by gonghaiyu on 05/03/2017.
*/
public class Client {
public static void main(String [] args){
Mediator m = new President();
Market market = new Market(m);
Development devp = new Development(m);
Finacial f = new Finacial(m);
market.selfAction();
market.outAction();
}
}
解释器模式
介绍
-是一种不常用的设计模式
-用于描述如何构成一个简单的语言解释器,主要用于使用面向对象语言开发的
编译器和解释器设计。
-当我们需要开发一种新的语言时,可以考虑使用解释器模式
-尽量不要使用使用解释器模式,在项目中,可以使用Jruby,Groovy,java的
js引擎来替代解释器的作用,弥补java语言的不足。
开发中常见的场景
-EL表达式的处理
-正则表达式解释器
-SQL语法的解释器
-数学表达式解析器
并发设计模式
Furture模式
JDK对Future模式的支持
public static void main(String [] args) throws InterruptedException, ExecutionException {
//构造FutureTask
FutureTask<String> future = new FutureTask<>(new RealData("a"));
ExecutorService executor = Executors.newFixedThreadPool(1);
//执行FutureTask,相当于上例中的client.request("a")发送请求
//在这里开启线程进行RealData的call()执行
executor.submit(future);
System.out.println("请求完毕");
try {
//这里可以做一些业务操作,这里用sleep替代
Thread.sleep(1000);
} catch (InterruptedException e) {
}
//相当于data.getResult(),取得call()方法的返回值
//如果此时call()方法没有执行完毕,则依然会等待(阻塞)
System.out.println("数据=" + future.get());
}
生产者消费者模式
生产者消费者模式是一个经典的多线程设计模式。它为多线程间的协作提供了良好的解决方案。在生产者消费者模式中,通常由两类线程,即若干个生产者线程和若干个消费者线程。生产者线程负责提交用户请求,消费者线程则负责具体处理生产者提交的任务。生产者和消费者之间则通过共享内存缓冲区进行通信。
参考文献
http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html
参考
链接:https://pan.baidu.com/s/1n0oaz8g1ZPCn2_qQEp3Rag
提取码:3w6k