设计模式 --- 结构型模式

2020-05-17   183 次阅读


适配器模式

1、问题引入
现实生活中的适配器例子
泰国插座用的是两孔的(欧标),可以买个多功能转换插头 (适配器) ,这样就可以使用了。
1.png

2、适配器模式基本介绍
a、 适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)
b、 适配器模式属于结构型模式
c、 主要分为三类:类适配器模式、对象适配器模式、接口适配器模式

3、适配器工作原理
a、 适配器模式:将一个类的接口转换成另一种接口.让原本接口不兼容的类可以兼容
b、 从用户的角度看不到被适配者,是解耦的
c、 用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法
d、 用户收到反馈结果,感觉只是和目标接口交互,如图
1.png

类适配器模式

1、基本介绍
Adapter类,通过继承 src类,实现 dst 类接口,完成src->dst的适配。
2、类适配器模式应用实例
a、以生活中充电器的例子来讲解适配器,充电器本身相当于Adapter,220V交流电相当于src (即被适配者),我们的目dst(即 目标)是5V直流电
b、思路分析
1.png

3、代码实现

public class Client {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println(" === 类适配器模式 ====");
		Phone phone = new Phone();
		phone.charging(new VoltageAdapter());
	}

}

//适配器类
public class VoltageAdapter extends Voltage220V implements IVoltage5V {

	@Override
	public int output5V() {
		// TODO Auto-generated method stub
		//获取到220V电压
		int srcV = output220V();
		int dstV = srcV / 44 ; //转成 5v
		return dstV;
	}

}

public interface IVoltage5V {
	public int output5V();
}

//被适配的类
public class Voltage220V {
	//输出220V的电压
	public int output220V() {
		int src = 220;
		System.out.println("电压=" + src + "伏");
		return src;
	}
}


public class Phone {

	//充电
	public void charging(IVoltage5V iVoltage5V) {
		if(iVoltage5V.output5V() == 5) {
			System.out.println("电压为5V, 可以充电~~");
		} else if (iVoltage5V.output5V() > 5) {
			System.out.println("电压大于5V, 不能充电~~");
		}
	}
}

4、类适配器模式注意事项和细节
a、 Java是单继承机制,所以类适配器需要继承src类这一点算是一个缺点, 因为这要求dst必须是接口,有一定局限性;
b、 src类的方法在Adapter中都会暴露出来,也增加了使用的成本。
c、 由于其继承了src类,所以它可以根据需求重写src类的方法,使得Adapter的灵活性增强了。

对象适配器

1、基本介绍
a、基本思路和类的适配器模式相同,只是将Adapter类作修改,不是继承src类,而是持有src类的实例,以解决兼容性的问题。 即:持有 src类,实现 dst 类接口,完成src->dst的适配
b、 根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系。
c、 对象适配器模式是适配器模式常用的一种

2、应用实例
a、以生活中充电器的例子来讲解适配器,充电器本身相当于Adapter,220V交流电相当于src (即被适配者),我们的目dst(即目标)是5V直流电,使用 对象适配器模式完成。
b、思路分析(类图):只需修改适配器即可, 如下:
1.png
c、代码实现

public class Client {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println(" === 对象适配器模式 ====");
		Phone phone = new Phone();
		phone.charging(new VoltageAdapter(new Voltage220V()));
	}

}

//适配器类
public class VoltageAdapter  implements IVoltage5V {

	private Voltage220V voltage220V; // 关联关系-聚合
	
	
	//通过构造器,传入一个 Voltage220V 实例
	public VoltageAdapter(Voltage220V voltage220v) {
		
		this.voltage220V = voltage220v;
	}



	@Override
	public int output5V() {
		
		int dst = 0;
		if(null != voltage220V) {
			int src = voltage220V.output220V();//获取220V 电压
			System.out.println("使用对象适配器,进行适配~~");
			dst = src / 44;
			System.out.println("适配完成,输出的电压为=" + dst);
		}
		
		return dst;
		
	}

}

public interface IVoltage5V {
	public int output5V();
}

//被适配的类
public class Voltage220V {
	//输出220V的电压,不变
	public int output220V() {
		int src = 220;
		System.out.println("电压=" + src + "伏");
		return src;
	}
}

public class Phone {

	//充电
	public void charging(IVoltage5V iVoltage5V) {
		if(iVoltage5V.output5V() == 5) {
			System.out.println("电压为5V, 可以充电~~");
		} else if (iVoltage5V.output5V() > 5) {
			System.out.println("电压大于5V, 不能充电~~");
		}
	}
}


3、对象适配器模式注意事项和细节

a、对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。根据合成复用原则,使用组合替代继承, 所以它解决了类适配器必须继承src的局限性问题,也不再要求dst必须是接口。
b、 使用成本更低,更灵活。

接口适配器

1、基本介绍
a、 一些书籍称为:适配器模式(Default Adapter Pattern)或缺省适配器模式。
b、 当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求
c、 适用于一个接口不想使用其所有的方法的情况。

2、应用实例
a、Android中的属性动画ValueAnimator类可以通过addListener(AnimatorListener listener)方
法添加监听器, 那么常规写法如右:
b、 有时候我们不想实现Animator.AnimatorListener接口的全部方法,我们只想监听onAnimationStart,我们会如下写
1.png

c、AnimatorListenerAdapter类,就是一个接口适配器,代码如右图:它空实现了Animator.AnimatorListener类(src)的所有方法.
d、AnimatorListener是一个接口.
1.png

e、 程序里的匿名内部类就是Listener 具体实现类
1.png

f、案例说明
1.png

g、代码

public class Client {
	public static void main(String[] args) {
		
		AbsAdapter absAdapter = new AbsAdapter() {
			//只需要去覆盖我们 需要使用 接口方法
			@Override
			public void m1() {
				// TODO Auto-generated method stub
				System.out.println("使用了m1的方法");
			}
		};
		
		absAdapter.m1();
	}
}

