百味皆苦 java后端开发攻城狮

java的23种设计模式

2019-04-27
百味皆苦

1:单例模式

核心作用

  • 单例模式保证一个类只有一个实例,并且提供一个访问该实例的全局访问点

应用场景

  • Windows的Task Manager(任务管理器)就是很典型的单例模式
  • windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
  • 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,每次new一个对象去读取。
  • 网站的计数器,一般也是采用单例模式实现,否则难以同步。
  • 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
  • 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。
  • 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。
  • Application 也是单例的典型应用(Servlet编程中会涉及到)
  • 在Spring中,每个Bean默认就是单例的,这样做的优点是Spring容器可以管理
  • 在servlet编程中,每个Servlet也是单例
  • 在spring MVC框架/struts1框架中,控制器对象也是单例

优点

  • 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决
  • 单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理

实现方式

  • 主要: • 饿汉式(线程安全,调用效率高。 但是,不能延时加载。) • 懒汉式(线程安全,调用效率不高。 但是,可以延时加载。)
  • 其他: • 双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题。不建议使用) • 静态内部类式(线程安全,调用效率高。 但是,可以延时加载) • 枚举单例(线程安全,调用效率高,不能延时加载)

饿汉式

  • 饿汉式单例模式代码中,static变量会在类装载时初始化,此时也不会涉及多个线程对象访问该对象的问 题。虚拟机保证只会装载一次该类,肯定不会发生并发访问的问题。因此,可以省略synchronized关键字。
  • 问题:如果只是加载本类,而不是要调用getInstance(),甚至永远没有调用,则会造成资源浪费!
package com.bjsxt.singleton;

/**
 * 测试饿汉式单例模式
 * @author 尚学堂高淇 www.sxt.cn
 *
 */
public class SingletonDemo1 {
	
	//类初始化时,立即加载这个对象(没有延时加载的优势)。加载类时,天然的是线程安全的!
	private static SingletonDemo1 instance = new SingletonDemo1();  
	
	private SingletonDemo1(){ 
	}
	
	//方法没有同步,调用效率高!
	public static SingletonDemo1  getInstance(){
		return instance;
	}
	
}

懒汉式

  • lazy load! 延迟加载, 懒加载! 真正用的时候才加载!
  • 资源利用率高了。但是,每次调用getInstance()方法都要同步,并发效率较低。
package com.bjsxt.singleton;

/**
 * 测试懒汉式单例模式
 * @author 尚学堂高淇 www.sxt.cn
 *
 */
public class SingletonDemo2 {
	
	//类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)。
	private static SingletonDemo2 instance;  
	
	private SingletonDemo2(){ //私有化构造器
	}
	
	//方法同步,调用效率低!
	public static  synchronized SingletonDemo2  getInstance(){
		if(instance==null){
			instance = new SingletonDemo2();
		}
		return instance;
	}
	
}

双重检测锁

  • 这个模式将同步内容下方到if内部,提高了执行的效率不必每次获取对象时都进行同步,只有第一次才同步 创建了以后就没必要了。
  • 由于编译器优化原因和JVM底层内部模型原因,偶尔会出问题。不建议使用。
package com.bjsxt.singleton;

/**
 * 双重检查锁实现单例模式
 * @author 尚学堂高淇 www.sxt.cn
 *
 */
public class SingletonDemo3 { 

  private static SingletonDemo3 instance = null; 

  public static SingletonDemo3 getInstance() { 
    if (instance == null) { 
      SingletonDemo3 sc; 
      synchronized (SingletonDemo3.class) { 
        sc = instance; 
        if (sc == null) { 
          synchronized (SingletonDemo3.class) { 
            if(sc == null) { 
              sc = new SingletonDemo3(); 
            } 
          } 
          instance = sc; 
        } 
      } 
    } 
    return instance; 
  } 

  private SingletonDemo3() { 

  } 
    
}

静态内部类

  • 也是一种懒加载方式
  • 外部类没有static属性,则不会像饿汉式那样立即加载对象。
  • 只有真正调用getInstance(),才会加载静态内部类。加载类时是线程 安全的。 instance是static final 类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性.
  • 兼备了并发高效调用和延迟加载的优势!
package com.bjsxt.singleton;

/**
 * 测试静态内部类实现单例模式
 * 这种方式:线程安全,调用效率高,并且实现了延时加载!
 * @author 尚学堂高淇 www.sxt.cn
 *
 */
public class SingletonDemo4 {
	
	private static class SingletonClassInstance {
		private static final SingletonDemo4 instance = new SingletonDemo4();
	}
	
	private SingletonDemo4(){
	}
	
	//方法没有同步,调用效率高!
	public static SingletonDemo4  getInstance(){
		return SingletonClassInstance.instance;
	}
	
}

问题

  • 反射可以破解上面几种(不包含枚举式)实现方式!(可以在构造方法中手动抛出异常控制)
  • 反序列化可以破解上面几种((不包含枚举式))实现方式!
  • 可以通过定义readResolve()防止获得不同对象。
package com.bjsxt.singleton;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;

/**
 * 测试反射和反序列化破解单例模式
 * @author 尚学堂高淇 www.sxt.cn
 *
 */
public class Client2 {
	
	public static void main(String[] args) throws Exception {
		SingletonDemo6 s1 = SingletonDemo6.getInstance();
		SingletonDemo6 s2 = SingletonDemo6.getInstance();
		
		System.out.println(s1);
		System.out.println(s2);
		
		//通过反射的方式直接调用私有构造器
//		Class<SingletonDemo6> clazz = (Class<SingletonDemo6>) Class.forName("com.bjsxt.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);发现s3和s4是两个不同的对象。s1和s2是相同的对象
		
		//通过反序列化的方式构造多个对象 
		//序列化
		FileOutputStream fos = new FileOutputStream("d:/a.txt");
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		oos.writeObject(s1);
		oos.close();
		fos.close();
		//反序列化
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt"));
		SingletonDemo6 s3 =  (SingletonDemo6) ois.readObject();
		System.out.println(s3);//s3对象和s1,s2不是同一个对象
		
		
	}
}

解决

package com.bjsxt.singleton;

import java.io.ObjectStreamException;
import java.io.Serializable;

/**
 * 测试懒汉式单例模式(如何防止反射和反序列化漏洞)
 * @author 尚学堂高淇 www.sxt.cn
 *
 */
public class SingletonDemo6 implements Serializable {
	//类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)。
	private static SingletonDemo6 instance;  
	
	private SingletonDemo6(){ //私有化构造器
		if(instance!=null){
			throw new RuntimeException();
		}
	}
	
	//方法同步,调用效率低!
	public static  synchronized SingletonDemo6  getInstance(){
		if(instance==null){
			instance = new SingletonDemo6();
		}
		return instance;
	}
	
	//反序列化时,如果定义了readResolve()则直接返回此方法指定的对象。而不需要单独再创建新对象!
	private Object readResolve() throws ObjectStreamException {
		return instance;
	}
	
}

枚举式

  • 实现简单,枚举本身就是单例模式。由JVM从根本上提供保障!避免通过反射和反序列化的漏洞!
  • 问题:无延迟加载
package com.bjsxt.singleton;

/**
 * 测试枚举式实现单例模式(没有延时加载)
 * @author 尚学堂高淇 www.sxt.cn
 *
 */
public enum SingletonDemo5 {
	
	//这个枚举元素,本身就是单例对象!
	INSTANCE;
	
	//添加自己需要的操作!
	public void singletonOperation(){
	}
	
	
}

测试

package com.bjsxt.singleton;

public class Client {
	
	public static void main(String[] args) {
		SingletonDemo4 s1 = SingletonDemo4.getInstance();
		SingletonDemo4 s2 = SingletonDemo4.getInstance();
		
		System.out.println(s1);
		System.out.println(s2);//s1,s2是同一个对象
		//返回true
		System.out.println(SingletonDemo5.INSTANCE==SingletonDemo5.INSTANCE);
		
		
	}
}

效率

package com.bjsxt.singleton;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.util.concurrent.CountDownLatch;

/**
 * 测试多线程环境下五种创建单例模式的效率
 * @author 尚学堂高淇 www.sxt.cn
 *
 */
public class Client3 {
	
	public static void main(String[] args) throws Exception {
		
		long start = System.currentTimeMillis();
		int threadNum = 10;//定义线程数量
		
		/*
		 同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
		 countDown() 当前线程调此方法,则计数减一(建议放在 finally里执行)
		 await(), 调用此方法会一直阻塞当前线程,直到计时器的值为0
		 */
		final CountDownLatch  countDownLatch = new CountDownLatch(threadNum);
		
		for(int i=0;i<threadNum;i++){
			new Thread(new Runnable() {
				@Override
				public void run() {
					
					for(int i=0;i<1000000;i++){
						//Object o = SingletonDemo4.getInstance();
						Object o = SingletonDemo5.INSTANCE;
					}
					
					countDownLatch.countDown();
				}
			}).start();
		}
		
		countDownLatch.await();	//main线程阻塞,直到计数器变为0,才会继续往下执行!
		
		long end = System.currentTimeMillis();
		System.out.println("总耗时:"+(end-start));
	}
}

总结

实现

  • 主要: • 饿汉式(线程安全,调用效率高。 但是,不能延时加载。) • 懒汉式(线程安全,调用效率不高。 但是,可以延时加载。)
  • 其他: • 双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题。不建议使用) • 静态内部类式(线程安全,调用效率高。 但是,可以延时加载) • 枚举式(线程安全,调用效率高,不能延时加载。并且可以天然的防止反射和反序列化漏洞!)

选用

  • 单例对象 占用 资源 少,不需要 延时加载: • 枚举式 好于 饿汉式
  • 单例对象 占用 资源 大,需要 延时加载: • 静态内部类式 好于 懒汉式

2:工厂模式

工厂分类

  • 工厂模式实现了创建者和调用者的分离
  • 详细分类: • 简单工厂模式 • 工厂方法模式 • 抽象工厂模式
  • 面向对象设计的基本原则
  • OCP(开闭原则,Open-Closed Principle):一个软件的实体应当对扩展开放,对修改关闭。
  • DIP(依赖倒转原则,Dependence Inversion Principle):要针对接口编程,不要针对实现编程。
  • LoD(迪米特法则,Law of Demeter):只与你直接的朋友通信,而避免和陌生人通信。

核心本质

  • 实例化对象,用工厂方法代替new操作
  • 将选择实现类、创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。

工厂模式

  • 简单工厂模式:用来生产同一等级结构中的任意产品。(对于增加新的产品,需要修改已有代码)
  • 工厂方法模式:用来生产同一等级结构中的固定产品。(支持增加任意产品)
  • 抽象工厂模式:用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族)

普通模式

package com.bjsxt.factory.simplefactory;
/**
 * 汽车接口
 * @author Administrator
 *
 */
public interface Car {
	void run();
}

package com.bjsxt.factory.simplefactory;
/**
 * 奥迪汽车类
 * @author Administrator
 *
 */
public class Audi implements Car {

	@Override
	public void run() {
		System.out.println("奥迪再跑!");
	}

}

package com.bjsxt.factory.simplefactory;
/**
 * 比亚迪汽车类
 * @author Administrator
 *
 */
public class Byd implements Car {

	@Override
	public void run() {
		System.out.println("比亚迪再跑!");
	}

}

package com.bjsxt.factory.simplefactory;

/**
 * 测试在没有工厂模式的情况下
 * @author 尚学堂高淇 www.sxt.cn
 *
 */
public class Client01 {   //调用者
	
	public static void main(String[] args) {
		Car c1 = new Audi();
		Car c2 = new Byd();
		
		c1.run();
		c2.run();
		
	}
}

