本文是学习设计模式后,自己做的笔记。
学习资源有菜鸟教程以及尚硅谷韩顺平图解设计模式。
1 适配器模式
概述:
一种作为两个不兼容接口的桥梁的模式,结合两个独立接口的功能。
其思想类似于生活中的插口转换器。
- 适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)
- 适配器模式属于结构型模式
- 主要分为三类:类适配器模式、对象适配器模式、接口适配器模式
特点:
可以将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
角色:
1、被适配者
2、适配器(中间桥梁作用)
3、适配者
类图:
- 在适配器中持有某需要适配的对象,然后进行转换适配。
类适配器:
要继承适配类并且实现适配器接口。
会导致无法再继承其他类,限制了类的兼容。
步骤代码实现:
- 创建适配者
// 1、适配者
public class Voltage {
public int ouput220v() {
int src = 220;
System.out.println(" 输出220V电压!");
return src;
}
}
- 创建适配器接口
//2、适配器接口
public interface Voltage5V {
int output5v();
}
- 创建适配器对象(适配器对象需要间接拥有需要适配的对象)
//3、适配器实现对象(继承与实现)
public class VoltageAdapter extends Voltage implements Voltage5V{
@Override
public int output5v() {
int src = ouput220v();
int dstv = src/44;
return dstv;
}
}
- 定义被适配对象
//4、定义被适配对象
public class Phone {
public void charging(Voltage5V voltage5V ) {
if(voltage5V.output5v()==5) {
System.out.println("电压为5V可以充电!");
}else if(voltage5V.output5v()>5)
{
System.out.println("电压过高,不可充电!");
}
}
}
- 调用
// 5、客户端调用
public class client {
public static void main(String[] args) {
System.out.println("========类适配器模式==========");
Phone phone = new Phone();
phone.charging(new VoltageAdapter());
}
}
接口适配器:
一些书籍称为:适配器模式(Default Adapter Pattern)或缺省适配器模式
设计一个抽象类实现接口,并为该接口所有方法提供一个默认实现,该抽象类所有子类可以有选择的覆盖父类的方法来实现功能。
核心思路:当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求。
适用于一个接口不想使用其所有的方法的情况。
步骤代码实现:
- 写一个接口
//1、写一个接口
public interface interface1 {
void m1();
void m2();
void m3();
void m4();
}
2、写一个抽象类空实现所有方法
//2、写一个抽象类空实现所有方法
//空实现所有接口方法
public abstract class AbcAdapter implements interface1{
@Override
public void m1() {
// TODO Auto-generated method stub
}
@Override
public void m2() {
// TODO Auto-generated method stub
}
@Override
public void m3() {
// TODO Auto-generated method stub
}
@Override
public void m4() {
// TODO Auto-generated method stub
}
}
- 调用时有选择得进行复写
//3、调用时有选择得进行复写
public class Client {
public static void main(String[] args) {
AbcAdapter absadapter = new AbcAdapter() {
public void m1() {
System.out.println("调用m1!");
}
};
absadapter.m1();
}
}
对象适配器:
不在继承适配器类,而是持有适配器类,解决兼容性问题。满足合成复用原则,是常用得一种。
满足合成复用原则,使用组合替代继承, 所以它解决了类适配器必须继承 src 的局限性问题,也不再要求 dst必须是接口。
成本低,更灵活。
步骤代码实现:
- 创建适配者
// 1、适配者
public class Voltage {
public int ouput220v() {
int src = 220;
System.out.println(" 输出220V电压!");
return src;
}
}
- 创建适配器接口
//2、适配器接口
public interface Voltage5V {
int output5v();
}
- 创建适配器对象(在此处聚合一个适配者对象)
//3、适配器对象(聚合一个适配者对象)
public class VoltageAdapter implements Voltage5V{
private Voltage voltege220v;
public VoltageAdapter(Voltage voltage220v) {
this.voltege220v=voltage220v;
}
@Override
public int output5v() {
if(voltege220v != null) {
int src = voltege220v.ouput220v();
int dstv = src/44;
return dstv;
}else {
return 0;
}
}
- 定义被适配对象
}
//4、定义被适配对象
public class Phone {
public void charging(Voltage5V voltage5V ) {
if(voltage5V.output5v()==5) {
System.out.println("电压为5V可以充电!");
}else if(voltage5V.output5v()>5)
{
System.out.println("电压过高,不可充电!");
}
}
}
- 调用
// 5、客户端调用
public class client {
public static void main(String[] args) {
System.out.println("========对象适配器模式==========");
Phone phone = new Phone();
phone.charging(new VoltageAdapter(new Voltage()));
}
}
适配器模式的注意事项和细节
- 三种命名方式,是根据 src 是以怎样的形式给到 Adapter(在 Adapter 里的形式)来命名的。
- 类适配器:以类给到,在 Adapter 里,就是将 src 当做类,继承
对象适配器:以对象给到,在 Adapter 里,将 src 作为一个对象,持有
接口适配器:以接口给到,在 Adapter 里,将 src 作为一个接口,实现 - Adapter 模式最大的作用还是将原本不兼容的接口融合在一起工作
2 桥接模式
概述:
一种将实现与抽象放在两个不同得类层次,使得两个类层次可以独立改变的设计模式。
特点:
把类的抽象跟行为分离开。保持各个部分的独立性以及他们的功能拓展。
基于类的最小设计原则,通过使用封装、聚合以及继承等行为让不同的类承担不同的责任。
用于两个层次的组合,可将类数由n*n变为n+n
角色:
1、抽象类(聚合接口以及定义抽象)
2、接口
3、接口实现类
4、抽象类实现类
5、具体调用端
类图:
- 在phone这一抽象类中队brand和继承者进行组合,对于类的多种组合的类数量可以由阶层关系转为相加关系。
步骤代码实现:
实现手机样式跟手机品牌的组合
- 定义行为接口
//1、定义行为接口
public interface Interface {
void open();
void close();
void call();
}
- 实现行为接口
//2、行为接口的实现
public class Xiaomi implements Interface {
@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(" 小米手机打电话!");
}
}
public class Vivo implements Interface{
@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手机打电话!");
}
}
- 定义桥接类抽象
可以由代码看出,此处的接口实现选取与抽象行为的实现是独立的,可以自由的选择进行组合。
//3、抽象类的定义(桥接类)
//桥接类,把实现的接口和抽象进行分离
public abstract class Phone {
Interface brand; //聚合接口
public Phone(Interface brand) {
this.brand = brand;
}
//抽象行为
protected void open() {
brand.open();
}
protected void close() {
brand.close();
}
protected void call() {
brand.call();
}
}
4、桥接类的实现
此处进行具体的组合
//4、桥接类的实现
public class FoldPhone extends Phone{
public FoldPhone(Interface 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(" 折叠手机样式手机");
}
}
- 调用
//5、使用
public class Client {
public static void main(String[] args) {
FoldPhone foldPhone = new FoldPhone(new Vivo());
foldPhone.call();
foldPhone.close();
foldPhone.open();
}
}
优缺点分析:
优点:
1、抽象和实现的分离。 (接口与抽象行为分离)
2、优秀的扩展能力。
3、实现细节对客户透明。
4、替代多层继承,减少类个数
缺点:
1、桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
2、桥接模式要求分析出系统两个层次,即两个独立变化的维度。(此例中的手机品牌和手机样式)
3 装饰模式
概述:
是一种允许向一个现有的对象添加新的功能,同时又不改变其结构的设计模式。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
动态的将新功能附加在对象上,在对象扩展上比继承更有弹性,装饰者模式体现了开闭原则。
核心想实现的功能是动态的装饰一个类,给类动态的增加新功能而不使用大量的子类。
角色:
1、抽象构件(Component)角色:给出一个抽象接口,以规范准备接受附加责任的对象。
2、具体构件(ConCreteComponent)角色:定义一个将要接受附加责任的类。
3、装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
4、具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。
类图:
图中的左边是产品,右边是装饰类,在装饰类中持有产品示例,对产品进行装饰后返回装饰完的产品。
步骤代码实现:
实现咖啡点餐与加入调料
- 创建抽象产品类
//1、创建抽象产品类
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();
}
- 创建产品实体类(用于缓冲)
//2、创建实体类实现抽象类(用于缓冲)
public class Coffee extends Drink{
@Override
public float cost() {
return super.getPrice();
}
}
- 创建各种实体类
//3、各种实体类
public class ShortBlack extends Coffee{
public ShortBlack() {
setDes(" shortblack");
setPrice(4.0f);
}
}
public class LongBlack extends Coffee{
public LongBlack() {
setDes(" longblack");
setPrice(5.0f);
}
}
public class Espresso extends Coffee{
public Espresso() {
setDes("意大利咖啡");
setPrice(6.0f);
}
}
- 创建装饰者类(内部聚合产品类以及继承产品类,继承用于调用产品类元素,聚合用于包装某个产品类)
//4、创建装饰者类(内部聚合产品类以及继承产品类,继承用于调用产品类元素,聚合用于包装某个产品类)
public class Decorator extends Drink{
Drink obj; //内部再聚合一个Drink,被装饰
public Decorator(Drink obj) {
this.obj = obj;
}
@Override
public float cost() {
return super.getPrice() + obj.getPrice(); //聚合类的价格加上一次继承下来的价格(也就是装饰类中定义的价格),类似递归获取,然后保存在属性中。
}
@Override
public String getDes() {
//obj.getDes()输出被装饰者的类
return des + " " + getPrice() + "&&" + obj.getDes();
}
}
- 继承实现装饰类
//5、继承实现装饰类
public class Milk extends Decorator{
public Milk(Drink obj) {
super(obj);
setDes(" 牛奶");
setPrice(2.0f);
}
}
public class Soy extends Decorator{
public Soy(Drink obj) {
super(obj);
setDes(" 豆浆");
setPrice(1.5f);
}
}
public class Chocolate extends Decorator{
public Chocolate(Drink obj) {
super(obj);
setDes(" 巧克力");
setPrice(3.0f);
}
}
- 调用
//6、具体调用
public class Client {
public static void main(String[] args) {
Drink order = new LongBlack();
System.out.println("费用1:" + order.cost());
System.out.println("描述:" + order.getDes());
order = new Milk(order); //传入被装饰
System.out.println("加入一份牛奶费用:" + order.cost());
System.out.println("加入一份牛奶描述:" + order.getDes());
}
}
优缺点分析:
优点:
装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点:
多层装饰比较复杂。系统复杂性上升。
4 组合模式
概述:
组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系。
一种依据树形结构来组合对象,用来表示部分以及整体层次的设计模式。
组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象。
也就是说去除了层次等级关系,由用户自己定义对象之间的关系。
特点:
它创建了对象组的树形结构。
把有等级层次的对象划分成统一层次的对象,并在同一层次中进行树状的构建(对象内部聚合对象list)。
将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
角色:
1、抽象构成者
2、实体构成者(内部相互聚合形成树状结构)
类图示例:
看与看出来,各个类之间(部门、学院、学校)去除了等级关系,由他们进行组合来形成自定义的等级关系。
步骤代码实现:
目标:实现学校部门专业遍历,构建同层次的树状结构。
- 创建抽象构成者
//1、创建抽象构成者
public abstract class OrganizationComponent {
private String name; //名字
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;
}
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;
}
protected abstract void print();
}
- 创建构成类实例:
//2、实例构成类
public class University extends OrganizationComponent {
ArrayList<OrganizationComponent> organizationcomponents =new ArrayList<OrganizationComponent>(); //用list来储存分支,形成树状
public University(String name, String des) {
super(name, des);
}
protected void add(OrganizationComponent organizationcomponent) {
organizationcomponents.add(organizationcomponent);
}
protected void remove(OrganizationComponent organizationcomponent) {
organizationcomponents.remove(organizationcomponent);
}
public String getName() {
return super.getName();
}
public String getDes() {
return super.getDes();
}
//遍历输出
protected void print() {
System.out.println("---------------"+getName()+"----------------");
for(OrganizationComponent organizationcomponent:organizationcomponents) {
organizationcomponent.print();
}
}
}
public class College extends OrganizationComponent{
public College(String name, String des) {
super(name, des);
// TODO Auto-generated constructor stub
}
ArrayList<OrganizationComponent> organizationcomponents =new ArrayList<OrganizationComponent>();
protected void add(OrganizationComponent organizationcomponent) {
organizationcomponents.add(organizationcomponent);
}
protected void remove(OrganizationComponent organizationcomponent) {
organizationcomponents.remove(organizationcomponent);
}
public String getName() {
return super.getName();
}
public String getDes() {
return super.getDes();
}
//遍历输出
protected void print() {
System.out.println("---------------"+getName()+"----------------");
for(OrganizationComponent organizationcomponent:organizationcomponents) {
organizationcomponent.print();
}
}
}
//最后节点类不用储存分支
public class Departments extends OrganizationComponent{
public Departments(String name, String des) {
super(name, des);
}
public String getName() {
return super.getName();
}
public String getDes() {
return super.getDes();
}
@Override
protected void print() {
System.out.println(getName());
}
}
- 直接调用(自己对各个类进行组合,形成自定义的等级关系)
//3. 直接调用
public class Client {
public static void main(String[] args) {
OrganizationComponent university = new University("惠州学院","惠州大学");
//创建院级
OrganizationComponent computerCollege = new College("计算机学院","我的学院");
OrganizationComponent infoCollege = new College("信息学院","我以前的学院");
//创建专业
computerCollege.add(new Departments("软件工程", "我的专业"));
computerCollege.add(new Departments("计算机技术", "我的第一专业"));
infoCollege.add(new Departments("信息工程", "我不是很了解的专业"));
infoCollege.add(new Departments("电子信息工程", "我不了解的专业"));
//将部门加入学校
university.add(computerCollege);
university.add(infoCollege);
//输出
university.print();
}
}
组合模式的注意事项和细节
- 简化客户端操作。客户端只需要面对一致的对象而不用考虑整体部分或者节点叶子的问题。
- 具有较强的扩展性。当我们要更改组合对象时,我们只需要调整内部的层次关系,客户端不用做出任何改动.
- 方便创建出复杂的层次结构。客户端不用理会组合里面的组成细节,容易添加节点或者叶子从而创建出复杂的
树形结构 - 需要遍历组织机构,或者处理的对象具有树形结构时, 非常适合使用组合模式.
- 要求较高的抽象性,如果节点和叶子有很多差异性的话,比如很多方法和属性都不一样,不适合使用组合模式
5 外观模式
概述:
一种可以隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口的设计模式。
强调的是对功能调用的进一步抽象。
特点:
向现有的系统添加一个接口,来隐藏系统的复杂性。
客户端不需要知道系统内部的复杂联系,整个系统只需提供一个"接待员"即可。
在客户端和复杂系统之间再加一层,这一层将调用顺序、依赖关系等处理好。
通过定义一个一致的接口,用以屏蔽内部子系统的细节,是的调用端只与这个接口发生调用,无需关注调用的细节。
角色:
- 外观类(Facade): 为调用端提供统一的调用接口, 外观类知道哪些子系统负责处理请求,从而将调用端的请求代
理给适当子系统对象 - 调用者(Client): 外观接口的调用者
- 子系统的集合:指模块或者子系统,处理 Facade 对象指派的任务,他是功能的实际提供者
步骤代码实现:
- 创建具体实现功能的类
//1、创建某类
public class Rectangle {
public void draw() {
System.out.println("Rectangle::draw()");
}
}
public class Square{
public void draw() {
System.out.println("Square::draw()");
}
}
public class Circle {
public void draw() {
System.out.println("Circle::draw()");
}
}
- 创建外观类用于抽线功能
//2、创建外观类
public class ShapeMaker {
private Shape circle;
private Shape rectangle;
private Shape square;
public ShapeMaker() { //内部聚合类
circle = new Circle();
rectangle = new Rectangle();
square = new Square();
}
public void drawCircle(){
circle.draw();
}
public void drawRectangle(){
rectangle.draw();
}
public void drawSquare(){
square.draw();
}
}
- 客户端调用
//3、客户端调用
public class FacadePatternDemo {
public static void main(String[] args) {
ShapeMaker shapeMaker = new ShapeMaker();
shapeMaker.drawCircle();
shapeMaker.drawRectangle();
shapeMaker.drawSquare();
}
}
6 享元模式
概述:
一种主要用于减少创建对象的数量,以减少内存占用和提高性能的设计模式。
核心思想是像java的String那样子,创建字符串是先放入缓冲池,当再次创建相同字符串时会直接使用缓冲池中已有的例子,否则才创建新的String对象。
特点:
运用共享技术有效的支持大量细粒度的对象。
常用于系统底层开发,解决系统底层问题。
可用于解决重复对象内存浪费的问题。
经常用于池技术。
角色:
1、FlyWeight(抽象类):定义出外部状态以及内部状态
2、FlyWeight实现类
- FlyWeight实现类1:内部状态类:对象得以依赖分别的标志,是随环境改变的,不可共享的。
- FlyWeight实现类2:内部状态类:储存在对象内部的且不会随对象改变的,可以共享的类。
3、FlyWeightFactory(工厂类):可以取出想要的FlyWeight类。缓冲层。构建池的容器。提供从池中获取对象的相关方法
类图实例:
步骤代码实现:
- 创建一个实例对象接口
public interface Shape {
void draw();
}
- 创建实现接口的实体类
public class Circle implements Shape {
private String color;
private int x;
private int y;
private int radius;
public Circle(String color){
this.color = color;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setRadius(int radius) {
this.radius = radius;
}
@Override
public void draw() {
System.out.println("Circle: Draw() [Color : " + color
+", x : " + x +", y :" + y +", radius :" + radius);
}
}
- 创建一个工厂,生成基于给定信息的实体类的对象
public class ShapeFactory {
private static final HashMap<String, Shape> circleMap = new HashMap<>();
public static Shape getCircle(String color) {
Circle circle = (Circle)circleMap.get(color);
if(circle == null) {
circle = new Circle(color);
circleMap.put(color, circle);
System.out.println("Creating circle of color : " + color);
}
return circle;
}
}
这里就使用了一个HashMap来进行处理数据,当在HashMap中找不到数据时才会创建新的对象。
- 使用该工厂,通过传递颜色信息来获取实体类的对象
public class FlyweightPatternDemo {
private static final String colors[] =
{ "Red", "Green", "Blue", "White", "Black" };
public static void main(String[] args) {
for(int i=0; i < 20; ++i) {
Circle circle =
(Circle)ShapeFactory.getCircle(getRandomColor());
circle.setX(getRandomX());
circle.setY(getRandomY());
circle.setRadius(100);
circle.draw();
}
}
private static String getRandomColor() {
return colors[(int)(Math.random()*colors.length)];
}
private static int getRandomX() {
return (int)(Math.random()*100 );
}
private static int getRandomY() {
return (int)(Math.random()*100);
}
}
享元模式的注意事项和细节
- 在享元模式这样理解,“享”就表示共享,“元”表示对象
- 系统中有大量对象,这些对象消耗大量内存,并且对象的状态大部分可以外部化时,我们就可以考虑选用享元模式
- 用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象,用 HashMap/HashTable 存储
- 享元模式大大减少了对象的创建,降低了程序内存的占用,提高效率
- 享元模式提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变,这是我们使用享元模式需要注意的地方.
- 使用享元模式时,注意划分内部状态(可以理解为一个类的结构,是不变的部分)和外部状态(一个类的数据部分,是会变化的),并且需要有一个工厂类加以控制。
- 享元模式经典的应用场景是需要缓冲池的场景,比如 String 常量池、数据库连接池
7 代理模式
概述:
为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象
代理模式有不同的形式, 主要有三种 静态代理、动态代理 (JDK 代理、接口代理)和 Cglib 代理 (可以在内存动态的创建对象,而不需要实现接口, 他是属于动态代理的范畴) 。
特点:
为一个对象提供一个替身,以控制对这个对象的访问。
可以在目标对象实现的基础上,增强额外的功能操作,即扩展母标对象的功能。
被代理对象一般是远程对象、创建开销大的对象、需要安全控制的对象
有三种代理方式,静态代理、动态代理(JDK代理,接口代理)、Cglib代理(可在内存中动态创建对象完成代理,不需要实现接口)。
角色:
1、对象类
2、代理类(聚合对象类)
3、客户端(通过代理类使用对象类)
类图示例:
静态代理:
代码实现:
需定义接口或者父类,被代理对象(目标对象)与代理对象一起实现相同的接口或者继承相同父类。
- 定义实例接口
//1、定义接口
public interface ITercherDao {
void teach();
}
- 创建对象实例接口
//2、对象类实现接口
public class TeacherDao implements ITercherDao{
@Override
public void teach() {
System.out.println("开始授课");
}
}
- 代理类实现接口并且聚合对象(这里是静态代理的明显标志)
//3、代理类实现接口并且聚合对象
public class TercherDaoProxy implements ITercherDao{
private ITercherDao techer; //聚合目标对象
public TercherDaoProxy(ITercherDao techer) {
super();
this.techer = techer;
}
@Override
public void teach() {
System.out.println("开始代理!");
techer.teach();
System.out.println("代理结束");
}
}
- 调用
//4、客户端调用
public class Client {
public static void main(String[] args) {
TercherDaoProxy proxy = new TercherDaoProxy(new TeacherDao());
proxy.teach();
}
}
动态代理:
- 代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
- 代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象
- 动态代理也叫做:JDK 代理、接口代理
步骤代码实现:
- 创建接口以及实体类
//1、创建接口以及实体类
public interface ITercherDao {
void teach();
}
public class TeacherDao implements ITercherDao{
@Override
public void teach() {
System.out.println("开始授课");
}
}
- 创建动态工厂类
//2、创建工厂类
//动态工厂类不在继承接口,内部要维护一个Object对象
//通过反射机制来完成代理
public class ProxyFactory {
private Object target;
public ProxyFactory(Object target) {
super();
this.target = target;
}
//给目标对象生成一个代理对象,使用代理工厂类封装方法
public Object getProxyInstance() {
//三个参数,分别是:
//1、loader:指定当前对象使用的类加载器,获取加载器的方法固定
//2、目标对象使用的接口类型,使用泛型方法确认类型
//3、时件处理器,执行目标对象方法时触发时件处理器,会把当前执行目标对象的方法作为参数
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); //传入target跟参数
return returnVal;
}
});
}
}
- 调用
//3、客户端调用
public class Client {
public static void main(String[] args) {
ITercherDao terget = new TeacherDao();
//给目标对象一个代理对象
ITercherDao proxyInstance = (ITercherDao) new ProxyFactory(terget).getProxyInstance();
proxyInstance.teach();
}
}
Chlib代理:
-
静态代理和 JDK 代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实
现任何的接口,这个时候可使用目标对象子类来实现代理-这就是 Cglib 代理 -
Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib代
理归属到动态代理。 -
Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口.它广泛的被许多 AOP 的框架使用,例如 Spring AOP,实现方法拦截
-
可以不实现接口完成代理。
-
需要引包
步骤代码实现:
- 创建实体类
//1、实体类
public class Engineer {
// 可以被代理
public void eat() {
System.out.println("工程师正在吃饭");
}
// final 方法不会被生成的字类覆盖
public final void work() {
System.out.println("工程师正在工作");
}
// private 方法不会被生成的字类覆盖
private void play() {
System.out.println("this engineer is playing game");
}
}
- 创建代理类
//2、Chlib代理类
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
private Object target;
public CglibProxy(Object target) {
this.target = target;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("### before invocation");
Object result = method.invoke(target, objects);
System.out.println("### end invocation");
return result;
}
public static Object getProxy(Object target) {
Enhancer enhancer = new Enhancer();
// 设置需要代理的对象
enhancer.setSuperclass(target.getClass());
// 设置代理人
enhancer.setCallback(new CglibProxy(target));
return enhancer.create();
}
}
- 调用
//3、测试
public class CglibMainTest {
public static void main(String[] args) {
// 生成 Cglib 代理类
Engineer engineerProxy = (Engineer) CglibProxy.getProxy(new Engineer());
// 调用相关方法
engineerProxy.eat();
}
}
代理模式注意事项和小结
- 和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
- 和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。