//在AbsAdapter 我们将 Interface4 的方法进行默认实现
public abstract class AbsAdapter implements Interface4 {

	//默认实现
	public void m1() {

	}

	public void m2() {

	}

	public void m3() {

	}

	public void m4() {

	}
}


public interface Interface4 {
	public void m1();
	public void m2();
	public void m3();
	public void m4();
}


SpringMVC使用的适配器源码分析

a、 SpringMvc中的HandlerAdapter, 就使用了适配器模式
b、 SpringMVC处理请求的流程回顾
c、 使用HandlerAdapter 的原因分析:可以看到处理器的类型不同,有多重实现方式,那么调用方式就不是确定的,如果需要直接调用
Controller方法,需要调用的时候就得不断是使用if else来进行判断是哪一种子类然后执行。那么
如果后面要扩展Controller,就得修改原来的代码,这样违背了OCP原则。
1.png

d、 动手写SpringMVC通过适配器设计模式获取到对应的Controller的源码
1.png
1.png

注意事项和细节

a、 三种命名方式,是根据 src是以怎样的形式给到Adapter(在Adapter里的形式)来命名的。
b、 类适配器:以类给到,在Adapter里,就是将src当做类,继承对象适配器:以对象给到,在Adapter里,将src作为一个对象,持有接口适配器:以接口给到,在Adapter里,将src作为一个接口,实现
c、 Adapter模式最大的作用还是将原本不兼容的接口融合在一起工作。
d、 实际开发中,实现起来不拘泥于我们讲解的三种经典形式

桥接模式

1、问题引入
现在对不同手机类型的不同品牌实现操作编程(比如:开机、关机、上网,打电话等),如图:
1.png

2、问题分析
传统方案解决手机使用问题(类图):
1.png

3、传统方案问题分析
a、 扩展性问题(类爆炸),如果我们再增加手机的样式(旋转式),就需要增加各个品牌手机的类,同样如果我们增加一个手机品牌,也要在各个手机样式类下增加。
b、 违反了单一职责原则,当我们增加手机样式时,要同时增加所有品牌的手机,这样增加了代码维护成本.
c、 解决方案-使用桥接模式

4、桥接模式基本介绍
a、 桥接模式(Bridge模式)是指:将实现抽象放在两个不同的类层次中,使两个层次可以独立改变。
b、 是一种结构型设计模式
c、 Bridge模式基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现(Implementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展

5、桥接模式原理类图
1.png

说明:
a、Client类:桥接模式的调用者
b、抽象类(Abstraction):维护了Implementor/即它的实现类ConcreterImplementorA..两者是聚合关系,Abstraction充当桥接类
c、RefineAbstraction: 是Abstraction抽象类的子类
d、Implementor:行为实现类的接口
e、ConcreteImplementorA/B:行为的具体实现类
f、从UML图:这里的抽象类和接口是聚合的关系,其实调用和被调用关系

6、使用模式解决问题
使用桥接模式改进传统方式,让程序具有搞好的扩展性,利用程序维护
UNL类图:
1.png

代码:

public class Client {

	public static void main(String[] args) {
		
		//获取折叠式手机 (样式 + 品牌 )
		
		Phone phone1 = new FoldedPhone(new XiaoMi());
		
		phone1.open();
		phone1.call();
		phone1.close();
		
		System.out.println("=======================");
		
		Phone phone2 = new FoldedPhone(new Vivo());
		
		phone2.open();
		phone2.call();
		phone2.close();
		
		System.out.println("==============");
		
		UpRightPhone phone3 = new UpRightPhone(new XiaoMi());
		
		phone3.open();
		phone3.call();
		phone3.close();
		
		System.out.println("==============");
		
		UpRightPhone phone4 = new UpRightPhone(new Vivo());
		
		phone4.open();
		phone4.call();
		phone4.close();
	}

}


public interface Brand {
	void open();
	void close();
	void call();
}

//折叠式手机类,继承 抽象类 Phone
public class FoldedPhone extends Phone {

	//构造器
	public FoldedPhone(Brand brand) {
		super(brand);
	}
	
	public void open() {
		super.open();
		System.out.println(" 折叠样式手机 ");
	}
	
	public void close() {
		super.close();
		System.out.println(" 折叠样式手机 ");
	}
	
	public void call() {
		super.call();
		System.out.println(" 折叠样式手机 ");
	}
}

public abstract class Phone {
	
	//组合品牌
	private Brand brand;

	//构造器
	public Phone(Brand brand) {
		super();
		this.brand = brand;
	}
	
	protected void open() {
		this.brand.open();
	}
	protected void close() {
		brand.close();
	}
	protected void call() {
		brand.call();
	}
	
}

public class UpRightPhone extends Phone {
	
		//构造器
		public UpRightPhone(Brand brand) {
			super(brand);
		}
		
		public void open() {
			super.open();
			System.out.println(" 直立样式手机 ");
		}
		
		public void close() {
			super.close();
			System.out.println(" 直立样式手机 ");
		}
		
		public void call() {
			super.call();
			System.out.println(" 直立样式手机 ");
		}
}


public class Vivo implements Brand {

	@Override
	public void open() {
		// TODO Auto-generated method stub
		System.out.println(" Vivo手机开机 ");
	}

	@Override
	public void close() {
		// TODO Auto-generated method stub
		System.out.println(" Vivo手机关机 ");
	}

	@Override
	public void call() {
		// TODO Auto-generated method stub
		System.out.println(" Vivo手机打电话 ");
	}

}


public class XiaoMi implements Brand {

	@Override
	public void open() {
		// TODO Auto-generated method stub
		System.out.println(" 小米手机开机 ");
	}

	@Override
	public void close() {
		// TODO Auto-generated method stub
		System.out.println(" 小米手机关机 ");
	}

	@Override
	public void call() {
		// TODO Auto-generated method stub
		System.out.println(" 小米手机打电话 ");
	}

}

7、在JDBC的源码解析
a、 Jdbc 的 Driver接口,如果从桥接模式来看,Driver就是一个接口,下面可以有MySQL的Driver,Oracle的Driver,这些就可以当做实现接口类
b、 代码分析+Debug源码
1.png

对jdbc源码分析的类图
1.png

8、桥接模式的注意事项和细节
a、实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统。
b、 对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成。
c、 桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本。
d、 桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程
e、 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围有一定的局限性,即需要有这样的应用场景。

9、其他应用场景
a、对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用.
b、 常见的应用场景:

  • JDBC驱动程序
  • 银行转账系统
    转账分类: 网上转账,柜台转账,AMT转账
    转账用户类型:普通用户,银卡用户,金卡用户..
  • 消息管理
    消息类型:即时消息,延时消息
    消息分类:手机短信,邮件消息,QQ消息...

装饰者模式

1、问题引入
星巴克咖啡订单项目(咖啡馆):
a、 咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)
b、 调料:Milk、Soy(豆浆)、Chocolate
c、 要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
d、 使用OO的来计算不同种类咖啡的费用: 客户可以点单品咖啡,也可以单品咖啡+调料组合。