简单工厂

  • 简单工厂模式也叫静态工厂模式,就是工厂类一般是使用静态方法,通过接收的参数的不同来返回不同的对象实例。
  • 对于增加新产品无能为力!不修改代码的话,是无法扩展的。
package com.bjsxt.factory.simplefactory;

/**
 * 简单工厂类
 * @author 尚学堂高淇 www.sxt.cn
 *
 */
public class CarFactory2 {
	
	public static  Car createAudi(){
		return new Audi();
	}
	public static  Car createByd(){
		return new Byd();
	}
	
}

package com.bjsxt.factory.simplefactory;

public class CarFactory {
	
	public static  Car createCar(String type){
		if("奥迪".equals(type)){
			return new Audi();
		}else if("比亚迪".equals(type)){
			return new Byd();
		}else{
			return null;
		}
	}
	
}

package com.bjsxt.factory.simplefactory;

/**
 * 简单工厂情况下
 * @author 尚学堂高淇 www.sxt.cn
 *
 */
public class Client02 {   //调用者
	
	public static void main(String[] args) {
		Car c1 =CarFactory.createCar("奥迪");
		Car c2 = CarFactory.createCar("比亚迪");
		
		c1.run();
		c2.run();
		
	}
}

工厂方法

  • 为了避免简单工厂模式的缺点,不完全满足OCP。
  • 工厂方法模式和简单工厂模式最大的不同在于,简单工厂模式只有一个(对于一个项目或者一个独立模块而言)工厂类,而工厂方法模式有一组实现了相同接口的工厂类。
package com.bjsxt.factory.factorymethod;
/**
 * 汽车创建工厂接口
 * @author Administrator
 *
 */
public interface CarFactory {
	Car createCar();
}

package com.bjsxt.factory.factorymethod;
/**
 * 奥迪生产工厂类,实现了汽车生产工厂
 * @author Administrator
 *
 */
public class AudiFactory implements CarFactory {

	@Override
	public Car createCar() {
		return new Audi();
	}

}

package com.bjsxt.factory.factorymethod;
/**
 * 奔驰生产工厂类,实现了汽车生产工厂
 * @author Administrator
 *
 */
public class BenzFactory implements CarFactory {

	@Override
	public Car createCar() {
		return new Benz();
	}

}

package com.bjsxt.factory.factorymethod;
/**
 * 比亚迪生产工厂类,实现了汽车生产工厂
 * @author Administrator
 *
 */
public class BydFactory implements CarFactory {

	@Override
	public Car createCar() {
		return new Byd();
	}

}
package com.bjsxt.factory.factorymethod;
/**
 * 测试工厂方法
 * @author Administrator
 *
 */
public class Client {
	public static void main(String[] args) {
		Car c1 = new AudiFactory().createCar();
		Car c2 = new BydFactory().createCar();
		
		c1.run();
		c2.run();
	}
}

原理

汽车接口(有run方法)————>奥迪类,奔驰类,比亚迪类

汽车生产工厂接口(有生产汽车方法createCar)----->奥迪生产工厂类,奔驰生产工厂类,比亚迪生产工厂类

比较与建议

  • 结构复杂度: 从这个角度比较,显然简单工厂模式要占优。简单工厂模式只需一个工厂类,而工厂方法模式的工厂类随着产品类个数增加而增加,这无疑会使类的个数越来越多,从而增加了结构的复杂程度。
  • 代码复杂度: 代码复杂度和结构复杂度是一对矛盾,既然简单工厂模式在结构方面相对简洁,那么它在代码方面肯定是比工厂方法模式复杂的了。简单工厂模式的工厂类随着产品类的增加需要增加很多方法(或代码),而工厂方法模式每个具体工厂类只完成单一任务,代码简洁。
  • 客户端编程难度: 工厂方法模式虽然在工厂类结构中引入了接口从而满足了OCP,但是在客户端编码中需要对工厂类进行实例化。而简单工厂模式的工厂类是个静态类,在客户端无需实例化,这无疑是个吸引人的优点。
  • 管理上的难度: 这是个关键的问题。 我们先谈扩展。众所周知,工厂方法模式完全满足OCP,即它有非常良好的扩展性。那是否就说明了简单工厂模式就没有扩展性呢?答案是否定的。简单工厂模式同样具备良好的扩展性——扩展的时候仅需要修改少量的代码(修改工厂类的代码)就可以满足扩展性的要求了。尽管这没有完全满足OCP,但我们不需要太拘泥于设计理论,要知道,sun提供的java官方工具包中也有想到多没有满足OCP的例子啊。然后我们从维护性的角度分析下。假如某个具体产品类需要进行一定的修改,很可能需要修改对应的工厂类。当同时需要修改多个产品类的时候,对工厂类的修改会变得相当麻烦(对号入座已经是个问题了)。反而简单工厂没有这些麻烦,当多个产品类需要修改是,简单工厂模式仍然仅仅需要修改唯一的工厂类(无论怎样都能改到满足要求吧?大不了把这个类重写)。
  • 根据设计理论建议:工厂方法模式。但实际上,我们一般都用简单工厂模式。

抽象工厂

  • 抽象工厂模式用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族)
  • 抽象工厂模式是工厂方法模式的升级版本,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。
package com.bjsxt.factory.abstractFactory;
/**
 * 发动机接口
 * @author Administrator
 *
 */
public interface Engine {
	void run();
	void start();
}

/**
 * 高级发动机
 * @author Administrator
 *
 */
class LuxuryEngine implements Engine{

	@Override
	public void run() {
		System.out.println("转的快!");
	}

	@Override
	public void start() {
		System.out.println("启动快!可以自动启停!");
	}
	
}
/**
 * 低端发动机
 * @author Administrator
 *
 */
class LowEngine implements Engine{
	
	@Override
	public void run() {
		System.out.println("转的慢!");
	}
	
	@Override
	public void start() {
		System.out.println("启动慢!");
	}
	
}
package com.bjsxt.factory.abstractFactory;
/**
 * 座椅接口
 * @author Administrator
 *
 */
public interface Seat {
	void massage();
}
/**
 * 高级座椅类
 * @author Administrator
 *
 */
class LuxurySeat implements Seat {

	@Override
	public void massage() {
		System.out.println("可以自动按摩!");
	}
	
}
/**
 * 低级座椅类
 * @author Administrator
 *
 */
class LowSeat implements Seat {

	@Override
	public void massage() {
		System.out.println("不能按摩!");
	}
	
}

package com.bjsxt.factory.abstractFactory;
/**
 * 轮胎接口
 * @author Administrator
 *
 */
public interface Tyre {
	void revolve();
}
/**
 * 高级轮胎类
 * @author Administrator
 *
 */
class LuxuryTyre implements Tyre {

	@Override
	public void revolve() {
		System.out.println("旋转不磨损!");
	}
	
}
/**
 * 低级轮胎类
 * @author Administrator
 *
 */
class LowTyre implements Tyre {

	@Override
	public void revolve() {
		System.out.println("旋转磨损快!");
	}
	
}
package com.bjsxt.factory.abstractFactory;
/**
 * 汽车创建工厂接口
 * @author Administrator
 *
 */
public interface CarFactory {
	/**
	 * 创建发动机方法
	 * @return
	 */
	Engine createEngine();
	/**
	 * 创建座椅方法
	 * @return
	 */
	Seat createSeat();
	/**
	 * 创建轮胎工厂
	 * @return
	 */
	Tyre createTyre();
}


package com.bjsxt.factory.abstractFactory;
/**
 * 高级汽车生产工厂类
 * @author Administrator
 *
 */
public class LuxuryCarFactory implements CarFactory {

	@Override
	public Engine createEngine() {
		return new LuxuryEngine();
	}

	@Override
	public Seat createSeat() {
		return new LuxurySeat();
	}

	@Override
	public Tyre createTyre() {
		return new LuxuryTyre();
	}


}

package com.bjsxt.factory.abstractFactory;
/**
 * 低级汽车创建类
 * @author Administrator
 *
 */
public class LowCarFactory implements CarFactory {

	@Override
	public Engine createEngine() {
		return new LowEngine();
	}

	@Override
	public Seat createSeat() {
		return new LowSeat();
	}

	@Override
	public Tyre createTyre() {
		return new LowTyre();
	}


}

package com.bjsxt.factory.abstractFactory;
/**
 * 测试抽象工厂
 * @author Administrator
 *
 */
public class Client {

	public static void main(String[] args) {
		//用汽车工厂接口创建高级汽车工厂类
		CarFactory  factory = new LuxuryCarFactory();
		//用汽车工厂创建一个发动机,这个发动机就是高级发动机
		Engine e = factory.createEngine();
		e.run();
		e.start();
	}
}

原理

发动机接口(有run和start方法)--->高级发动机类,低级发动机类

座椅接口(有massage方法)--->高级座椅类,低级座椅类

轮胎接口(有revolve方法)--->高级轮胎类,低级轮胎类

汽车创建工厂接口(有创建发动机,座椅,轮胎方法)--->高级汽车生产工厂类,低级汽车生产工厂类

总结

  • 简单工厂模式(静态工厂模式):虽然某种程度不符合设计原则,但实际使用最多。
  • 工厂方法模式:不修改已有类的前提下,通过增加新的工厂类实现扩展。
  • 抽象工厂模式:不可以增加产品,可以增加产品族!

应用场景

  • JDK中Calendar的getInstance方法
  • JDBC中Connection对象的获取
  • Hibernate中SessionFactory创建Session
  • spring中IOC容器创建管理bean对象
  • XML解析时的DocumentBuilderFactory创建解析器对象
  • 反射中Class对象的newInstance()

3:建造者模式

场景和本质

场景

  • 我们要建造一个复杂的产品。比如:神州飞船,Iphone。这个复杂的产品的创建。有这样一个问题需要处理:装配这些子组件是不是有个步骤问题?
  • 实际开发中,我们所需要的对象构建时,也非常复杂,有很多步骤需要处理时。

本质

  • 分离了对象子组件的单独构造(由Builder来负责)和装配(由Director负责)。 从而可以构造出复杂的对象。这个模式适用于:某个对象的构建过程复杂的情况下使用。
  • 由于实现了构建和装配的解耦。不同的构建器,相同的装配,也可以做出不同的对象;相同的构建器,不同的装配顺序也可以做出不同的对象。也就是实现了构建算法、装配算法的解耦,实现了更好的复用。
  • 建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象
  • 意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示
  • 何时使用:一些基本部件不会变,而其组合经常变化的时候(飞船可以用不同品牌的零部件构造而成)
  • 优点: 1、建造者独立,易扩展。 2、便于控制细节风险
  • 缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类
  • 使用场景: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖
  • 注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序

案例

  • builder(抽象建造者):给出一个抽象结论,以规范产品对象的各个组成成分的建造。这个接口规定要实现复杂对象的那些部分的创建,并不涉及具体的对象部件的创建。
  • ConcreteBuilder(具体建造者):实现Builder接口,针对不同的商业逻辑,具体化复杂对象的各部分的创建。在构造过程完成后,提供产品的实例
  • Director(指导者):调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建
  • Product(产品类):要创建的复杂对象
package com.bjsxt.builder;

/**
 * 宇宙飞船类
 * @author 尚学堂高淇 www.sxt.cn
 *
 */
public class AirShip {
	
	private OrbitalModule orbitalModule;  //轨道舱
	private Engine engine; //发动机
	private EscapeTower escapeTower;  //逃逸塔
	
	
	public void launch(){
		System.out.println("发射!");
	}
	
	
	
