本文是学习设计模式时做的笔记,原视频链接:
https://www.bilibili.com/video/BV1G4411c7N4
目录
适配器模式(Adapter)
适配器模式:将某个接口转换成客户端期望的另一个接口,使得原本由于接口不兼容而不能一起工作的类协同工作。
使用场景
美国电器的额定电压是110V,中国电器的额定电压是220V,想要在美国使用made in China的电器,需要一个适配器将110V的交流电转换为220V。
实现方式
适配器模式主要分为三类:
- 类适配器模式
- 对象适配器模式
- 接口适配器模式
类适配器模式
美国交流电的电压是110V,中国电器的额定电压是220V,想要在美国使用made in China的电器,需要一个适配器将110V的交流电转换为220V。
1.创建适配器
美国交流电电压为110V:
public class Voltage110V {
int src = 110;
public int output110V() {
return src;
}
}
中国电器额定电压是220V,所以在使用适配器后的目标电压是220V:
public interface Voltage220V {
public int output220V();
}
适配器起到调节电压的作用:
public class VoltageAdapter extends Voltage110V implements Voltage220V {
int srcV;
int dstV;
@Override
public int output220V() {
srcV = output110V();
dstV = srcV * 2;
return dstV;
}
}
2.使用适配器
创建一个榨汁机类Juicer:
public class Juicer {
public void play(Voltage220V voltage220V) {
if (voltage220V.output220V() == 220) {
System.out.println("榨汁!");
} else {
System.out.println("不对劲!");
}
}
}
最后创建一个用户Client:
public class Client {
public static void main(String[] args) {
Juicer juicer = new Juicer();
juicer.play(new VoltageAdapter());
}
}
输出结果:
榨汁!
分析:
adapter类(VoltageAdapter),通过继承src类(Voltage110V),实现dst接口(Voltage220V),完成了从src到dst的适配。
这么做有两个缺点:
- adapter类继承了src类,由于Java是单继承机制,adapter类不能再继承其它类
- dst必须是接口,有一定局限性
对象适配器模式在类适配器模式的基础上,遵循合成复用原则,对类适配器模式进行了改进。
对象适配器模式
基本实现思路和类适配器模式相同,只是adapter类不再继承src类,而是持有src类的实例。
根据合成复用原则,在系统中尽量使用组合/聚合关系来代替继承关系。解决了适配器必须继承src类的局限性问题,也不再要求dst必须是接口。
美国交流电的电压是110V,中国电器的额定电压是220V,想要在美国使用made in China的电器,需要一个适配器将110V的交流电转换为220V。
1.创建适配器
美国交流电电压为110V:
public class Voltage110V {
int src = 110;
public int output110V() {
return src;
}
}
中国电器额定电压是220V,所以在使用适配器后的目标电压是220V:
public interface Voltage220V {
public int output220V();
}
适配器起到调节电压的作用:
public class VoltageAdapter implements Voltage220V {
int srcV;
int dstV;
private Voltage110V voltage110V;
public VoltageAdapter(Voltage110V voltage110V) {
this.voltage110V = voltage110V;
}
@Override
public int output220V() {
dst = 0;
if (null != voltage110V) {
src = voltage110V.output110V();
dst = src * 2;
}
return dst;
}
}
2.使用适配器
创建一个榨汁机类Juicer:
public class Juicer {
public void play(Voltage220V voltage220V) {
if (voltage220V.output220V() == 220) {
System.out.println("榨汁!");
} else {
System.out.println("不对劲!");
}
}
}
最后创建一个用户Client:
public class Client {
public static void main(String[] args) {
Juicer juicer = new Juicer();
juicer.play(new VoltageAdapter(new Voltage110V()));
}
}
输出结果:
榨汁!
接口适配器模式
使用接口适配器模式的场景是:想要使用一个接口中的一部分(而非全部的)抽象方法。
某个接口提供了多个抽象方法,当不需要全部实现接口提供的方法时,可以先设计一个抽象类实现该接口,并为该接口中每个方法提供一个默认实现(空方法),该抽象类的子类可有选择地重写需要用到的方法来实现需求。
1.创建一个接口
public interface Interface4 {
public void method1();
public void method2();
public void method3();
public void method4();
}
2.创建接口适配器(抽象类)
实现接口,并为接口中每个方法提供默认实现:
public abstract class AbsAdapter implements Interface4 {
@Override
public void method1() {
}
@Override
public void method2() {
}
@Override
public void method3() {
}
@Override
public void method4() {
}
}
3.(Client)使用接口适配器
使用匿名内部类,重写接口适配器中需要用到的方法:
public class Client {
public static void main(String[] args) {
AbsAdapter absAdapter = new AbsAdapter() {
@Override
public void method1() {
System.out.println("使用了method1方法");
}
};
absAdapter.method1();
}
}
输出结果:
使用了method1方法
桥接模式(Bridge)
桥接模式:把抽象与实现解耦,使得二者可以独立变化。解耦是通过桥接结构实现的。
使用场景
从多个角度分析问题,问题在每个角度都可能发生变化。
举一个例子,一个男性(Man)按不同的年龄阶段,我们可以将他归为男孩(Boy)、成年男子(Adult)、老爷爷(OldMan),在不同的时间,他可能有不同的情绪(Mood),比如开心(Happy)、生气(Angry)、难过(Sad)。
我们在路上遇到一个陌生男子,我们可以从他的年龄和情绪两个角度来形容对他的印象:
“我刚才在路上走着,遇到了一个看起来很难过的男孩。”
“我刚才在路上走着,遇到了一个看起来很开心的老爷爷。”
桥接模式强调每个角度的变化:下一个遇到的陌生男子可能是开心的、生气的、难过的,这和他的年龄段关系不大(大概吧…);下一个遇到的陌生男子可能是男孩、成年男子、老爷爷,这和他的情绪关系也不大。
实现方式
桥接模式原理类图:
Client:用户,桥接类的调用者。
Abstraction:桥接类,是一个抽象类。
RefinedAbstraction:桥接类的子类。
Implementor:行为实现类的接口。
ConcreteImplementorA/B:具体的行为实现类。
从UML图可以看出:Abstraction和Implementor之间是聚合关系,也就是调用和被调用的关系。
举一个例子,一个男性(Man)按不同的年龄阶段,我们可以将他归为男孩(Boy)、成年男子(Adult)、老爷爷(OldMan),在不同的时间,他可能有不同的情绪(Mood),比如开心(Happy)、生气(Angry)、难过(Sad)。
我们在路上遇到一个陌生男子,我们可以从他的年龄和情绪两个角度来形容对他的印象:
“我刚才在路上走着,遇到了一个看起来很难过的男孩。”
“我刚才在路上走着,遇到了一个看起来很开心的老爷爷。”
1.一个男性(Man)和情绪(Mood)之间的关系是聚合,所以我们先写部分(Mood)再写整体(Man)
public interface Mood {
void look();
}
public class Happy implements Mood{
@Override
public void look() {
System.out.print("看起来很开心的");
}
}
public class Angry implements Mood {
@Override
public void look() {
System.out.print("看起来很生气的");
}
}
public class Sad implements Mood {
@Override
public void look() {
System.out.print("看起来很难过的");
}
}
这部分很容易理解。
2.写抽象类Man和它的子类Boy、Adult、OldMan
public abstract class Man {
//Mood接口和Man类之间为聚合关系
private Mood mood;
//通过构造函数传入Mood对象
public Man(Mood mood) {
this.mood = mood;
}
//根据传入的不同的Mood对象决定调用哪个Mood接口实现类中的look()
protected void look() {
this.mood.look();
}
}
public class Boy extends Man {
public Boy(Mood mood) {
super(mood);
}
public void look() {
super.look();
System.out.println("小男孩");//我们说一个Boy类对象是“小男孩”是符合逻辑的
}
}
public class Adult extends Man {
public Adult(Mood mood) {
super(mood);
}
public void look() {
super.look();
System.out.println("成年男子");
}
}
public class OldMan extends Man {
public OldMan(Mood mood) {
super(mood);
}
public void look() {
super.look();
System.out.println("老爷爷");
}
}
3.最后写我们Client
public class Client {
public static void main(String[] args) {
System.out.print("我刚才在路上走着,遇到了一个");
Man man1 = new Boy(new Happy());
man1.look();
System.out.print("然后遇到一个");
Man man2 = new Adult(new Angry());
man2.look();
System.out.print("后来又遇到一个");
Man man3 = new OldMan(new Sad());
man3.look();
}
}
输出结果:
我刚才在路上走着,遇到了一个看起来很开心的小男孩
然后遇到一个看起来很生气的成年男子
后来又遇到一个看起来很难过的老爷爷
装饰者模式(Decorator)
装饰者模式:为一个现有的对象添加新的功能,同时又不改变其结构。
装饰者模式是继承的一个替代模式,可以动态扩展一个实现类的功能。
装饰类和被装饰类可以独立发展,不会相互耦合。
使用场景
代替继承。
实现方式
一个女孩(Girl)要去约会(Date)。
Beauty(美人)是Girl(女孩)的扩展类。
1.“一个女孩要去约会”——女孩类Girl要实现约会接口Date
public interface Date {
void date();
}
public class Girl implements Date {
@Override
public void date() {
System.out.println("去约会");
}
}
2.创建实现了Date接口的抽象装饰类DateDecorator
public abstract class DateDecorator implements Date {
protected Date decorator;
public DateDecorator(Date date) {
this.decorator = date;
}
public void date() {
decorator.date();
}
}
3.创建扩展了DateDecorator类的实体装饰类Beauty
public class Beauty extends DateDecorator {
public Beauty(Date date) {
super(date);
}
@Override
public void date() {
clean(decorator);
makeUp(decorator);
decorator.date();
}
private void clean(Date date) {
System.out.print("洗漱,");
}
private void makeUp(Date date) {
System.out.print("化妆,");
}
}
4.测试
public class Test {
public static void main(String[] args) {
Date girl = new Girl();
DateDecorator beauty = new Beauty(new Girl());
System.out.println("普通女孩:");
girl.date();
System.out.println("美人:");
beauty.date();
}
}
输出结果:
普通女孩:
去约会
美人:
洗漱,化妆,去约会
我的理解,面向对象思想的重点在于对自然界中关系的描述,“一个女孩去约会”,“女孩”是类,“一个女孩”是女孩类的实例;“约会”是具体的事件,用接口来描述,“去约会”是“约会”这件事中的一个行为,用接口中的方法来描述;女孩类实现约会接口,表示所有的女孩都可以完成“去约会”这件事。
如果说装饰者模式是继承关系的替代模式,我们先要考虑:继承描述了自然界中的什么关系?
我们在刚开始学习继承的时候经常用的一个例子是:平行四边形是特殊的四边形——平行四边形类是四边形类的子类:平行四边形类继承四边形类,平行四边形满足四边形的所有性质。可见,继承描述了自然界中的泛化关系。
所有的鸟类都是动物——鸟类是动物类的子类。
在女孩约会的例子中有两个与继承有关的问题:
- 装饰者模式替代了哪一段继承关系?
- 从面向对象的角度来看,装饰者类描述了什么?
1.装饰者模式替代了哪一段继承关系?
替代了“美人类继承女孩类”的关系。从自然界的角度看,我们可以认为“所有的美人都是女孩”。
代码:Beauty beauty = new Beauty(new Girl())。美人类构造方法中传入了一个女孩类对象,表示beauty(美人类的一个实例)是女孩。
看到这里,我们就能get到装饰者模式的优点了:
如果我们不认为“所有的美人都是女孩”——也会存在一些美人是男孩,我们可以创建男孩类Boy并让它实现约会接口,通过向美人类构造方法中传入男孩类对象,例如Beauty Yanzu = new Beauty(new Boy()),可以表示Yanzu(美人类的一个实例)是男孩。
总结为,装饰者模式使美人类和女孩类的耦合度很低。
2.从面向对象的角度来看,装饰者类描述了什么?
装饰类实现了约会接口,说明装饰类的对象是能够完成约会事件的某人。
美人类实际上继承了抽象的装饰类,说明“所有的美人都是装饰类的对象”。
装饰类是对所有其它实现约会接口的类的总结,不管是男孩还是女孩,就算后边再来个外星人类实现约会接口,通通都是约会人。
我们用非常不正确但很直观的表达方式来理解:
- 美人类可以继承女孩类,女孩类继承装饰类。
- 美人类可以继承男孩类,男孩类继承装饰类。
- 美人类可以继承外星人类,外星人类继承装饰类。
所以我们就懂了,装饰类对象就是约会人。
以上分析,如有误欢迎指出。
组合模式(Composite)
组合模式:创建对象组的树形结构,依据树形结构来表示对象间“整体-部分”的层次关系。
使用场景
一所大学(University)可能设有多个学院(College),每个学院可能设有多个系(Department)。
郑州大学设有机械工程学院和外语学院。
机械工程学院设有机械工程和工业设计两个系。
外语学院设有英语、日语、俄语、德语四个系。
使用组合模式可以很方便地对郑州大学的院系关系进行管理。
实现方式
我们很容易想到创建University类,College类继承University类,然后Department类继承College类。
根据里氏替换原则,我们可以创建一个公共的基类Organization,使University、College、Department类都继承这个基类,采用聚合关系代替继承。
1.创建公共基类Organization
public abstract class Organization {
private String name;//名称
private String description;//描述
public Organization(String name, String description) {
this.name = name;
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
//考虑到系不需要add方法和remove方法,所以这里默认实现而非使用抽象方法
protected void add(Organization organization) {
}
protected void remove(Organization organization) {
}
protected abstract void print();
}
2.创建University、College、Department
import java.util.ArrayList;
import java.util.List;
public class University extends Organization {
List<Organization> organizations = new ArrayList<Organization>();
public University(String name, String description) {
super(name, description);
}
@Override
public String getName() {
return super.getName();
}
@Override
public String getDescription() {
return super.getDescription();
}
@Override
protected void add(Organization organization) {
organizations.add(organization);
}
@Override
protected void remove(Organization organization) {
organizations.remove(organization);
}
@Override
protected void print() {
System.out.println("--------" + getName() + "--------");
for (Organization organization : organizations) {
organization.print();
}
}
}
import java.util.ArrayList;
import java.util.List;
public class College extends Organization {
List<Organization> organizations = new ArrayList<Organization>();
public College(String name, String description) {
super(name, description);
}
@Override
public String getName() {
return super.getName();
}
@Override
public String getDescription() {
return super.getDescription();
}
@Override
protected void add(Organization organization) {
organizations.add(organization);
}
@Override
protected void remove(Organization organization) {
organizations.remove(organization);
}
@Override
protected void print() {
System.out.println("----" + getName() + "----");
for (Organization organization : organizations) {
organization.print();
}
}
}
public class Department extends Organization {
public Department(String name, String description) {
super(name, description);
}
@Override
public String getName() {
return super.getName();
}
@Override
public String getDescription() {
return super.getDescription();
}
@Override
protected void print() {
System.out.print(getName());
System.out.println(": " + getDescription());
}
}
3.测试
public class Client {
public static void main(String[] args) {
Organization zzu = new University("郑州大学", "某不知名高校");
Organization mechanicalCollege = new College("机械工程学院", "盛产帅哥");
Organization languagesCollege = new College("外语学院", "盛产美女");
mechanicalCollege.add(new Department("机械工程", "百分之八十的人数,百分之二十的女生"));
mechanicalCollege.add(new Department("工业设计", "百分之二十的人数,百分之八十的女生"));
languagesCollege.add(new Department("英语", "Hello"));
languagesCollege.add(new Department("日语", "こんにちは"));
languagesCollege.add(new Department("俄语", "Здравствыйте"));
languagesCollege.add(new Department("德语", "Hallo!"));
zzu.print();
mechanicalCollege.print();
languagesCollege.print();
}
}
输出结果:
--------郑州大学--------
----机械工程学院----
机械工程: 百分之八十的人数,百分之二十的女生
工业设计: 百分之二十的人数,百分之八十的女生
----外语学院----
英语: Hello
日语: こんにちは
俄语: Здравствыйте
德语: Hallo!
外观模式(Facade)
外观模式:为子系统中的一组接口提供一个一致的界面,再定义了一个高层接口来访问这个界面。
使用场景
简化调用。
实现方式
小贝(Bei)要去上学,她的嫂子湘玉(Xiangyu)请求保镖老白(Bai)、书童秀才(Lv)、丫鬟小郭(Guo)陪同。
1.老白、秀才、小郭各司其职
(因为世界上只有一个老白、一个秀才、一个小郭,所以使用单例模式)
public class Bai {
private final static Bai bai = new Bai();
private Bai() {
}
public static Bai getBai() {
return bai;
}
public void work() {
System.out.println("白:像不像,像不像,行者悟空?");
}
}
public class Lv {
private final static Lv lv = new Lv();
private Lv() {
}
public static Lv getLv() {
return lv;
}
public void work() {
System.out.println("吕:我看起来不会太老吧?");
}
}
public class Guo {
private final static Guo guo = new Guo();
private Guo() {
}
public static Guo getGuo() {
return guo;
}
public void work() {
System.out.println("郭:我怎么觉得有点别扭啊?");
}
}
2.上学去
如果没有湘玉,小贝要做的是:
public class Bei {
public static void main(String[] args) {
Guo guo = Bai.getBai();
guo.work();
Lv lv = Lv.getLv();
lv.work();
Bai bai = Guo.getGuo();
bai.work();
System.out.println("贝:嫂子我去上学了!");
}
}
输出结果:
郭:我怎么觉得有点别扭啊?
吕:我看起来不会太老吧?
白:像不像,像不像,行者悟空?
贝:嫂子我去上学了!
如果湘玉帮小贝打点:
public class Xiangyu {
public Bai bai;
public Lv lv;
public Guo guo;
public Xiangyu(Bai bai, Lv lv, Guo guo) {
this.bai = bai;
this.lv = lv;
this.guo = guo;
}
public void request() {
System.out.println("湘玉:来嘛来嘛!");
guo.work();
lv.work();
bai.work();
System.out.println("湘玉:美滴很美滴很!");
}
}
小贝只需要:
public class Bei {
public static void main(String[] args) {
Xiangyu xiangyu = new Xiangyu(Bai.getBai(), Lv.getLv(), Guo.getGuo());
xiangyu.request();
System.out.println("贝:嫂子我去上学了!");
}
}
输出结果:
湘玉:来嘛来嘛!
郭:我怎么觉得有点别扭啊?
吕:我看起来不会太老吧?
白:像不像,像不像,行者悟空?
湘玉:美滴很美滴很!
贝:嫂子我去上学了!
享元模式(Flyweight)
享元模式:运用共享技术有效地支持大量细粒度的对象。
使用场景
享元模式经典的应用场景是池技术。String常量池、数据库连接池、缓冲池等等。
实现方式
享元模式将对象的信息分为两个部分:内部状态和外部状态。
内部状态指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变。
外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。
在一场围棋对局中,可能有两三百个棋子对象产生,如果用享元模式来处理棋子,棋子对象就可以减少到只有两个实例。
棋子的颜色是内部状态,棋子的坐标是外部状态。
Flyweight:抽象的享元角色,是产品的抽象类。同时定义出对象的外部状态和内部状态的接口或实现。
ConcreteFlyweight:具体的享元角色,是具体的产品类。实现抽象享元角色定义的相关业务。
UnsharedConcreteFlyweight:不可共享的角色,一般不会出现在享元工厂中。
FlyweightFactory:享元工厂类,用于构建池的容器(集合),并提供从池中获取对象的方法。
在一场围棋对局中:
棋笥(Box):FlyweightFactory
围棋棋子(GoPieces):Flyweight
每颗棋子(Piece):ConcreteFlyweight
棋子坐标(Coordinate):UnsharedConcreteFlyweight
吃瓜群众(Client):Client
1.围棋棋子(GoPieces)
每颗棋子可以在棋盘上的某个坐标(coordinate)被放置(place):
public abstract class GoPieces {
public abstract void place(Coordinate coordinate);//需先创建Coordinate类
}
可以看到,GoPieces类中聚合了Coordinate类,我们认为棋子的坐标是棋子的外部状态。
2.棋子坐标(Coordinate)
棋子的坐标包括横坐标abscissa和纵坐标ordinate,补充构造方法和getter and setter:
public class Coordinate {
private int abscissa;//横坐标
private int ordinate;//纵坐标
public Coordinate(int abscissa, int ordinate) {
this.abscissa = abscissa;
this.ordinate = ordinate;
}
public int getAbscissa() {
return abscissa;
}
public void setAbscissa(int abscissa) {
this.abscissa = abscissa;
}
public int getOrdinate() {
return ordinate;
}
public void setOrdinate(int ordinate) {
this.ordinate = ordinate;
}
}
3.每颗棋子(Piece)
棋子的内部状态包括棋子的颜色color,Piece继承了GoPieces类,每颗棋子都可以在棋盘上的某个坐标(coordinate)被放置(place):
public class Piece extends GoPieces {
private String color = "";
private String coordinate = "";
public Piece(String color) {
this.color = color;
}
@Override
public void place(Coordinate coordinate) {
this.coordinate = "(" + coordinate.getAbscissa() + ", " + coordinate.getOrdinate() + "),";
System.out.println(this.coordinate + color + "棋落子");
}
}
4.棋笥(Box)
享元模式中,围棋棋子的内部状态被共享:
import java.util.HashMap;
public class Box {
//集合,充当池
private HashMap<String, Piece> pool = new HashMap<>();
//根据颜色,返回一个棋子,如果没有就创建,并放到池中
public GoPieces getPieceColor(String color) {
if (!pool.containsKey(color)) {
pool.put(color, new Piece(color));
}
return (GoPieces) pool.get(color);
}
}
5.吃瓜群众(Client)
围观一场围棋对局:
public class Client {
public static void main(String[] args) {
Box box = new Box();
GoPieces blackPiece = box.getPieceColor("黑");
GoPieces whitePiece = box.getPieceColor("白");
System.out.println("执黑先行");
blackPiece.place(new Coordinate(17, 4));
whitePiece.place(new Coordinate(4, 3));
blackPiece.place(new Coordinate(16, 17));
whitePiece.place(new Coordinate(15, 3));
blackPiece.place(new Coordinate(3, 16));
whitePiece.place(new Coordinate(17, 15));
blackPiece.place(new Coordinate(16, 5));
System.out.println("……");
}
}
输出结果:
执黑先行
(17, 4),黑棋落子
(4, 3),白棋落子
(16, 17),黑棋落子
(15, 3),白棋落子
(3, 16),黑棋落子
(17, 15),白棋落子
(16, 5),黑棋落子
……
代理模式(Proxy)
代理模式:为一个对象提供一个替身,以控制对这个对象的访问。
使用场景
直接访问某个对象,可能会产生一些问题时,我们可以控制对这个对象的访问。
比如要访问的对象创建开销很大、或者某些操作需要安全控制、或者需要进程外的访问等等。
实现方式
代理模式有不同的形式,主要包括静态代理和动态代理。
静态代理
使用静态代理时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。
数学老师(MathTeacher)生病了,体育老师(PETeacher)来给他代课(Teach)。
1.数学老师授课
public interface Teach {
void teach();
}
public class MathTeacher implements Teach {
@Override
public void teach() {
System.out.println("这节课讲的是微积分");
}
}
2.体育老师代课
public class PETeacher implements Teach {
private Teach teacher;
public PETeacher(Teach teacher) {
this.teacher = teacher;
}
@Override
public void teach() {
System.out.println("体育老师:这节课由我来带大家学习");
teacher.teach();
System.out.println("同学们学的很好");
}
}
3.学生听课
public class Student {
public static void main(String[] args) {
MathTeacher mathTeacher = new MathTeacher();
PETeacher peTeacher = new PETeacher(mathTeacher);
peTeacher.teach();
}
}
输出结果:
体育老师:这节课由我来带大家学习
这节课讲的是微积分
同学们学的很好
静态代理优缺点:
- 优点:在不修改目标对象的前提下,通过代理对象对目标功能进行了扩展
- 缺点:代理对象需要与目标对象实现一样的接口,所以会产生很多代理类,一旦接口增加方法,目标对象和所有代理对象都需要进行维护
动态代理
动态代理以调用JDK中API的方式,动态地在内存中构建代理对象。
代理类所在包:java.lang.reflect.Proxy
调用Proxy类中的newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法,即可获得代理对象。
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法利用了Java反射机制,它的三个参数分别为:
ClassLoader loader:指定当前目标对象使用的类加载器
Class<?>[] interfaces:目标对象实现的接口类型,使用泛型可以确认其类型
InvocationHandler h:事件处理器
数学老师(MathTeacher)生病了,组织(ProxyFactory)派人来给他代课(Teach)。
1.数学老师授课
public interface Teach {
void teach();
}
public class MathTeacher implements Teach {
@Override
public void teach() {
System.out.println("这节课讲的是微积分");
}
}
2.生成代课老师(看起来很复杂,但写法比较固定)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxyInstance() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object retrunValue = method.invoke(target, args);
return retrunValue;
}
});
}
}
3.吃瓜学生
public class Student {
public static void main(String[] args) {
Teach target = new MathTeacher();
Teach proxyTeacher = (Teach) new ProxyFactory(target).getProxyInstance();
proxyTeacher.teach();
}
}
输出结果:
这节课讲的是微积分
加油!