2、解决方案
方案一:
1.png

方案一-解决星巴克咖啡订单问题分析
a、 Drink 是一个抽象类,表示饮料
b、 des就是对咖啡的描述, 比如咖啡的名字
c、 cost() 方法就是计算费用,Drink 类中做成一个抽象方法.
d、 Decaf 就是单品咖啡, 继承Drink, 并实现cost
e、 Espress && Milk 就是单品咖啡+调料, 这个组合很多
f、 问题:这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料,类的数量就会倍增,就会出现类爆炸

方案二:
前面分析到方案1因为咖啡单品+调料组合会造成类的倍增,因此可以做改进,将调料内置到Drink类,这样就不会造成类数量过多。从而提高项目的维护性(如图)
1.png
说明: milk,soy,chocolate 可以设计Boolean,表示是否要添加相应的调料.

方案二-的问题分析

  1. 方案2可以控制类的数量,不至于造成很多的类
  2. 增加或者删除调料种类时,代码的维护量很大
  3. 考虑到用户可以添加多份 调料时,可以将hasMilk 返回一个对应int
  4. 考虑使用 装饰者 模式

3、装饰着模式定义
a、装饰着模式:动态的将新功能附加到对象上。在对象功能扩展方面,他比继承更有弹性,装饰者模式也体现了开闭原则(ocp)
b、这里提到了动态的将新功能附加到对象ocp原则,,在后面的应用实例上会以代
码的形式体现,请同学们注意体会。

4、装饰者模式原理
a、 装饰者模式就像打包一个快递

  • 主体:比如:陶瓷、衣服 (Component) // 被装饰者
  • 包装:比如:报纸填充、塑料泡沫、纸板、木板(Decorator)
    b、 Component
  • 主体:比如类似前面的Drink
    c、 ConcreteComponent和DecoratorConcreteComponent:具体的主体,比如前面的各个单品咖啡Decorator: 装饰者,比如各调料.
    d、 在如图的Component与ConcreteComponent之间,如果ConcreteComponent类很多,还可以设计一个缓冲层,将共有的部分提取出来,抽象层一个类。
    e、UML类图
    1.png

5、用装饰者模式解决问题
a、设计方案
1.png
b、订单:
1.png

6、代码实现

//具体的Decorator, 这里就是调味品
public class Chocolate extends Decorator {

	public Chocolate(Drink obj) {
		super(obj);
		setDes(" 巧克力 ");
		setPrice(3.0f); // 调味品 的价格
	}

}

public class Coffee  extends Drink {

	@Override
	public float cost() {
		// TODO Auto-generated method stub
		return super.getPrice();
	}

	
}

public class DeCaf extends Coffee {

	public DeCaf() {
		setDes(" 无因咖啡 ");
		setPrice(1.0f);
	}
}

public class Decorator extends Drink {
	private Drink obj;
	
	public Decorator(Drink obj) { //组合
		// TODO Auto-generated constructor stub
		this.obj = obj;
	}
	
	@Override
	public float cost() {
		// TODO Auto-generated method stub
		// getPrice 自己价格
		return super.getPrice() + obj.cost();
	}
	
	@Override
	public String getDes() {
		// TODO Auto-generated method stub
		// obj.getDes() 输出被装饰者的信息
		return des + " " + getPrice() + " && " + obj.getDes();
	}
	
	

}

public abstract class Drink {

	public String des; // 描述
	private float price = 0.0f;
	public String getDes() {
		return des;
	}
	public void setDes(String des) {
		this.des = des;
	}
	public float getPrice() {
		return price;
	}
	public void setPrice(float price) {
		this.price = price;
	}
	
	//计算费用的抽象方法
	//子类来实现
	public abstract float cost();
	
}


public class Espresso extends Coffee {
	
	public Espresso() {
		setDes(" 意大利咖啡 ");
		setPrice(6.0f);
	}
}

public class LongBlack extends Coffee {

	public LongBlack() {
		setDes(" longblack ");
		setPrice(5.0f);
	}
}

public class Milk extends Decorator {

	public Milk(Drink obj) {
		super(obj);
		// TODO Auto-generated constructor stub
		setDes(" 牛奶 ");
		setPrice(2.0f); 
	}

}

public class ShortBlack extends Coffee{
	
	public ShortBlack() {
		setDes(" shortblack ");
		setPrice(4.0f);
	}
}

public class Soy extends Decorator{

	public Soy(Drink obj) {
		super(obj);
		// TODO Auto-generated constructor stub
		setDes(" 豆浆  ");
		setPrice(1.5f);
	}

}