	public OrbitalModule getOrbitalModule() {
		return orbitalModule;
	}
	public void setOrbitalModule(OrbitalModule orbitalModule) {
		this.orbitalModule = orbitalModule;
	}
	public Engine getEngine() {
		return engine;
	}
	public void setEngine(Engine engine) {
		this.engine = engine;
	}
	public EscapeTower getEscapeTower() {
		return escapeTower;
	}
	public void setEscapeTower(EscapeTower escapeTower) {
		this.escapeTower = escapeTower;
	}
	
	
	
}
/**
 * 轨道舱类
 * @author Administrator
 *
 */
class OrbitalModule{
	/**
	 * 轨道舱名称
	 */
	private String name;

	public OrbitalModule(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
}
/**
 * 发动机名称
 * @author Administrator
 *
 */
class Engine {
	/**
	 * 发动机名称
	 */
	private String name;

	public Engine(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	
}
/**
 * 逃逸塔类
 * @author Administrator
 *
 */
class EscapeTower{
	/**
	 * 逃逸塔名称
	 */
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public EscapeTower(String name) {
		super();
		this.name = name;
	}
	
}



package com.bjsxt.builder;
/**
 * 飞船组装接口
 * @author Administrator
 *
 */
public interface AirShipDirector {
	
	/**
	 * 组装飞船对象
	 * @return
	 */
	AirShip   directAirShip();
	
}

package com.bjsxt.builder;

/**
 * 宇宙飞船构造接口
 * @author Administrator
 *
 */
public interface AirShipBuilder {
	/**
	 * 构造发动机
	 * @return
	 */
	Engine builderEngine();
	/**
	 * 构造轨道舱
	 * @return
	 */
	OrbitalModule builderOrbitalModule();
	/**
	 * 构造逃逸塔
	 * @return
	 */
	EscapeTower  builderEscapeTower();
}

package com.bjsxt.builder;


public class SxtAirShipBuilder implements AirShipBuilder {
	//StringBuilder, 以后学习XML解析中,JDOM库中的类:DomBuilder,SaxBuilder
	@Override
	public Engine builderEngine() {
		System.out.println("构建尚学堂牌发动机!");
		return new Engine("尚学堂牌发动机!");
	}

	@Override
	public EscapeTower builderEscapeTower() {
		
		System.out.println("构建逃逸塔");
		return new EscapeTower("尚学堂牌逃逸塔");
	}

	@Override
	public OrbitalModule builderOrbitalModule() {
		System.out.println("构建轨道舱");
		return new OrbitalModule("尚学堂牌轨道舱");
	}	
	
}

package com.bjsxt.builder;

public class SxtAirshipDirector implements AirShipDirector {

	private AirShipBuilder builder;

	public SxtAirshipDirector(AirShipBuilder builder) {
		this.builder = builder;
	}


	@Override
	public AirShip directAirShip() {
		Engine e = builder.builderEngine();
		OrbitalModule o = builder.builderOrbitalModule();
		EscapeTower et = builder.builderEscapeTower();
		
		//装配成飞船对象
		AirShip ship = new AirShip();
		ship.setEngine(e);
		ship.setEscapeTower(et);
		ship.setOrbitalModule(o);
		
		return ship;
	}

}

package com.bjsxt.builder;

public class Client {
	public static void main(String[] args) {
		
		AirShipDirector director = new SxtAirshipDirector(new SxtAirShipBuilder());

		AirShip ship = director.directAirShip();
		
		System.out.println(ship.getEngine().getName());
		
		ship.launch();
		
	}
}

原理

飞船类(有轨道舱,发动机,逃逸塔属性),轨道舱类(有名称属性),发动机类(有名称属性),逃逸塔类(有名称属性)

飞船构造接口(有构造发动机,轨道舱,逃逸塔方法)--->尚学堂飞船构造类

飞船组装接口(有组装飞船对象的方法)--->尚学堂飞船组装类(使用了飞船构造接口)

应用场景

  • StringBuilder类的append方法:StringBuilder把构建者的角色交给了其的父类AbstractStringBuilder,最终调用的是父类的append()
  • SQL中的PreparedStatement
  • JDOM中,DomBuilder、SAXBuilder

4:原型模式

场景

  • 思考一下:克隆技术是怎么样的过程? 克隆羊多利大家还记得吗?
  • javascript语言中的,继承怎么实现?那里面也有prototype
  • 原型模式叫prototype

介绍

  • 通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
  • 就是java中的克隆技术,以某个对象为原型,复制出新的对象。显然,新的对象具备原型对象的特点
  • 优势有:效率高(直接克隆,避免了重新执行构造过程步骤)、“接口造接口”。
  • 克隆类似于new,但是不同于new。new创建新的对象属性采用的是默认值。克隆出的对象的属性值完全和原型对象相同。并且克隆出的新对象改变不会影响原型对象。然后,再修改克隆对象的值。

实现方式

  • Cloneable接口和clone方法
  • Prototype模式中实现起来最困难的地方就是内存复制操作,所幸在Java中提供了clone()方法替我们做了绝大部分事情。

浅克隆

  • 问题:被复制的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都 仍然指向原来的对象。
package com.bjsxt.prototype;

import java.io.Serializable;
import java.util.Date;
/**
 * 测试浅复制
 * @author Administrator
 *
 */
public class Sheep implements Cloneable,Serializable {   //1997,英国的克隆羊,多利!
	private String sname;
	private Date birthday;
	
	/**
	 * Object类中的克隆方法
	 */
	@Override
	protected Object clone() throws CloneNotSupportedException {
		//直接调用object对象的clone()方法!
		Object obj = super.clone();  
		return obj;
	}


	public String getSname() {
		return sname;
	}


	public void setSname(String sname) {
		this.sname = sname;
	}


	public Date getBirthday() {
		return birthday;
	}


	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}


	public Sheep(String sname, Date birthday) {
		super();
		this.sname = sname;
		this.birthday = birthday;
	}
	
	public Sheep() {
	}
	
}

package com.bjsxt.prototype;

import java.util.Date;

/**
 * 测试原型模式(浅克隆)
 * @author 尚学堂高淇 www.sxt.cn
 *
 */
public class Client {
	public static void main(String[] args) throws Exception {
		Date date = new Date(12312321331L);
		Sheep s1 = new Sheep("少利",date);
		System.out.println(s1);
		System.out.println(s1.getSname());
		System.out.println(s1.getBirthday());
		//修改时间属性
		date.setTime(23432432423L);
		//显示修改后的时间
		System.out.println(s1.getBirthday());
		//s2通过克隆s1得到,其中时间属性显示的是最新的修改之后的时间
		Sheep s2 = (Sheep) s1.clone();
		s2.setSname("多利");
		System.out.println("克隆之后");
		System.out.println(s2);
		System.out.println(s2.getSname());
		System.out.println(s2.getBirthday());
		
		
	}
}

com.bjsxt.prototype.Sheep@65690726
少利
Sat May 23 20:05:21 CST 1970
Tue Sep 29 13:00:32 CST 1970
克隆之后
com.bjsxt.prototype.Sheep@4ce86da0
多利
Tue Sep 29 13:00:32 CST 1970

深克隆

  • 深克隆把引用的变量指向复制过的新对象,而不是原有的被引用的对象。
  • 深克隆:让已实现Clonable接口的类中的属性也实现Clonable接口
  • 基本数据类型能够自动实现深度克隆(值的复制)
  • 有时候增加克隆的代码比较麻烦!
  • 可以利用序列化和反序列化实现深克隆!
package com.bjsxt.prototype;

import java.util.Date;


//测试深复制
public class Sheep2 implements Cloneable {   //1997,英国的克隆羊,多利!
	private String sname;
	private Date birthday;
	
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		//直接调用object对象的clone()方法!
		Object obj = super.clone();  
		
		//添加如下代码实现深复制(deep Clone)
		Sheep2 s = (Sheep2) obj;
        //把属性也进行克隆!
		s.birthday = (Date) this.birthday.clone();  
		return obj;
	}


	public String getSname() {
		return sname;
	}


	public void setSname(String sname) {
		this.sname = sname;
	}


	public Date getBirthday() {
		return birthday;
	}


	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}


	public Sheep2(String sname, Date birthday) {
		super();
		this.sname = sname;
		this.birthday = birthday;
	}
	
	public Sheep2() {
	}
	
}

package com.bjsxt.prototype;

import java.util.Date;

/**
 * 原型模式(深复制)
 * @author 尚学堂高淇www.sxt.cn
 *
 */
public class Client2 {
	public static void main(String[] args) throws CloneNotSupportedException {
		Date date = new Date(12312321331L);
		Sheep2 s1 = new Sheep2("少利",date);
		//实现深复制。s2对象的birthday是一个新对象!
		Sheep2 s2 = (Sheep2) s1.clone();   
		
		System.out.println(s1);
		System.out.println(s1.getSname());
		System.out.println(s1.getBirthday());
		//修改时间值
		date.setTime(23432432423L);
		System.out.println(s1.getBirthday());
		
		System.out.println("深克隆之后的对象,时间属性显示为未修改之前的时间");
		s2.setSname("多利");
		System.out.println(s2);
		System.out.println(s2.getSname());
		System.out.println(s2.getBirthday());

	}
}

com.bjsxt.prototype.Sheep2@65690726
少利
Sat May 23 20:05:21 CST 1970
Tue Sep 29 13:00:32 CST 1970
深克隆之后的对象,时间属性显示为未修改之前的时间
com.bjsxt.prototype.Sheep2@4ce86da0
多利
Sat May 23 20:05:21 CST 1970

使用序列化

package com.bjsxt.prototype;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;

/**
 * 原型模式(深复制,使用序列化和反序列化的方式实现深复制)
 * @author 尚学堂高淇www.sxt.cn
 *
 */
public class Client3 {
	public static void main(String[] args) throws CloneNotSupportedException, Exception {
		Date date = new Date(12312321331L);
		Sheep s1 = new Sheep("少利",date);
		System.out.println(s1);
		System.out.println(s1.getSname());
		System.out.println(s1.getBirthday());

		
		//使用序列化和反序列化实现深复制
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ObjectOutputStream    oos = new ObjectOutputStream(bos);
		oos.writeObject(s1);
		byte[] bytes = bos.toByteArray();
		
		ByteArrayInputStream  bis = new ByteArrayInputStream(bytes);
		ObjectInputStream	  ois = new ObjectInputStream(bis);
		
		Sheep s2 = (Sheep) ois.readObject();   //克隆好的对象!
		
		System.out.println("修改原型对象的属性值");  
		date.setTime(23432432423L);
		
		System.out.println(s1.getBirthday());
		System.out.println("经过序列化和反序列化之后的对象:");
		s2.setSname("多利");
		System.out.println(s2);
		System.out.println(s2.getSname());
		System.out.println(s2.getBirthday());
		
		
	}
}

少利
Sat May 23 20:05:21 CST 1970
修改原型对象的属性值
Tue Sep 29 13:00:32 CST 1970
经过序列化和反序列化之后的对象:
com.bjsxt.prototype.Sheep@71f6f0bf
多利
Sat May 23 20:05:21 CST 1970

性能测试

package com.bjsxt.prototype;

/**
 * 测试普通new方式创建对象和clone方式创建对象的效率差异!
 * 如果需要短时间创建大量对象,并且new的过程比较耗时。则可以考虑使用原型模式!
 * @author 尚学堂高淇 www.sxt.cn
 *
 */
public class Client4 {
	
	public static void testNew(int size){
		long start = System.currentTimeMillis();
		for(int i=0;i<size;i++){
			Laptop t = new Laptop();
		}
		long end = System.currentTimeMillis();
		System.out.println("new的方式创建耗时:"+(end-start));
	}
	