public class CoffeeBar {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// 装饰者模式下的订单:2份巧克力+一份牛奶的LongBlack

		// 1. 点一份 LongBlack
		Drink order = new LongBlack();
		System.out.println("费用1=" + order.cost());
		System.out.println("描述=" + order.getDes());

		// 2. order 加入一份牛奶
		order = new Milk(order);

		System.out.println("order 加入一份牛奶 费用 =" + order.cost());
		System.out.println("order 加入一份牛奶 描述 = " + order.getDes());

		// 3. order 加入一份巧克力

		order = new Chocolate(order);

		System.out.println("order 加入一份牛奶 加入一份巧克力  费用 =" + order.cost());
		System.out.println("order 加入一份牛奶 加入一份巧克力 描述 = " + order.getDes());

		// 3. order 加入一份巧克力

		order = new Chocolate(order);

		System.out.println("order 加入一份牛奶 加入2份巧克力   费用 =" + order.cost());
		System.out.println("order 加入一份牛奶 加入2份巧克力 描述 = " + order.getDes());
	
		System.out.println("===========================");
		
		Drink order2 = new DeCaf();
		
		System.out.println("order2 无因咖啡  费用 =" + order2.cost());
		System.out.println("order2 无因咖啡 描述 = " + order2.getDes());
		
		order2 = new Milk(order2);
		
		System.out.println("order2 无因咖啡 加入一份牛奶  费用 =" + order2.cost());
		System.out.println("order2 无因咖啡 加入一份牛奶 描述 = " + order2.getDes());

	
	}

}

7、在JDK源码分析
Java的IO结构,FilterInputStream就是一个装饰者
1.png

public class Decorator {

	public static void main(String[] args) throws Exception{
		// TODO Auto-generated method stub
		
		//说明
		//1. InputStream 是抽象类, 类似我们前面讲的 Drink
		//2. FileInputStream 是  InputStream 子类,类似我们前面的 DeCaf, LongBlack
		//3. FilterInputStream  是  InputStream 子类:类似我们前面 的 Decorator 修饰者
		//4. DataInputStream 是 FilterInputStream 子类,具体的修饰者,类似前面的 Milk, Soy 等
		//5. FilterInputStream 类 有  protected volatile InputStream in; 即含被装饰者
		//6. 分析得出在jdk 的io体系中,就是使用装饰者模式
		
		DataInputStream dis = new DataInputStream(new FileInputStream("d:\\abc.txt"));
		System.out.println(dis.read());
		dis.close();
	}

}

组合模式

1、问题引出
编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系。如图:
1.png

2、解决方案(使用继承):
1.png

3、传统方案存在问题分析:
a、将学院看做是学校的子类,系是学院的子类,这样实际上是站在组织大小来进行分层次的
b、实际上我们的要求是 :在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系, 因此这种方案,不能很好实现的管理的操作,比如对学院、系的添加,删除,遍历等
c、解决方案:把学校、院、系都看做是组织结构,他们之间没有继承的关系,而是一个树形结构,可以更好的实现管理操作。 => 组合模式

4、组合模式基本介绍
a、组合模式,又叫部分整体模式,创建了对象组的树形结构,将对象组合成树状结构以表示“整体-组合”的层次关系
b、组合模式依赖树形结构来组合对象,对用表示部分以及整体层次
c、这种类型的设计模式属于结构型模式
d、组合模式是的用户对单个对象和组合对象的访问具有一致性,即:组合能以一致的方式处理个别对象以及组合对象

5、组合模式的原理类图
1.png
对原理结构图的说明:
a、Component: 这是组合中对象声明接口,在适合情况下,实现所有类共有的接口默认行为,用于访问和管理Component子部件,Component可以是抽象类或者接口
b、Leaf:在组合中表示叶子结点,叶子节点没有子节点
c、Composite: 非叶子结点,用于存储子部件,在Component 接口中实现子部件的相关操作,比如增加(add),删除(remove)

6、使用组合模式解决问题
a、分析思路:
1.png

7、代码实现:

package com.demo.composite;

public abstract class OrganizationComponent {

	private String name; // 名字
	private String des; // 说明
	
	protected  void add(OrganizationComponent organizationComponent) {
		//默认实现
		throw new UnsupportedOperationException();
	}
	
	protected  void remove(OrganizationComponent organizationComponent) {
		//默认实现
		throw new UnsupportedOperationException();
	}

	//构造器
	public OrganizationComponent(String name, String des) {
		super();
		this.name = name;
		this.des = des;
	}

	public String getName() {
		return name;
	}

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

	public String getDes() {
		return des;
	}

	public void setDes(String des) {
		this.des = des;
	}
	
	//方法print, 做成抽象的, 子类都需要实现
	protected abstract void print();
	
	
}

package com.demo.composite;

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

//University 就是 Composite , 可以管理College
public class University extends OrganizationComponent {

	List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();

	// 构造器
	public University(String name, String des) {
		super(name, des);
		// TODO Auto-generated constructor stub
	}

	// 重写add
	@Override
	protected void add(OrganizationComponent organizationComponent) {
		// TODO Auto-generated method stub
		organizationComponents.add(organizationComponent);
	}

	// 重写remove
	@Override
	protected void remove(OrganizationComponent organizationComponent) {
		// TODO Auto-generated method stub
		organizationComponents.remove(organizationComponent);
	}

	@Override
	public String getName() {
		// TODO Auto-generated method stub
		return super.getName();
	}

	@Override
	public String getDes() {
		// TODO Auto-generated method stub
		return super.getDes();
	}

	// print方法,就是输出University 包含的学院
	@Override
	protected void print() {
		// TODO Auto-generated method stub
		System.out.println("--------------" + getName() + "--------------");
		//遍历 organizationComponents 
		for (OrganizationComponent organizationComponent : organizationComponents) {
			organizationComponent.print();
		}
	}

}



package com.demo.composite;

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

public class College extends OrganizationComponent {

	//List 中 存放的Department
	List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();

	// 构造器
	public College(String name, String des) {
		super(name, des);
		// TODO Auto-generated constructor stub
	}

	// 重写add
	@Override
	protected void add(OrganizationComponent organizationComponent) {
		// TODO Auto-generated method stub
		//  将来实际业务中,Colleage 的 add 和  University add 不一定完全一样
		organizationComponents.add(organizationComponent);
	}

	// 重写remove
	@Override
	protected void remove(OrganizationComponent organizationComponent) {
		// TODO Auto-generated method stub
		organizationComponents.remove(organizationComponent);
	}

	@Override
	public String getName() {
		// TODO Auto-generated method stub
		return super.getName();
	}

	@Override
	public String getDes() {
		// TODO Auto-generated method stub
		return super.getDes();
	}

	// print方法,就是输出University 包含的学院
	@Override
	protected void print() {
		// TODO Auto-generated method stub
		System.out.println("--------------" + getName() + "--------------");
		//遍历 organizationComponents 
		for (OrganizationComponent organizationComponent : organizationComponents) {
			organizationComponent.print();
		}
	}


}






package com.demo.composite;

public class Department extends OrganizationComponent {

	//没有集合
	
	public Department(String name, String des) {
		super(name, des);
		// TODO Auto-generated constructor stub
	}

	
	//add , remove 就不用写了,因为他是叶子节点
	
	@Override
	public String getName() {
		// TODO Auto-generated method stub
		return super.getName();
	}
	
	@Override
	public String getDes() {
		// TODO Auto-generated method stub
		return super.getDes();
	}
	
	@Override
	protected void print() {
		// TODO Auto-generated method stub
		System.out.println(getName());
	}

}


package com.demo.composite;

public class Client {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		//从大到小创建对象 学校
		OrganizationComponent university = new University("清华大学", " 中国顶级大学 ");
		
		//创建 学院
		OrganizationComponent computerCollege = new College("计算机学院", " 计算机学院 ");
		OrganizationComponent infoEngineercollege = new College("信息工程学院", " 信息工程学院 ");
		
		
		//创建各个学院下面的系(专业)
		computerCollege.add(new Department("软件工程", " 软件工程不错 "));
		computerCollege.add(new Department("网络工程", " 网络工程不错 "));
		computerCollege.add(new Department("计算机科学与技术", " 计算机科学与技术是老牌的专业 "));
		
		//
		infoEngineercollege.add(new Department("通信工程", " 通信工程不好学 "));
		infoEngineercollege.add(new Department("信息工程", " 信息工程好学 "));
		
		//将学院加入到 学校
		university.add(computerCollege);
		university.add(infoEngineercollege);
		
		//university.print();
		infoEngineercollege.print();
	}

}


8、组合模式在JDK集合的源码分析
a、Java的集合类-HashMap就使用了组合模式
b、代码分析
1.png

c、类图
1.png

9、组合模式的注意事项和细节
a、简化客户端操作。客户端只需要面对一致的对象而不用考虑整体部分或者节点叶子的问题。
b、具有较强的扩展性。当我们要更改组合对象时,我们只需要调整内部的层次关系,客户端不用做出任何改动.
c、方便创建出复杂的层次结构。客户端不用理会组合里面的组成细节,容易添加节点或者叶子从而创建出复杂的树形结构
d、需要遍历组织机构,或者处理的对象具有树形结构时, 非常适合使用组合模式.
e、要求较高的抽象性,如果节点和叶子有很多差异性的话,比如很多方法和属性都不一样,不适合使用组合模式




外观模式

1、问题引出
影院管理项目。组建一个家庭影院:
DVD播放器、投影仪、自动屏幕、环绕立体声、爆米花机,要求完成使用家庭影院的
功能,其过程为:

  • 直接用遥控器:统筹各设备开关
  • 开爆米花机
  • 放下屏幕
  • 开投影仪
  • 开音响
  • 开DVD,选dvd
  • 去拿爆米花
  • 调暗灯光
  • 播放
  • 观影结束后,关闭各种设备

2、传统解决方案
1.png

3、传统存在问题分析
a、在ClientTest 的main方法中,创建各个子系统的对象,并直接去调用子系统(对象)相关方法,会造成调用过程混乱,没有清晰的过程
b、 不利于在ClientTest 中,去维护对子系统的操作
c、 解决思路:定义一个高层接口,给子系统中的一组接口提供一个一致的界面(比如在高层接口提供四个方法 ready, play, pause, end ),用来访问子系统中的一群接口
d、 也就是说 就是通过定义一个一致的接口(界面类),用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节 => 外观模式

3、 外观模式介绍
a、外观模式(Facade),也叫“过程模式:外观模式为子系统中的一组接口提供一个一致的界面”,此模式定义了一个高层接口,这个接口是的这一子系统更加容易使用
b、 外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用段只需这个接口发生调用,而无需关心这个子系统的内部细节

4、外观模型的原理类图
1.png
对类图的说明:
a、外观类(Facade):为调用端提供统一的调用接口,外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给适当子系统处理
b、调用者(Client):外观接口的调用者
c、子系统的集合: 值模块或者子系统,处理Facade对象指派的任务,他是功能的实际提供者。

5、外观模式处理影院问题说明
a、外观模式可以理解为转换一群接口,客户只要调用一个接口,而不用调用多个接口才能达到目的。比如:在pc上安装软件的时候经常有一键安装选项(省去选择安装目录、安装的组件等等),还有就是手机的重启功能(把关机和启动合为一个操作)。
b、 外观模式就是解决多个复杂接口带来的使用困难,起到简化用户操作的作用
c、示意图说明
1.png