	public static void testClone(int size) throws CloneNotSupportedException{
		long start = System.currentTimeMillis();
		Laptop t = new Laptop();
		for(int i=0;i<size;i++){
			Laptop temp = (Laptop) t.clone();
		}
		long end = System.currentTimeMillis();
		System.out.println("clone的方式创建耗时:"+(end-start));
	}
	
	
	public static void main(String[] args) throws Exception {	
		testNew(1000);
		testClone(1000);
	}
}


class Laptop implements Cloneable {  //笔记本电脑
	public Laptop() {
		try {
			Thread.sleep(10);  //模拟创建对象耗时的过程!
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		Object obj = super.clone();  //直接调用object对象的clone()方法!
		return obj;
	}
	
}
new的方式创建耗时:10071
clone的方式创建耗时:10

应用场景

  • 原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。
  • spring中bean的创建实际就是两种:单例模式和原型模式。(当然,原型模式需要和工厂模式搭配起来)

总结

  • 创建型模式:都是用来帮助我们创建对象的!
  • 单例模式:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。
  • 工厂模式 • 简单工厂模式 – 用来生产同一等级结构中的任意产品。(对于增加新的产品,需要修改已有代码) • 工厂方法模式 – 用来生产同一等级结构中的固定产品。(支持增加任意产品) • 抽象工厂模式 – 用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族)
  • 建造者模式 • 分离了对象子组件的单独构造(由Builder来负责)和装配(由Director负责)。 从而可 以构造出复杂的对象。
  • 原型模式 • 通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式

5:代理模式

核心作用

  • 通过代理,控制对对象的访问!Proxy pattern
  • 可以详细控制访问某个(某类)对象的方法,在调用这个方法前做前置处理,调用这个方法后做后置处理。(即:AOP的微观实现!)
  • AOP(Aspect Oriented Programming面向切面编程)的核心实现机制!

核心角色

  • 抽象角色:定义代理角色和真实角色的公共对外方法
  • 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用,关注真正的业务逻辑
  • 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加 自己的操作。–将统一的流程控制放到代理角色中处理!

应用场景

  • 安全代理:屏蔽对真实角色的直接访问
  • 远程代理:通过代理类处理远程方法调用(RMI)
  • 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象

分类

  • 静态代理(静态定义代理类)

  • 动态代理(动态生成代理类)

    •JDK自带的动态代理

    •javaassist字节码操作库实现

    •CGLIB

    •ASM(底层使用指令,可维护性较差)

静态代理

  • static proxy
package com.bjsxt.proxy.staticProxy;
/**
 * 明星接口
 * @author Administrator
 *
 */
public interface Star {
	/**
	 * 面谈
	 */
	void confer();
	/**
	 * 签合同
	 */
	void signContract();
	/**
	 * 订票
	 */
	void bookTicket();
	/**
	 * 唱歌
	 */
	void sing();
	/**
	 * 收钱
	 */
	void collectMoney();
}

package com.bjsxt.proxy.staticProxy;
/**
 * 真实角色,实现了明星接口
 * @author Administrator
 *
 */
public class RealStar implements Star {

	@Override
	public void bookTicket() {
		System.out.println("RealStar.bookTicket()");
	}

	@Override
	public void collectMoney() {
		System.out.println("RealStar.collectMoney()");
	}

	@Override
	public void confer() {
		System.out.println("RealStar.confer()");
	}

	@Override
	public void signContract() {
		System.out.println("RealStar.signContract()");
	}

	@Override
	public void sing() {
		System.out.println("RealStar(周杰伦本人).sing()");
	}

}

package com.bjsxt.proxy.staticProxy;
/**
 * 代理人(含有真实角色属性,完成代理人不能完成的工作),实现了明星接口
 * @author Administrator
 *
 */
public class ProxyStar implements Star {
	
	private Star star;
	
	public ProxyStar(Star star) {
		super();
		this.star = star;
	}

	@Override
	public void bookTicket() {
		System.out.println("ProxyStar.bookTicket()");
	}

	@Override
	public void collectMoney() {
		System.out.println("ProxyStar.collectMoney()");
	}

	@Override
	public void confer() {
		System.out.println("ProxyStar.confer()");
	}

	@Override
	public void signContract() {
		System.out.println("ProxyStar.signContract()");
	}

	@Override
	public void sing() {
		star.sing();
	}

}

package com.bjsxt.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();
		
	}
}

ProxyStar.confer()
ProxyStar.signContract()
ProxyStar.bookTicket()
RealStar(周杰伦本人).sing()
ProxyStar.collectMoney()

原理

明星接口---》真实角色,代理角色(包含了真实角色)
客户---》代理角色(做一些无关紧要的事)---》真实角色(若代理角色无法做到的事,代理角色就调用真实角色)

动态代理

  • dynamic proxy

jdk自带

  • JDK自带的动态代理
  • –java.lang.reflect.Proxy:作用:动态生成代理类和对象
  • –java.lang.reflect.InvocationHandler(处理器接口):可以通过invoke方法实现对真实角色的代理访问。每次通过Proxy生成代理类对象对象时都要指定对应的处理器对象
package com.bjsxt.proxy.dynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
 * 动态代理处理器
 * @author Administrator
 *
 */
public class StarHandler implements InvocationHandler {
	
	Star realStar;
	
	public StarHandler(Star realStar) {
		super();
		this.realStar = realStar;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		Object object = null;
		
		System.out.println("真正的方法执行前!");
		System.out.println("面谈,签合同,预付款,订机票");
		
		if(method.getName().equals("sing")){
			object = method.invoke(realStar, args);
		}
		
		System.out.println("真正的方法执行后!");
		System.out.println("收尾款");
		return object;
	}

}

package com.bjsxt.proxy.dynamicProxy;

import java.lang.reflect.Proxy;

public class Client {
	public static void main(String[] args) {
		
		Star realStar = new RealStar();
		StarHandler handler = new StarHandler(realStar);
		//jdk动态生成代理类和对象
		Star proxy = (Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), 
				new Class[]{Star.class}, handler);
		
		proxy.sing();
		
	}
	
}
真正的方法执行前!
面谈,签合同,预付款,订机票
RealStar(周杰伦本人).sing()
真正的方法执行后!
收尾款

原理

1:通过jdk自带的反射动态生成代理类和对象
2:当调用对象的方法时会进入到动态代理处理器的invoke方法中,如果方法名为真实对象需要调用的,就返回真实对象的方法结果

优点

  • 抽象角色中(接口)声明的所以方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。

应用场景

  • struts2中拦截器的实现
  • 数据库连接池关闭处理
  • Hibernate中延时加载的实现
  • mybatis中实现拦截器插件
  • AspectJ的实现
  • spring中AOP的实现(•日志拦截•声明式事务处理)
  • web service
  • RMI远程方法调用
  • 实际上,随便选择一个技术框架都会用到代理模式!!

6:桥接模式

核心要点

  • 处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继承结构,使各个维度可以独立的扩展在抽象层建立关联

案例

package com.bjsxt.bridge;

public interface Computer {
	void sale();
}

class Desktop implements Computer {
	@Override
	public void sale() {
		System.out.println("销售台式机!");
	}
}

class Laptop implements Computer {
	@Override
	public void sale() {
		System.out.println("销售笔记本!");
	}
}
class Pad implements Computer {
	@Override
	public void sale() {
		System.out.println("销售平板电脑!");
	}
}

class LenovoDesktop extends Desktop {
	@Override
	public void sale() {
		System.out.println("销售联想台式机");
	}
}
class LenovoLaptop extends Laptop {
	@Override
	public void sale() {
		System.out.println("销售联想笔记本");
	}
}
class LenovoPad extends Pad {
	@Override
	public void sale() {
		System.out.println("销售联想平板电脑");
	}
}



class ShenzhouDesktop extends Desktop {
	@Override
	public void sale() {
		System.out.println("销售神舟台式机");
	}
}
class ShenzhouLaptop extends Laptop {
	@Override
	public void sale() {
		System.out.println("销售神舟笔记本");
	}
}
class ShenzhouPad extends Pad {
	@Override
	public void sale() {
		System.out.println("销售神舟平板电脑");
	}
}


class DellDesktop extends Desktop {
	@Override
	public void sale() {
		System.out.println("销售戴尔台式机");
	}
}
class DellLaptop extends Laptop {
	@Override
	public void sale() {
		System.out.println("销售戴尔笔记本");
	}
}
class DellPad extends Pad {
	@Override
	public void sale() {
		System.out.println("销售戴尔平板电脑");
	}
}



package com.bjsxt.bridge;

/**
 * 品牌
 * @author Administrator
 *
 */
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电脑");
	}
	
}

class Shenzhou implements Brand {
	
	@Override
	public void sale() {
		System.out.println("销售神舟电脑");
	}
	
}

package com.bjsxt.bridge;

/**
 * 电脑类型的维度
 * @author Administrator
 *
 */
public class Computer2 {
	
	protected Brand brand;
	
	public Computer2(Brand b) {
		this.brand = b;
	}
	
	public void sale(){
		brand.sale();
	}
	
}

class Desktop2 extends Computer2 {

	public Desktop2(Brand b) {
		super(b);
	}
	
	@Override
	public void sale() {
		super.sale();
		System.out.println("销售台式机");
	}
}

class Laptop2 extends Computer2 {
	
	public Laptop2(Brand b) {
		super(b);
	}
	
	@Override
	public void sale() {
		super.sale();
		System.out.println("销售笔记本");
	}
}

package com.bjsxt.bridge;

public class Client {
	public static void main(String[] args) {
		//销售联想的笔记本电脑
		Computer2  c = new Laptop2(new Lenovo());
		c.sale();
		
		//销售神舟的台式机
		Computer2 c2 = new Desktop2(new Shenzhou());
		c2.sale();
		
		
	}
}

原理

使用传统的继承模式:
	定义电脑接口-->台式,笔记本,平板实现接口-->联想台式,笔记本,平板继承台式,笔记本,平板类
传统模式问题:
	类个数膨胀问题
	如果要增加一个新的电脑类型:智能手机,则要增加各个品牌下面的类
	如果要增加一个新的品牌,也要增加各种电脑类型的类
	违反单一职责原则:一个类:联想笔记本,有两个引起这个类变化的原因
	
	
使用桥接模式:
	定义品牌接口-->联想实现接口
	台式,笔记本-->继承电脑类型维度类(包含品牌类)
	Computer2  c = new Laptop2(new Lenovo());

总结

  • 桥接模式可以取代多层继承的方案。多层继承违背了单一职责原则,复用性较差,类的个数也非常多。桥接模式可以极大的减少子类的个数,从而降低管理和维护的成本。
  • 桥接模式极大的提高了系统可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有的系统,符合开闭原则。
  • 就像一个桥,将两个变化维度连接起来。各个维度都可以独立的变化。故称之为:桥模式

应用场景

  • JDBC驱动程序
  • –AWT中的Peer架构
  • 银行日志管理(格式分类:操作日志、交易日志、异常日志)
  • 人力资源系统中的奖金计算模块(奖金分类:个人奖金、团体奖金、激励奖金。)
  • OA系统中的消息处理(业务类型:普通消息、加急消息、特急消息)

7:适配器模式

结构型模式

核心作用

  • 核心作用:是从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题。

分类

  • 适配器模式:使原本由于接口不兼容不能一起工作的类可以一起工作
  • 代理模式:为真实对象提供一个代理,从而控制对真实对象的访问
  • 桥接模式:处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继承结构,使各个维度可以独立的扩展在抽象层建立关联。
  • 装饰模式:动态地给一个对象添加额外的功能,比继承灵活
  • 组合模式:将对象组合成树状结构以表示”部分和整体”层次结构,使得客户可以统一的调用叶子对象和容器对象
  • 外观模式:为子系统提供统一的调用接口,使得子系统更加容易使用
  • 享元模式:运用共享技术有效的实现管理大量细粒度对象,节省内存,提高效率

适配模式

概念

  • 适配器模式是将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作

角色

  • 目标接口(Target):客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口
  • 需要适配的类(Adaptee):需要适配的类或适配者类
  • 适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口

案例

被适配类

package com.bjsxt.adapter;

/**
 * 被适配的类
 * (相当于例子中的,PS/2键盘)
 * @author Administrator
 *
 */
public class Adaptee {
	
	public void request(){
		System.out.println("可以完成客户请求的需要的功能!");
	}
}

适配器

package com.bjsxt.adapter;

/**
 * 适配器 (类适配器方式)
 * (相当于usb和ps/2的转接器)
 * @author Administrator
 *
 */
public class Adapter extends Adaptee implements Target {
	
	
	@Override
	public void handleReq() {
		super.request();
	}
	
}

package com.bjsxt.adapter;

/**
 * 适配器 (对象适配器方式,使用了组合的方式跟被适配对象整合)
 * (相当于usb和ps/2的转接器)
 * @author Administrator
 *
 */
public class Adapter2  implements Target {
	
	private Adaptee adaptee;
	
	@Override
	public void handleReq() {
		adaptee.request();
	}

	public Adapter2(Adaptee adaptee) {
		super();
		this.adaptee = adaptee;
	}
	
}

目标接口

package com.bjsxt.adapter;
/**
 * 目标接口
 * @author Administrator
 *
 */
public interface Target {
	void handleReq();
}

客户端

package com.bjsxt.adapter;

/**
 * 客户端类
 * (相当于例子中的笔记本,只有USB接口)
 * @author Administrator
 *
 */
public class Client {
	
	public void test1(Target t){
		t.handleReq();
	}
	
	public static void main(String[] args) {
		Client  c = new Client();
		
		Adaptee a = new Adaptee();
		
//		Target t = new Adapter();

		Target t = new Adapter2(a);
		
		c.test1(t);
		
	}
	
}

原理

客户端--->适配器(实现了目标接口,包含被适配对象)

使用场景

  • 经常用来做旧系统改造和升级
  • 如果我们的系统开发之后再也不需要维护,那么很多模式都是没必要的,但是不幸的是,事实却是维护一个系统的代价往往是开发一个系统的数倍
  • –java.io.InputStreamReader(InputStream)
  • –java.io.OutputStreamWriter(OutputStream)

8:外观模式

迪米特法则

  • 最少知识原则:一个软件实体应当尽可能少的与其他实体发生相互作用

核心

  • 为子系统提供统一的入口。封装子系统的复杂性,便于客户端调用

案例

  • 注册公司流程

工商局

package com.bjsxt.facade;

public interface 工商局  {
	void checkName();  //核名
}

class 海淀区工商局 implements 工商局 {

	@Override
	public void checkName() {
		System.out.println("检查名字是否有冲突!");
	}

}

税务局

package com.bjsxt.facade;

public interface 税务局 {
	void taxCertificate();  //办理税务登记证
}


class 海淀税务局 implements 税务局 {

	@Override
	public void taxCertificate() {
		System.out.println("在海淀税务局办理税务登记证!");
	}

}

####银行

package com.bjsxt.facade;

public interface 银行 {
	void  openAccount();  //开户
}


class 中国工商银行 implements 银行 {

	@Override
	public void openAccount() {
		System.out.println("在中国工商银行开户!");
	}

}

质检局

package com.bjsxt.facade;

public interface 质检局 {
	void  orgCodeCertificate();  //办理组织机构代码证
}



class 海淀质检局 implements 质检局 {

	@Override
	public void orgCodeCertificate() {
		System.out.println("在海淀区质检局办理组织机构代码证!");
	}

}

外观对象

package com.bjsxt.facade;

/**
 * 办理注册公司流程的门面对象
 * @author Administrator
 *
 */
public class RegisterFacade {
	public void register(){
		工商局  a = new 海淀区工商局();
		a.checkName();
		质检局 b = new 海淀质检局();
		b.orgCodeCertificate();
		税务局  c  = new 海淀税务局();
		c.taxCertificate();
		银行  d = new 中国工商银行();
		d.openAccount();
	}
}

测试

package com.bjsxt.facade;

public class Client1 {
	public static void main(String[] args) {
//		工商局  a = new 海淀区工商局();
//		a.checkName();
//		质检局 b = new 海淀质检局();
//		b.orgCodeCertificate();
//		税务局  c  = new 海淀税务局();
//		c.taxCertificate();
//		银行  d = new 中国工商银行();
//		d.openAccount();
		
		new RegisterFacade().register();
		
	}
}

使用场景

  • JDBC封装后的,commons提供的DBUtils类
  • Hibernate提供的工具类
  • Spring JDBC工具类

9:享元模式

场景

  • 内存属于稀缺资源,不要随便浪费。如果有很多个完全相同或相似的对象,我们可以通过享元模式,节省内存

核心

  • 享元模式以共享的方式高效地支持大量细粒度对象的重用
  • 享元对象能做到共享的关键是区分了内部状态和外部状态
  • 内部状态:可以共享,不会随环境变化而改变
  • 外部状态:不可以共享,会随环境变化而改变

案例

  • 每个围棋棋子都是一个对象
  • 颜色,形状,大小这些是可以共享的,称之为:内部状态
  • 位置这些不可以共享,称之为:外部状态

实现

  • FlyweightFactory享元工厂类:创建并管理享元对象,享元池一般设计成键值对
  • FlyWeight抽象享元类:通常是一个接口或抽象类,声明公共方法,这些方法可以向外界提供对象的内部状态,设置外部状态
  • ConcreteFlyWeight具体享元类:为内部状态提供成员变量进行存储
  • UnsharedConcreteFlyWeight非共享享元类:不能被共享的子类可以设计为非共享享元类

享元类

package com.bjsxt.flyweight;

/**
 * 享元接口
 * @author Administrator
 *
 */
public interface ChessFlyWeight {
	/**
	 * 设置棋子颜色
	 * @param c
	 */
	void setColor(String c);
	/**
	 * 获取棋子颜色
	 * @return
	 */
	String getColor();
	/**
	 * 设置棋子位置
	 * @param c
	 */
	void display(Coordinate c);
}

/**
 * 具体享元类(真实棋子),实现了抽象享元接口
 * @author Administrator
 *
 */
class ConcreteChess implements ChessFlyWeight {

	private String color;
	
	public ConcreteChess(String color) {
		super();
		this.color = color;
	}

	@Override
	public void display(Coordinate c) {
		System.out.println("棋子颜色:"+color);
		System.out.println("棋子位置:"+c.getX()+"----"+c.getY());
	}

	@Override
	public String getColor() {
		return color;
	}

	@Override
	public void setColor(String c) {
		this.color = c;
	}
	
}

享元工厂

package com.bjsxt.flyweight;

import java.util.HashMap;
import java.util.Map;

/**
 * 享元工厂类
 * @author Administrator
 *
 */
public class ChessFlyWeightFactory {
	//享元池
	private static Map<String,ChessFlyWeight> map = new HashMap<String, ChessFlyWeight>();
	/**
	 * 根据颜色获取棋子
	 * @param color
	 * @return
	 */
	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.bjsxt.flyweight;

/**
 * 外部状态UnSharedConcreteFlyWeight
 * @author Administrator
 *
 */
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.bjsxt.flyweight;

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));
		
		
	}
}

com.bjsxt.flyweight.ConcreteChess@6af62373
com.bjsxt.flyweight.ConcreteChess@6af62373
增加外部状态的处理===========
棋子颜色:黑色
棋子位置:10----10
棋子颜色:黑色
棋子位置:20----20

原理

真实棋子(包含颜色和位置)--->实现棋子接口
外部状态:位置
享元工厂--->使用map存储真实棋子对象
测试:从享元工厂获取对应颜色的棋子--->然后设置外部状态--->生成唯一的棋子

应用场景

  • 享元模式由于其共享的特性,可以在任何“池”中操作,比如:线程池、数据库连接池
  • String类的设计也是享元模式

优点

  • 极大减少内存中对象的数量
  • 相同或相似对象内存中只存一份,极大的节约资源,提高系统性能
  • 外部状态相对独立,不影响内部状态

缺点

  • 模式较复杂,使程序逻辑复杂化
  • 为了节省内存,共享了内部状态,分离出外部状态,而读取外部状态使运行时间变长。用时间换取了空间

结构模型总结

  • 代理模式:为真实对象提供一个代理,从而控制对真实对象的访问
  • 适配模式:使原本由于接口不兼容不能一起工作的类可以一起工作
  • 桥接模式:处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继承结构,使各个维度可以独立的扩展在抽象层建立关联
  • 组合模式:将对象组合成树状结构以表示”部分和整体”层次结构,使得客户可以统一的调用叶子对象和容器对象
  • 装饰模式:动态地给一个对象添加额外的功能,比继承灵活
  • 外观模式:为子系统提供统一的调用接口,使得子系统更加容易使用
  • 享元模式:运用共享技术有效的实现管理大量细粒度对象,节省内存,提高效率

10:装饰模式

职责

  • 动态的为一个对象增加新的功能
  • 装饰模式是一种用于代替继承的技术,无须通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀

实现细节

  • Component抽象构件角色:真实对象和装饰对象有相同的接口。这样,客户端对象就能够以与真实对象相同的方式同装饰对象交互
  • ConcreteComponent 具体构件角色(真实对象):•io流中的FileInputStream、FileOutputStream
  • Decorator装饰角色:持有一个抽象构件的引用。装饰对象接受所有客户端的请求,并把这些请求转发给真实的对象。这样,就能在真实对象调用前后增加新的功能
  • ConcreteDecorator具体装饰角色:负责给构件对象增加新的责任

案例

抽象构建

package com.bjsxt.decorator;

/**
 * 抽象构建
 * @author Administrator
 *
 */
public interface ICar {
	void move();
}

//ConcreteComponent 具体构件角色(真实对象)
class Car implements ICar {
	@Override
	public void move() {
		System.out.println("陆地上跑!");
	}
}

//Decorator装饰角色
class SuperCar implements ICar {
	protected ICar car;
	public SuperCar(ICar car) {
		super();
		this.car = car;
	}

	@Override
	public void move() {
		car.move();
	}
}

//ConcreteDecorator具体装饰角色
class FlyCar extends SuperCar {

	public FlyCar(ICar car) {
		super(car);
	}
	
	public void fly(){
		System.out.println("天上飞!");
	}

	@Override
	public void move() {
		super.move();
		fly();
	}
	
}

//ConcreteDecorator具体装饰角色
class WaterCar extends SuperCar {
	
	public WaterCar(ICar car) {
		super(car);
	}
	
	public void swim(){
		System.out.println("水上游!");
	}
	
	@Override
	public void move() {
		super.move();
		swim();
	}
	
}

//ConcreteDecorator具体装饰角色
class AICar extends SuperCar {
	
	public AICar(ICar car) {
		super(car);
	}
	
	public void autoMove(){
		System.out.println("自动跑!");
	}
	
	@Override
	public void move() {
		super.move();
		autoMove();
	}
	
}

测试

package com.bjsxt.decorator;

import java.io.FileInputStream;
import java.io.InputStreamReader;

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(car);
		waterCar.move();
		
		System.out.println("增加两个新的功能,飞行,水里游-------");
		WaterCar waterCar2 = new WaterCar(new FlyCar(car));
		waterCar2.move();

		
	}
}