6、代码说明:

public class Client {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//这里直接调用。。 很麻烦
		HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade();
		homeTheaterFacade.ready();
		homeTheaterFacade.play();
		
		
		homeTheaterFacade.end();
	}

}



public class HomeTheaterFacade {
	
	//定义各个子系统对象
	private TheaterLight theaterLight;
	private Popcorn popcorn;
	private DVDPlayer dVDPlayer;
	
	//构造器
	public HomeTheaterFacade() {
		super();
		this.theaterLight = TheaterLight.getInstance();
		this.popcorn = Popcorn.getInstance();
		this.dVDPlayer = DVDPlayer.getInstanc();
	}

	//操作分成 4 步
	
	public void ready() {
		popcorn.on();
		popcorn.pop();
		theaterLight.dim();
		dVDPlayer.on();
	}
	
	public void play() {
		dVDPlayer.play();
	}
	
	public void pause() {
		dVDPlayer.pause();
	}
	
	public void end() {
		popcorn.off();
		theaterLight.bright();
		dVDPlayer.off();
	}
}


public class TheaterLight {

	private static TheaterLight instance = new TheaterLight();

	public static TheaterLight getInstance() {
		return instance;
	}

	public void on() {
		System.out.println(" TheaterLight on ");
	}

	public void off() {
		System.out.println(" TheaterLight off ");
	}

	public void dim() {
		System.out.println(" TheaterLight dim.. ");
	}

	public void bright() {
		System.out.println(" TheaterLight bright.. ");
	}
}


public class Popcorn {
	
	private static Popcorn instance = new Popcorn();
	
	public static Popcorn getInstance() {
		return instance;
	}
	
	public void on() {
		System.out.println(" popcorn on ");
	}
	
	public void off() {
		System.out.println(" popcorn ff ");
	}
	
	public void pop() {
		System.out.println(" popcorn is poping  ");
	}
}


public class DVDPlayer {
	
	//使用单例模式, 使用饿汉式
	private static DVDPlayer instance = new DVDPlayer();
	
	public static DVDPlayer getInstanc() {
		return instance;
	}
	
	public void on() {
		System.out.println(" dvd on ");
	}
	public void off() {
		System.out.println(" dvd off ");
	}
	
	public void play() {
		System.out.println(" dvd is playing ");
	}
	
	//....
	public void pause() {
		System.out.println(" dvd pause ..");
	}
}

7、外观模式在Mybatis框架的应用代码分析
a、Mybatis中的COnfiguration去创建MetaObject对象使用到外观模式
b、示意图
1.png
c、对源码中使用到的外观模式的角色类图
1.png

8、外观模式的注意事项和细节
a、外观模式对外屏蔽了子系统的细节,有词外观模式降低了客户端对子系统使用的复杂度
b、外观模式对客户端与子系统的耦合关系 - 解耦,让子系统内部的模块更易维护和扩展
c、通过合理的使用外观模式,可以帮我们更好的划分访问的层次
d、当系统需要进行分层设计时,可以考虑使用Facade模式
e、在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个Facade类,来提供遗留系统的比较清晰简单的接口,让新系统与Facade类交互,提高复用性
f、不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好。要以让系统有层次,利于维护为目的。

享元模式

1、小型的外包项目,给客户A做一个产品展示网站,客户A的朋友感觉效果不错,也希望做这样的产品展示网站,但是要求都有些不同:
a、 有客户要求以新闻的形式发布
b、 有客户人要求以博客的形式发布
c、 有客户希望以微信公众号的形式发布

2、传统方案解决网站展现项目
a、 直接复制粘贴一份,然后根据客户不同要求,进行定制修改
b、 给每个网站租用一个空间
c、 方案设计示意图 客户A的网站
1.png

3、传统方案解决问题分析
a、需要的网站结构相似度很高,而且都不是高访问量网站,如果分成多个虚拟空间来处理,相当于一个相同网站的实例对象很多,造成服务器的资源浪费
b、 解决思路:整合到一个网站中,共享其相关的代码和数据,对于硬盘、内存、CPU、数据库空间等服务器资源都可以达成共享,减少服务器资源
c、 对于代码来说,由于是一份实例,维护和扩展都更加容易
d、 上面的解决思路就可以使用 享元模式 来解决、

4、享元模式基本介绍
a、享元模式(Flyweight Pattern) 也叫 蝇量模式: 运用共享技术有效地支持大量细粒度的对象
b、 常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在
这些连接对象中有我们需要的则直接拿来用,避免重新创建,如果没有我们需要的,则创建一个
c、 享元模式能够解决重复对象的内存浪费的问题,当系统中有大量相似对象,需要缓冲池时。不需总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率
d、 享元模式经典的应用场景就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式

4、原理类型图
1.png

类图说明:
a、FlyWeight 是抽象的享元角色, 他是产品的抽象类, 同时定义出对象的外部状态内部状态(后面介绍) 的接口或实现
b、ConcreteFlyWeight 是具体的享元角色,是具体的产品类,实现抽象角色定义相关业务
c、 UnSharedConcreteFlyWeight 是不可共享的角色,一般不会出现在享元工厂。
d、FlyWeightFactory 享元工厂类,用于构建一个池容器(集合),同时提供从翅中获取对象方法

5、内部状态和外部状态
比如围棋、五子棋、跳棋,它们都有大量的棋子对象,围棋和五子棋只有黑白两色,跳棋颜色多一
点,所以棋子颜色就是棋子的内部状态;而各个棋子之间的差别就是位置的不同,当我们落子后,
落子颜色是定的,但位置是变化的,所以棋子坐标就是棋子的外部状态
a、 享元模式提出了两个要求:细粒度和共享对象。这里就涉及到内部状态和外部状态,即将对象的信息分为两个部分:内部状态外部状态
b、内部状态指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变
c、外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。
d、举个例子:围棋理论上有361个空位可以放棋子,每盘棋都有可能有两三百个棋子对象产生,因为内存空间有限,一台服务器很难支持更多的玩家玩围棋游戏,如果用享元模式来处理棋子,那么棋子对象就可以减少到只有两个实例,这样就很好的解决了对象的开销问题