陆地上跑!
增加新的功能,飞行----------
陆地上跑!
天上飞!
增加新的功能,水里游---------
陆地上跑!
水上游!
增加两个新的功能,飞行,水里游-------
陆地上跑!
天上飞!
水上游!

原理

具体构建角色:车--->实现汽车接口
装饰角色:特殊车--->实现汽车接口(包含一个实现了汽车接口的子类)
具体装饰角色:飞车--->继承装饰角色(传入需要装饰的车,类似于组合)

使用场景

  • IO中输入流和输出流的设计
  • Swing包中图形界面构件功能
  • Servlet API 中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper,HttpServletRequestWrapper 类,增强了request对象的功能。
  • Struts2中,request,response,session对象的处理

总结

  • 装饰模式(Decorator)也叫包装器模式(Wrapper)
  • 装饰模式降低系统的耦合度,可以动态的增加或删除对象的职责,并使得需要装饰的具体构建类和具体装饰类可以独立变化,以便增加新的具体构建类和具体装饰类

优点

  • 扩展对象功能,比继承灵活,不会导致类个数急剧增加
  • 可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更加强大的对象
  • 具体构建类和具体装饰类可以独立变化,用户可以根据需要自己增加新的具体构件子类和具体装饰子类

缺点

  • 产生很多小对象。大量小对象占据内存,一定程度上影响性能
  • 装饰模式易于出错,调试排查比较麻烦

与桥接区别

  • 两个模式都是为了解决过多子类对象问题。但他们の诱因不一样。桥模式是对象自身现有机制沿着多个维度变化,是既有部分不稳定。装饰模式是为了增加新的功能

11:组合模式

核心内容

  • 抽象构件(Component)角色: 定义了叶子和容器构件的共同点
  • 叶子(Leaf)构件角色:无子节点
  • 容器(Composite)构件角色: 有容器特征,可以包含子节点

工作流程

  • 组合模式为处理树形结构提供了完美的解决方案,描述了如何将容器和叶子进行递归组合,使得用户在使用时可以一致性的对待容器和叶子
  • 当容器对象的指定方法被调用时,将遍历整个树形结构,寻找也包含这个方法的成员,并调用执行。其中,使用了递归调用的机制对整个结构进行处理

模拟杀毒

抽象组件

package com.bjsxt.composite;

/**
 * 抽象组件
 * @author Administrator
 *
 */
public interface Component {
	void operation();
}

//叶子组件
interface Leaf extends Component {
}
//容器组件
interface Composite extends Component {
	/**
	 * 添加方法
	 * @param c
	 */
	void add(Component c);
	/**
	 * 删除方法
	 * @param c
	 */
	void remove(Component c);
	/**
	 * 获取子节点方法
	 * @param index
	 * @return
	 */
	Component getChild(int index);
}

抽象构建

package com.bjsxt.composite;

import java.util.ArrayList;
import java.util.List;

//抽象构建
public interface AbstractFile {
	void killVirus();  //杀毒
}

/**
 *照片文件类 
 * @author Administrator
 *
 */
class ImageFile implements AbstractFile {
	private String name;
	
	public ImageFile(String name) {
		super();
		this.name = name;
	}

	@Override
	public void killVirus() {
		System.out.println("---图像文件:"+name+",进行查杀!");
	}
	
}
/**
 * 文本文件
 * @author Administrator
 *
 */
class TextFile implements AbstractFile {
	private String name;
	
	public TextFile(String name) {
		super();
		this.name = name;
	}
	
	@Override
	public void killVirus() {
		System.out.println("---文本文件:"+name+",进行查杀!");
	}
}
/**
 * 视频文件类
 * @author Administrator
 *
 */
class VideoFile implements AbstractFile {
	private String name;
	
	public VideoFile(String name) {
		super();
		this.name = name;
	}
	
	@Override
	public void killVirus() {
		System.out.println("---视频文件:"+name+",进行查杀!");
	}
}
/**
 * 文件夹类
 * @author Administrator
 *
 */
class Folder implements AbstractFile {
	private String name;
	//定义容器,用来存放本容器构建下的子节点
	private List<AbstractFile> list = new ArrayList<AbstractFile>();
	
	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);
	}

	@Override
	public void killVirus() {
		System.out.println("---文件夹:"+name+",进行查杀");
		
		for (AbstractFile file : list) {
			file.killVirus();
		}
		
	}
	
}


测试

package com.bjsxt.composite;

public class Client {
	public static void main(String[] args) {
		AbstractFile f2,f3,f4,f5;
		Folder f1 = new Folder("我的收藏");
		
		f2 = new ImageFile("老高的大头像.jpg");
		f3 = new TextFile("Hello.txt");
		f1.add(f2);
		f1.add(f3);
		
		Folder f11 = new Folder("电影");
		f4 = new VideoFile("笑傲江湖.avi");
		f5 = new VideoFile("神雕侠侣.avi");
		f11.add(f4);
		f11.add(f5);
		f1.add(f11);
		
		
//		f2.killVirus();
		
		f1.killVirus();
		
		
	}
}

原理

图像文件,文本文件,视频文件,文件夹--->实现杀毒接口(有一个杀毒方法)

应用场景

  • –操作系统的资源管理器
  • –GUI中的容器层次图
  • –XML文件解析
  • –OA系统中,组织结构的处理
  • –Junit单元测试框架(•底层设计就是典型的组合模式,TestCase(叶子)、TestUnite(容器)、Test接口(抽象))

12:迭代器模式

场景

  • 提供一种可以遍历聚合对象的方式。又称为:游标cursor模式
  • 聚合对象:存储数据
  • 迭代器:遍历数据

结构

  • 聚合对象:存储数据
  • 迭代器:遍历数据

案例

迭代接口

package com.bjsxt.iterator;


/**
 *  自定义的迭代器接口
 * @author Administrator
 *
 */
public interface MyIterator {
	void first();	//将游标指向第一个元素
	void next();	//将游标指向下一个元素
	boolean hasNext();//判断是否存在下一个元素
	
	boolean isFirst();
	boolean isLast();
	
	Object getCurrentObj();  //获取当前游标指向的对象
}

聚合类

package com.bjsxt.iterator;

import java.util.ArrayList;
import java.util.List;

/**
 * 自定义的聚合类
 * @author Administrator
 *
 */
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 Object getCurrentObj() {
			return list.get(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 void next() {
			if(cursor<list.size()){
				cursor++;
			}
		}
		
	}

}

测试

package com.bjsxt.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();
		}
		
	}
}

aa
bb
cc

使用场景

  • JDK内置的迭代器(List/Set)

13:责任链模式

定义

  • 将能够处理同一类请求的对象连成一条链,所提交的请求沿着链传递,链上的对象逐个判断是否有能力处理该请求,如果能则处理,如果不能则传递给链上的下一个对象
  • 大学中,奖学金审批
  • 公司中,公文审批

请假流程

  • 如果请假天数小于3天,主任审批
  • 如果请假天数大于等于3天,小于10天,经理审批
  • 如果大于等于10天,小于30天,总经理审批
  • 如果大于等于30天,提示拒绝

领导者

package com.bjsxt.chainOfResp;

/**
 * 抽象类
 * @author Administrator
 *
 */
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.bjsxt.chainOfResp;

/**
 * 主任
 * @author Administrator
 *
 */
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.bjsxt.chainOfResp;

/**
 * 经理
 * @author Administrator
 *
 */
public class Manager extends Leader {

	public Manager(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{
			if(this.nextLeader!=null){
				this.nextLeader.handleRequest(request);
			}
		}
	}

}

总经理

package com.bjsxt.chainOfResp;

/**
 * 总经理
 * @author Administrator
 *
 */
public class GeneralManager extends Leader {

	public GeneralManager(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{
			System.out.println("莫非"+request.getEmpName()+"想辞职,居然请假"+request.getLeaveDays()+"天!");
		}
	}

}

请假信息

package com.bjsxt.chainOfResp;

/**
 * 封装请假的基本信息
 * @author Administrator
 *
 */
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.bjsxt.chainOfResp;

public class Client {
	public static void main(String[] args) {
		Leader a = new Director("张三");
		Leader b = new Manager("李四");
		Leader b2 = new ViceGeneralManager("李小四");
		Leader c = new GeneralManager("王五");
		//组织责任链对象的关系
		a.setNextLeader(b);
		b.setNextLeader(b2);
		b2.setNextLeader(c);
		
		//开始请假操作
		LeaveRequest req1 = new LeaveRequest("TOM", 15, "回英国老家探亲!");
		a.handleRequest(req1);
		
	}
}

员工:TOM请假,天数:15,理由:回英国老家探亲!
副总经理:李小四,审批通过!

添加新对象

  • 由于责任链的创建完全在客户端,因此新增新的具体处理者对原有类库没有任何影响,只需添加新的类,然后在客户端调用时添加即可
  • 符合开闭原则

使用场景

  • Java中,异常机制就是一种责任链模式。一个try可以对应多个catch,当第一个catch不匹配类型,则自动跳到第二个catch
  • Javascript语言中,事件的冒泡和捕获机制。Java语言中,事件的处理采用观察者模式
  • Servlet开发中,过滤器的链式处理
  • Struts2中,拦截器的调用也是典型的责任链模式

14:备忘录模式

场景

  • 录入大批人员资料。正在录入当前人资料时,发现上一个人录错了,此时需要恢复上一个人的资料,再进行修改
  • Word文档编辑时,忽然电脑死机或断电,再打开时,可以看到word提示你恢复到以前的文档
  • 管理系统中,公文撤回功能。公文发送出去后,想撤回来

核心

  • 就是保存某个对象内部状态的拷贝,这样以后就可以将该对象恢复到原先的状态

结构

  • 源发器类Originator
  • 备忘录类Memento
  • 负责人类CareTaker

案例

源发器类

package com.bjsxt.memento;

/**
 * 源发器类
 * @author Administrator
 *
 */
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.bjsxt.memento;

/**
 * 备忘录类
 * @author Administrator
 *
 */
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.bjsxt.memento;

/**
 * 负责人类
 * 负责管理备忘录对象
 * @author Administrator
 *
 */
public class CareTaker {
	
	private EmpMemento memento;

//如果想要存储多个备忘点 private List<EmpMemento> list = new ArrayList<EmpMemento>();
	

	public EmpMemento getMemento() {
		return memento;
	}

	public void setMemento(EmpMemento memento) {
		this.memento = memento;
	}
	
}

测试

package com.bjsxt.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());
		
	}
}

第一次打印对象:高淇---18---900.0
第二次打印对象:搞起---38---9000.0
第三次打印对象:高淇---18---900.0

使用场景

  • 棋类游戏中的,悔棋
  • 普通软件中的,撤销操作
  • 数据库软件中的,事务管理中的,回滚操作
  • Photoshop软件中的,历史记录

15:访问者模式

介绍

  • 表示一个作用于某对象结构中的各元素的操作,它使我们可以在不改变个元素的类的前提下定义作用于这些元素的新操作

模式动机

  • 对于存储在一个集合中的对象,他们可能具有不同的类型(即使有一个公共的接口),对于该集合中的对象,可以接受一类称为访问者的对象来访问,不同的访问者其访问方式也有所不同

使用场景

  • XML文档解析器设计
  • 编译器的设计
  • 复杂集合对象的处理

16:观察者模式

场景

  • 网站上,很多人订阅了”java主题”的新闻。当有这个主题新闻时,就会将这些新闻发给所有订阅的人
  • 大家一起玩CS游戏时,服务器需要将每个人的方位变化发给所有的客户
  • 我们可以把多个订阅者、客户称之为观察者; 需要同步给多个订阅者的数据封装到对象中,称之为目标

核心

  • –观察者模式主要用于1:N的通知。当一个对象(目标对象Subject或Objservable)的状态变化时,他需要及时告知一系列对象(观察者对象,Observer),令他们做出响应
  • 通知观察者的方式:
  • 推:每次都会把通知以广播方式发送给所有观察者,所有观察者只能被动接收。
  • 拉:观察者只要直到有情况即可。至于什么时候获取内容,获取什么内容,都可以自主决定。
  • JAVASE提供了java.util.Observable和java.util.Observer来实现观察者模式

案例

目标父类

package com.bjsxt.observer;

import java.util.ArrayList;
import java.util.List;
/**
 * 目标通用父类
 * @author Administrator
 *
 */
public class Subject {
	//存储所有观察者
	protected List<Observer> list = new ArrayList<Observer>();
	//注册
	public void registerObserver(Observer obs){
		list.add(obs);
	}
	//删除
	public void removeObserver(Observer obs){
		list.add(obs);
	}

	//如果目标状态改变,通知所有的观察者更新状态
	public void notifyAllObservers(){
		for (Observer obs : list) {
			obs.update(this);
		}
	}
	
}

真实目标

package com.bjsxt.observer;
/**
 * 具体目标类,状态改变通知所有观察者
 * @author Administrator
 *
 */
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.bjsxt.observer;
/**
 * 观察者接口
 * @author Administrator
 *
 */
public interface Observer {
	void  update(Subject subject);
}

真实观察者

package com.bjsxt.observer;
/**
 * 真实观察者
 * @author Administrator
 *
 */
public class ObserverA implements Observer {

	private int myState;   //myState需要跟目标对象的state值保持一致!
	
	
	@Override
	public void update(Subject subject) {
		myState = ((ConcreteSubject)subject).getState();
	}


	public int getMyState() {
		return myState;
	}
	public void setMyState(int myState) {
		this.myState = myState;
	}
	
	

}

测试

package com.bjsxt.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.registerObserver(obs1);
		subject.registerObserver(obs2);
		subject.registerObserver(obs3);
		
		
		//改变subject的状态
		subject.setState(3000);
		System.out.println("########################");
		//我们看看,观察者的状态是不是也发生了变化
		System.out.println(obs1.getMyState());
		System.out.println(obs2.getMyState());
		System.out.println(obs3.getMyState());
		
		//改变subject的状态
		subject.setState(30);
		System.out.println("########################");
		//我们看看,观察者的状态是不是也发生了变化
		System.out.println(obs1.getMyState());
		System.out.println(obs2.getMyState());
		System.out.println(obs3.getMyState());
		
		
	}
}

########################
3000
3000
3000
########################
30
30
30

java自带

  • JAVASE提供了java.util.Observable和java.util.Observer来实现观察者模式

目标对象

package com.bjsxt.observer2;

import java.util.Observable;

//目标对象
public class ConcreteSubject extends Observable {
	
	private int state; 
	
	public void set(int s){
		state = s;  //目标对象的状态发生了改变
		
		setChanged();  //表示目标对象已经做了更改
		notifyObservers(state);  //通知所有的观察者
		
	}

	public int getState() {
		return state;
	}

	public void setState(int state) {
		this.state = state;
	}
	
}

观察者

package com.bjsxt.observer2;

import java.util.Observable;
import java.util.Observer;

public class ObserverA implements Observer {

	private int myState;
	
	
	@Override
	public void update(Observable o, Object arg) {
		myState = ((ConcreteSubject)o).getState();
	}


	public int getMyState() {
		return myState;
	}


	public void setMyState(int myState) {
		this.myState = myState;
	}

	
	
}

测试

package com.bjsxt.observer2;

public class Client {
	public static void main(String[] args) {
		//创建目标对象Obserable
		ConcreteSubject subject = new ConcreteSubject();
		
		//创建观察者
		ObserverA obs1 = new ObserverA();
		ObserverA obs2 = new ObserverA();
		ObserverA obs3 = new ObserverA();
		
		//将上面三个观察者对象添加到目标对象subject的观察者容器中
		subject.addObserver(obs1);
		subject.addObserver(obs2);
		subject.addObserver(obs3);
		
		//改变subject对象的状态
		subject.set(3000);
		System.out.println("===============状态修改了!");
		//观察者的状态发生了变化
		System.out.println(obs1.getMyState());
		System.out.println(obs2.getMyState());
		System.out.println(obs3.getMyState());

		subject.set(600);
		System.out.println("===============状态修改了!");
		//观察者的状态发生了变化
		System.out.println(obs1.getMyState());
		System.out.println(obs2.getMyState());
		System.out.println(obs3.getMyState());		
		
	}
}

===============状态修改了!
3000
3000
3000
===============状态修改了!
600
600
600

###使用场景

  • –聊天室程序的,服务器转发给所有客户端
  • –网络游戏(多人联机对战)场景中,服务器将客户端的状态进行分发
  • –邮件订阅
  • –Servlet中,监听器的实现
  • –Android中,广播机制
  • –JDK的AWT中事件处理模型,基于观察者模式的委派事件模型(Delegation Event Model),•事件源—————-目标对象,•事件监听器————观察者
  • –京东商城中,群发某商品打折信息

17:解释器模式

介绍

  • –是一种不常用的设计模式
  • 用于描述如何构成一个简单的语言解释器,主要用于使用面向对象语言开发的编译器和解释器设计
  • 当我们需要开发一种新的语言时,可以考虑使用解释器模式
  • 尽量不要使用解释器模式,后期维护会有很大麻烦。在项目中,可以使用Jruby,Groovy、java的js引擎来替代解释器的作用,弥补java语言的不足

使用场景

  • EL表达式式的处理
  • 正则表达式解释器
  • SQL语法的解释器
  • 数学表达式解析器

18:命令模式

介绍

  • 命令模式:将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;
  • 对请求排队或者记录请求日志,以及支持可撤销的操作。
  • 也称之为:动作Action模式、事务transaction模式

结构

  • Command抽象命令类
  • ConcreteCommand具体命令类
  • Invoker调用者/请求者:请求的发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联。在程序运行时,将调用命令对象的execute(),间接调用接收者的相关操作
  • Receiver接收者:接收者执行与请求相关的操作,具体实现对请求的业务处理。未抽象前,实际执行操作内容的对象
  • Client客户类:在客户类中需要创建调用者对象、具体命令类对象,在创建具体命令对象时指定对应的接收者。发送者和接收者之间没有直接关系,都通过命令对象间接调用

案例

命令抽象类

package com.bjsxt.command;

public interface Command {
	/**
	 * 这个方法是一个返回结果为空的方法。
	 * 实际项目中,可以根据需求设计多个不同的方法
	 */
	void execute();
}

具体命令类

class ConcreteCommand implements Command {
	
	private Receiver receiver;	//命令的真正的执行者
	
	public ConcreteCommand(Receiver receiver) {
		super();
		this.receiver = receiver;
	}

	@Override
	public void execute() {
		//命令真正执行前或后,执行相关的处理!
		receiver.action();
	}
}

调用者

package com.bjsxt.command;

//调用者/发起者
public class Invoke {
	
	private Command command;   //也可以通过容器List<Command>容纳很多命令对象,进行批处理。数据库底层的事务管理就是类似的结构!

	public Invoke(Command command) {
		super();
		this.command = command;
	} 
	
	//业务方法 ,用于调用命令类的方法
	public void call(){
		command.execute();
	}
	
	
}

命令执行者

package com.bjsxt.command;

/**
 * 真正的命令的执行者
 * @author Administrator
 *
 */
public class Receiver {
	public void action(){
		System.out.println("Receiver.action()");
	}
}

测试

package com.bjsxt.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();
		
	}
}

原理

真实命令类--->实现命令接口
调用者--->调用真实命令类--->真正执行者--->执行命令

使用场景

  • Struts2中,action的整个调用过程中就有命令模式
  • 数据库事务机制的底层实现
  • 命令的撤销和恢复

19:模板方法模式

场景

  • 客户到银行办理业务:①取号排队②办理具体现金业务③给银行工作人员评分

介绍

  • 模板方法模式是编程中经常用得到模式
  • 它定义了一个操作中的算法骨架,将某些步骤延迟到子类中实现
  • 新的子类可以在不改变一个算法结构的前提下重新定义该算法的某些特定步骤

核心

  • 处理某个流程的代码已经都具备,但是其中某个节点的代码暂时不能确定
  • 我们采用工厂方法模式,将这个节点的代码实现转移给子类完成
  • 即:处理步骤父类中定义好,具体实现延迟到子类中定义

方法回调

  • 子类不能调用父类,而通过父类调用子类。这些调用步骤已经在父类中写好了,完全由父类控制整个过程。

案例

模板抽象类

package com.bjsxt.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.bjsxt.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();
		
		BankTemplateMethod btm3 = new BankTemplateMethod() {
			@Override
			public void transact() {
				System.out.println("我要理财!我这里有2000万韩币");
			}
		};
		
		btm3.process();
		
	}
}


/**
 * 取款业务,继承模板并重写其中的抽象方法
 * @author Administrator
 *
 */
class DrawMoney extends BankTemplateMethod {

	@Override
	public void transact() {
		System.out.println("我要取款!!!");
	}
	
}

我要取款!!!
反馈评分
取号排队
我要存钱!
反馈评分
取号排队
我要理财!我这里有2000万韩币
反馈评分

使用场景

  • 实现一个算法时,整体步骤很固定。但是,某些部分易变。易变部分可以抽象成出来,供子类实现。
  • 数据库访问的封装
  • Junit单元测试
  • servlet中关于doGet/doPost方法调用
  • Hibernate中模板程序
  • spring中JDBCTemplate、HibernateTemplate等

20:中介者模式

场景

  • 假如没有总经理。下面三个部门:财务部、市场部、研发部。财务部要发工资,让大家核对公司需要跟市场部和研发部都通气;市场部要接个新项目,需要研发部处理技术、需要财务部出资金。市场部跟各个部门打交道。 虽然只有三个部门,但是关系非常乱
  • 实际上,公司都有总经理。各个部门有什么事情都通报到总经理这里,总经理再通知各个相关部门
  • 这就是一个典型的“中介者模式”总经理起到一个中介、协调的作用

核心

  • –如果一个系统中对象之间的联系呈现为网状结构,对象之间存在大量多对多关系,将导致关系及其复杂,这些对象称为“同事对象”
  • 我们可以引入一个中介者对象,使各个同事对象只跟中介者对象打交道,将复杂的网络结构化解为如下的星形结构

本质

  • 解耦多个同事对象之间的交互关系。每个对象都持有中介者对象的引用,只跟中介者对象打交道。我们通过中介者对象统一管理这些交互关系

案例

中介者接口

package com.bjsxt.mediator;
/**
 * 中介者接口
 * @author Administrator
 *
 */
public interface Mediator {
	/**
	 * 注册
	 * @param dname
	 * @param d
	 */
	void register(String dname,Department d);
	/**
	 * 命令
	 * @param dname
	 */
	void command(String dname);
	
}

中介者实现类

package com.bjsxt.mediator;

import java.util.HashMap;
import java.util.Map;
/**
 * 中介者实现类
 * @author Administrator
 *
 */
public class President implements Mediator {
	/**
	 * 存储同事类
	 */
	private Map<String,Department> map = new HashMap<String , Department>();
	
	@Override
	public void command(String dname) {
		map.get(dname).selfAction();
	}

	@Override
	public void register(String dname, Department d) {
		map.put(dname, d);
	}

}

同事类接口

package com.bjsxt.mediator;

//同事类的接口
public interface Department {
	void selfAction(); //做本部门的事情
	void outAction();  //向总经理发出申请
}