6、使用享元模式解决问题
a、UML图解
1.png
b、代码实现

public class Client {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		// 创建一个工厂类
		WebSiteFactory factory = new WebSiteFactory();

		// 客户要一个以新闻形式发布的网站
		WebSite webSite1 = factory.getWebSiteCategory("新闻");

		
		webSite1.use(new User("tom"));

		// 客户要一个以博客形式发布的网站
		WebSite webSite2 = factory.getWebSiteCategory("博客");

		webSite2.use(new User("jack"));

		// 客户要一个以博客形式发布的网站
		WebSite webSite3 = factory.getWebSiteCategory("博客");

		webSite3.use(new User("smith"));

		// 客户要一个以博客形式发布的网站
		WebSite webSite4 = factory.getWebSiteCategory("博客");

		webSite4.use(new User("king"));
		
		System.out.println("网站的分类共=" + factory.getWebSiteCount());
	}

}

// 网站工厂类,根据需要返回压一个网站
public class WebSiteFactory {

	
	//集合, 充当池的作用
	private HashMap<String, ConcreteWebSite> pool = new HashMap<>();
	
	//根据网站的类型,返回一个网站, 如果没有就创建一个网站,并放入到池中,并返回
	public WebSite getWebSiteCategory(String type) {
		if(!pool.containsKey(type)) {
			//就创建一个网站,并放入到池中
			pool.put(type, new ConcreteWebSite(type));
		}
		
		return (WebSite)pool.get(type);
	}
	
	//获取网站分类的总数 (池中有多少个网站类型)
	public int getWebSiteCount() {
		return pool.size();
	}
}

public abstract class WebSite {

	public abstract void use(User user);//łéĎ󡽡¨
}

//具体网站
public class ConcreteWebSite extends WebSite {

	//共享的部分,内部状态
	private String type = ""; //网站发布的形式(类型)

	
	//构造器
	public ConcreteWebSite(String type) {
		
		this.type = type;
	}


	@Override
	public void use(User user) {
		// TODO Auto-generated method stub
		System.out.println("网站的发布形式为:" + type + " 在使用中 .. 使用者是" + user.getName());
	}
	
	
}

public class User {
	
	private String name;

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

	public String getName() {
		return name;
	}

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

7、在JDK-Integer的应用代码分析
a、图解
1.png
b、代码说明

public class FlyWeight {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//如果 Integer.valueOf(x) x 在  -128 --- 127 直接,就是使用享元模式返回,如果不在
		//范围类,则仍然 new 
		
		//小结:
		//1. 在valueOf 方法中,先判断值是否在 IntegerCache 中,如果不在,就创建新的Integer(new), 否则,就直接从 缓存池返回
		//2. valueOf 方法,就使用到享元模式
		//3. 如果使用valueOf 方法得到一个Integer 实例,范围在 -128 - 127 ,执行速度比 new 快
		
		
		Integer x = Integer.valueOf(127); // 得到 x实例,类型 Integer
		Integer y = new Integer(127); // 得到 y 实例,类型 Integer
		Integer z = Integer.valueOf(127);//..
		Integer w = new Integer(127);
		
		
		
		System.out.println(x.equals(y)); // 大小,true
		System.out.println(x == y ); //  false
		System.out.println(x == z ); // true
		System.out.println(w == x ); // false
		System.out.println(w == y ); // false
		
		
		Integer x1 = Integer.valueOf(200);
		Integer x2 = Integer.valueOf(200);
		System.out.println("x1==x2" + (x1 == x2)); // false

	}

}

8、注意事项和细节
a、享元模式中,“享”就是表示共享,“元”表示对象
b、系统中有大量对象,这些对象消耗大量内存,并且对象的状态大部分可以外部化时,我们就可以考虑使用享元模式
c、用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所表示的对象,用HashMap/HashTable存储
d、享元模式大大减少了对象的创建,降低了程序内存的占用,提高效率
e、享元模式提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变,这是我们使用享元模式需要注意的地方.
f、使用享元模式时,注意划分内部状态和外部状态,并且需要有一个工厂类加以控制。
g、享元模式经典的应用场景是需要缓冲池的场景,比如 String常量池、数据库连接池




代理模式

代理模式基本介绍

1、代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过道理对象访问目标对象。这样做得好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
2、被代理的对象可以是远程对象,创建开销大的对象或需要安全控制的对象
3、代理模式有三种不同的形式,主要有三种:静态代理、动态代理(JSK代理、接口代理)、Cglib代理(可以在内存动态的创建对象,而不需要实现接口,他是属于动态代理的范畴)
4、代理模式示意图
1.png

静态代理

1、 基本介绍
静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同的父类
2、问题引入
a、定义一个接口:ITeacherDao
b、 目标对象TeacherDAO实现接口ITeacherDAO
c、 使用静态代理方式,就需要在代理对象TeacherDAOProxy中也实现ITeacherDAO
d、 调用的时候通过调用代理对象的方法来调用目标对象.
e、 特别提醒:代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来
调用目标对象的方法。

3、思路分析
1.png

4、代码实现

public class Client {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//创建目标对象(被代理对象)
		TeacherDao teacherDao = new TeacherDao();
		
		//创建代理对象, 同时将被代理对象传递给代理对象
		TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);
		
		//通过代理对象,调用到被代理对象的方法
		//即:执行的是代理对象的方法,代理对象再去调用目标对象的方法 
		teacherDaoProxy.teach();
	}

}


//接口
public interface ITeacherDao {
	
	void teach(); // 授课的方法
}


public class TeacherDao implements ITeacherDao {

	@Override
	public void teach() {
		// TODO Auto-generated method stub
		System.out.println(" 老师授课中  。。。。。");
	}

}

//代理对象,静态代理
public class TeacherDaoProxy implements ITeacherDao{
	
	private ITeacherDao target; // 目标对象,通过接口来聚合
	
	
	//构造器
	public TeacherDaoProxy(ITeacherDao target) {
		this.target = target;
	}



	@Override
	public void teach() {
		// TODO Auto-generated method stub
		System.out.println("开始代理  完成某些操作。。。。。 ");//方法
		target.teach();
		System.out.println("提交。。。。。");//方法
	}

}

5、静态代理优缺点
a、优点:在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展
b、缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类
c、一旦接口增加方法,目标对象与代理对象都要维护

动态代理

1、基本介绍
a、代理对象不需要实现接口,但是目标对象需要实现接口,否则不能用动态代理
b、代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
c、动态代理也叫作:JDK代理、接口代理

2、JDK中生成代理对象的API
a、代理类所在包:java.lang.reflect.Proxy
b、JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接受三个参数,完整的额学法是:static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )

3、动态代理实例
将前面的静态代理改进成动态代理模式(即:JDK代理模式)

4、思维类图
1.png

5、代码实现

public class Client {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//创建目标对象
		ITeacherDao target = new TeacherDao();
		
		//给目标对象,创建代理对象, 可以转成 ITeacherDao
		ITeacherDao proxyInstance = (ITeacherDao)new ProxyFactory(target).getProxyInstance();
	
		// proxyInstance=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象
		System.out.println("proxyInstance=" + proxyInstance.getClass());
		
		//通过代理对象,调用目标对象的方法
		//proxyInstance.teach();
		
		proxyInstance.sayHello(" tom ");
	}

}

//接口
public interface ITeacherDao {

	void teach(); // 授课方法
	void sayHello(String name);
}

public class TeacherDao implements ITeacherDao {

	@Override
	public void teach() {
		// TODO Auto-generated method stub
		System.out.println(" 老师授课中.... ");
	}

	@Override
	public void sayHello(String name) {
		// TODO Auto-generated method stub
		System.out.println("hello " + name);
	}
	
}

public class ProxyFactory {

	//维护一个目标对象 , Object
	private Object target;

	//构造器 , 对target 进行初始化
	public ProxyFactory(Object target) {
		
		this.target = target;
	} 
	
	//给目标对象 生成一个代理对象
	public Object getProxyInstance() {
		
		//说明
		/*
		 *  public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
                                          
            //1. ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定
            //2. Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型
            //3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行的目标对象方法作为参数传入
		 */
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
				target.getClass().getInterfaces(), 
				new InvocationHandler() {
					
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						// TODO Auto-generated method stub
						System.out.println("JDK代理开始~~");
						//反射机制调用目标对象的方法
						Object returnVal = method.invoke(target, args);
						System.out.println("JDK代理提交");
						return returnVal;
					}
				}); 
	}
	
	
}

Cglib代理

1、基本介绍
a、静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只
是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现
代理-这就是Cglib代理
b、 Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功
能扩展, 有些书也将Cglib代理归属到动态代理。
c、 Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接
口.它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截
d、 在AOP编程中如何选择代理模式:

  • 目标对象需要实现接口,用JDK代理
  • 目标对象不需要实现接口,用Cglib代理

e、 Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类

2、实现步骤
a、需要引入cglib的jar包
1.png
b、在内存中动态构建子类,注意代理的类不能为final,否则报错java.lang.IllegalArgumentException:
c、目标对象的方法如果是final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法

3、应用实例
a、要求
将前面的案例用Cglib代理模式实现
b、思路图解
1.png
c、代码实现

public class Client {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//创建目标对象
		TeacherDao target = new TeacherDao();
		//获取到代理对象,并且将目标对象传递给代理对象
		TeacherDao proxyInstance = (TeacherDao)new ProxyFactory(target).getProxyInstance();

		//执行代理对象的方法,触发intecept 方法,从而实现 对目标对象的调用
		String res = proxyInstance.teach();
		System.out.println("res=" + res);
	}

}

public class TeacherDao {

	public String teach() {
		System.out.println(" 老师授课中  , 我是cglib代理,不需要实现接口 ");
		return "hello";
	}
}

public class ProxyFactory implements MethodInterceptor {

	//维护一个目标对象
	private Object target;
	
	//构造器,传入一个被代理的对象
	public ProxyFactory(Object target) {
		this.target = target;
	}

	//返回一个代理对象:  是 target 对象的代理对象
	public Object getProxyInstance() {
		//1. 创建一个工具类
		Enhancer enhancer = new Enhancer();
		//2. 设置父类
		enhancer.setSuperclass(target.getClass());
		//3. 设置回调函数
		enhancer.setCallback(this);
		//4. 创建子类对象,即代理对象
		return enhancer.create();
		
	}
	

	//重写  intercept 方法,会调用目标对象的方法
	@Override
	public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("Cglib代理模式 ~~ 开始");
		Object returnVal = method.invoke(target, args);
		System.out.println("Cglib代理模式 ~~ 提交");
		return returnVal;
	}

}

几种常见的代理模式介绍 --- 几种变体

1、防火墙代理
内网通过代理穿透防火墙,实现对公网的访问。
2、 缓存代理
比如:当请求图片文件等资源时,先到缓存代理取,如果取到资源则ok,如果取不到资源,再到公网或者数据库取,然后缓存。
3、 远程代理
远程对象的本地代表,通过它可以把远程对象当本地对象来调用。远程代理通过网络和真正的远程对象沟通信息。
4、同步代理
主要使用在多线程编程中,完成多线程间同步工作

Q.E.D.

知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议

一个萌新程序猿