开发部

package com.bjsxt.mediator;
/**
 * 开发部
 * @author Administrator
 *
 */
public class Development implements Department {

	private Mediator m;  //持有中介者(总经理)的引用
	
	public Development(Mediator m) {
		super();
		this.m = m;
		m.register("development", this);
	}

	@Override
	public void outAction() {
		System.out.println("汇报工作!没钱了,需要资金支持!");
	}

	@Override
	public void selfAction() {
		System.out.println("专心科研,开发项目!");
	}

}

市场部

package com.bjsxt.mediator;
/**
 * 市场部
 * @author Administrator
 *
 */
public class Market implements Department {

	private Mediator m;  //持有中介者(总经理)的引用
	
	public Market(Mediator m) {
		super();
		this.m = m;
		m.register("market", this);
	}

	@Override
	public void outAction() {
		System.out.println("汇报工作!项目承接的进度,需要资金支持!");
		
		m.command("finacial");
		
	}

	@Override
	public void selfAction() {
		System.out.println("跑去接项目!");
	}

}

财务部

package com.bjsxt.mediator;
/**
 * 财务部
 * @author Administrator
 *
 */
public class Finacial implements Department {

	private Mediator m;  //持有中介者(总经理)的引用
	
	public Finacial(Mediator m) {
		super();
		this.m = m;
		m.register("finacial", this);
	}

	@Override
	public void outAction() {
		System.out.println("汇报工作!没钱了,钱太多了!怎么花?");
	}

	@Override
	public void selfAction() {
		System.out.println("数钱!");
	}

}

测试

package com.bjsxt.mediator;

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();
		
	}
}

跑去接项目!
汇报工作!项目承接的进度,需要资金支持!
数钱!

原理

总经理(中介者)--->实现中介者接口(包含接收信息和发布命令两个方法)
同事类--->实现同事接口(包含自己业务需求和向中介者发送请求两个方法)
市场部--->需要资金支持--->总经理(中介者)接收到请求--->向财务部发布命令--->财务部执行自己方法

开发场景

  • MVC模式(其中的C,控制器就是一个中介者对象。M和V都和他打交道)
  • 窗口游戏程序,窗口软件开发中窗口对象也是一个中介者对象
  • 图形界面开发GUI中,多个组件之间的交互,可以通过引入一个中介者对象来解决,可以是整体的窗口对象或者DOM对象
  • Java.lang.reflect.Method#invoke()

21:状态模式

场景

  • –酒店系统中,房间的状态变化:已预订,已入住,空闲

核心

  • 用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题

结构

  • Context环境类:环境类中维护一个State对象,他是定义了当前的状态
  • State抽象状态类
  • ConcreteState具体状态类:每一个类封装了一个状态对应的行为

案例

状态接口

package com.bjsxt.state;

public interface State {
	void handle();
}

空闲状态

package com.bjsxt.state;

/**
 * 空闲状态
 * @author Administrator
 *
 */
public class FreeState implements State {

	@Override
	public void handle() {
		System.out.println("房间空闲!!!没人住!");
	}

}

预定状态

package com.bjsxt.state;

/**
 * 已预订状态
 * @author Administrator
 *
 */
public class BookedState implements State {

	@Override
	public void handle() {
		System.out.println("房间已预订!别人不能定!");
	}

}

入住状态

package com.bjsxt.state;

/**
 * 已入住状态
 * @author Administrator
 *
 */
public class CheckedInState implements State {

	@Override
	public void handle() {
		System.out.println("房间已入住!请勿打扰!");
	}

}

房间对象

package com.bjsxt.state;


/**
 * 房间对象
 * @author Administrator
 *
 */
public class HomeContext {
	//如果是银行系统,这个Context类就是账号。根据金额不同,切换不同的状态!
	
	private State state;
	
	
	public void setState(State s){
		System.out.println("修改状态!");
		state = s;
		state.handle();
	}
	
}

测试

package com.bjsxt.state;


public class Client {
	public static void main(String[] args) {
		HomeContext ctx = new HomeContext();
		
		ctx.setState(new FreeState());
		ctx.setState(new BookedState());
		
	}
}

修改状态!
房间空闲!!!没人住!
修改状态!
房间已预订!别人不能定!

结构图

空闲--->预定--->已预订
已预订--->退订--->空闲

已预订--->住入--->已入住
已入住--->续费--->已预订

已入住--->退房--->空闲
空闲--->住入--->已入住

使用场景

  • 银行系统中账号状态的管理
  • OA系统中公文状态的管理
  • 酒店系统中,房间状态的管理
  • 线程对象各状态之间的切换

22:策略模式

注意点

  • 分析项目中变化部分与不变部分
  • 多用组合少用继承,用行为类组合,而不是行为的继承,更有弹性
  • 设计模式有没有相应的库直接使用?有些库或框架本身就是某种设计模式设计的
  • 学习设计模式最好的方式:在你的设计和以往的工程里寻找何处可以使用它们
  • 设计模式的本质目的是使软件工程在维护性、扩展性、变化性、复杂度方面成O(N)

入门

在java里IO流的类设计,为什么把BufferedReader设计成:
new BufferedReader(new FileReader("F:\test.java"));
而不是设计成:
BufferedReader extends FileReader;
然后 
new BufferedReader("F:\test.java");
...!

原理

模拟项目

//从项目"模拟鸭子游戏"开始
//从OO的角度设计这个项目,鸭子超类,扩展超类:
public abstract class Duck {
    public void Quack() {
    System.out.println("~~gaga~~");
    }
    public abstract void display();
    public void swim() {
    System.out.println("~~im swim~~");
    }
}
//1、GreenHeadDuck继承Duck :
public class GreenHeadDuck extends Duck {
    @Override
    public void display() {
    System.out.println("**GreenHead**");
    }
}

新需求

  • 添加会飞的鸭子
//OO思维里的继承方式解决方案是:
public abstract class Duck {
    ...;
    public void Fly() {
    System.out.println("~~im fly~~");
    }
}
  • 问题来了,这个Fly让所有子类都会飞了,这是不科学的。
  • 继承的问题:对类的局部改动,尤其超类的局部改动,会影响其他部分。影响会有溢出效应
//继续尝试用OO原理来解决,覆盖:
public class GreenHeadDuck extends Duck {
    ...
    public void Fly() {
    System.out.println("~~no fly~~");
    }
}

新需求2

  • 又有新需求,石头鸭子
//填坑:
public class StoneDuck extends Duck {
....
}
  • 超类挖的一个坑,每个子类都要来填,增加工作量,复杂度O(N^2)。不是好的设计方式

用策略模式解决

  • 需要新的设计方式,应对项目的扩展性,降低复杂度: 1)分析项目变化与不变部分,提取变化部分,抽象成接口+实现; 2)鸭子哪些功能是会根据新需求变化的?叫声、飞行…

接口

public interface FlyBehavior {
	void fly();
}

public interface QuackBehavior
{
	void quack();
}
  • 好处:新增行为简单,行为类更好的复用,组合更方便。既有继承带来的复用好处,没有挖坑
public class GoodFlyBehavior implements FlyBehavior
{
	@Override
	public void fly() {
		// TODO Auto-generated method stub
		System.out.println("--GoodFly--");
	}
	
}
public	class GaGaQuackBehavior implements QuackBehavior
{
	@Override
	public void quack() {
		// TODO Auto-generated method stub
		System.out.println("__GaGa__");
	}
	
}

实现

//重新设计的鸭子项目:
public abstract class Duck {
	//飞行接口
    FlyBehavior mFlyBehavior;
    //叫声接口
    QuackBehavior mQuackBehavior;
    public Duck() {
    }
    public void Fly() {
    	mFlyBehavior.fly();
    }
    public void Quack() {
    	mQuackBehavior.quack();
    }
    public abstract void display();
}
//绿头鸭、石头鸭:
public class GreenHeadDuck extends Duck {
    //构造方法调用超类的构造方法
    public GreenHeadDuck() {
        //使用的时候只需要实例化想要的对象就可以了
        mFlyBehavior = new GoodFlyBehavior();
        mQuackBehavior = new GaGaQuackBehavior();
    }
    @Override
    public void display() {...}
}

总结

  • 策略模式:分别封装行为接口,实现算法族,超类里放行为接口对象,在子类里具体设 定行为对象。原则就是:分离变化部分,封装接口,基于接口编程各种功能。此模式让行为 算法的变化独立于算法的使用者。

场景

  • –某个市场人员接到单后的报价策略(CRM系统中常见问题)。报价策略很复杂,可以简单作如下分类:
  • 普通客户小批量报价
  • 普通客户大批量报价
  • 老客户小批量报价
  • 老客户大批量报价

  • 具体选用哪个报价策略,这需要根据实际情况来确定。这时候,我们采用策略模式即可
  • 假如,类型特别多,算法比较复杂时,整个条件控制代码会变得很长,难于维护

定义

  • 策略模式对应于解决某一个问题的一个算法族,允许用户从该算法族中任选一个算法解决某一问题,同时可以方便的更换算法或者增加新的算法。并且由客户端决定调用哪个算法

本质

  • 分离算法,选择实现

案例

普通方式

package com.bjsxt.strategy;

/**
 * 实现起来比较容易,符合一般开发人员的思路
 * 假如,类型特别多,算法比较复杂时,整个条件语句的代码就变得很长,难于维护。
 * 如果有新增类型,就需要频繁的修改此处的代码!
 * 不符合开闭原则!
 * @author Administrator
 *
 */
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;
	}

}

策略接口

package com.bjsxt.strategy;
/**
 * 
 * 策略接口
 * @author Administrator
 *
 */
public interface Strategy {
	/**
	 * 传入原价,获取价格
	 * @param standardPrice
	 * @return
	 */
	public double getPrice(double  standardPrice);
}

算法族

package com.bjsxt.strategy;

public class NewCustomerFewStrategy implements Strategy {

	@Override
	public double getPrice(double standardPrice) {
		System.out.println("不打折,原价");
		return standardPrice;
	}

}

package com.bjsxt.strategy;

public class NewCustomerManyStrategy implements Strategy {

	@Override
	public double getPrice(double standardPrice) {
		System.out.println("打九折");
		return standardPrice*0.9;
	}

}

package com.bjsxt.strategy;

public class OldCustomerFewStrategy implements Strategy {

	@Override
	public double getPrice(double standardPrice) {
		System.out.println("打八五折");
		return standardPrice*0.85;
	}

}

package com.bjsxt.strategy;

public class OldCustomerManyStrategy implements Strategy {

	@Override
	public double getPrice(double standardPrice) {
		System.out.println("打八折");
		return standardPrice*0.8;
	}

}

交互类

package com.bjsxt.strategy;

/**
 * 负责和具体的策略类交互
 * 这样的话,具体的算法和直接的客户端调用分离了,使得算法可以独立于客户端独立的变化。
 * 如果使用spring的依赖注入功能,还可以通过配置文件,动态的注入不同策略对象,动态的切换不同的算法.
 * @author Administrator
 *
 */
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 pringPrice(double s){
		System.out.println("您该报价:"+strategy.getPrice(s));
	}
	
	
}

测试

package com.bjsxt.strategy;

public class Client {
	public static void main(String[] args) {
		
		Strategy s1 = new OldCustomerManyStrategy();
		Context ctx = new Context(s1);
		
		ctx.pringPrice(998);
		
	}
}

打八折
您该报价:798.4000000000001

使用场景

  • JAVASE中GUI编程中,布局管理
  • Spring框架中,Resource接口,资源访问策略
  • javax.servlet.http.HttpServlet#service()

上一篇 javaPOI操作Excel

Comments

Content