设计模式的代码:https://github.com/Aerozb/design_patterns
目录
需求
如上图,张三及其跟他有关系的人需要购买A,B公司的产品 ,比如张三向A公司买女性娃娃,张三老婆向B公司买男性娃娃;
但是因为在海外,所以只能向代购公司购买,而且代购公司还能提供购买前后的服务,比如买前咨询,买后问怎么用。
静态代理实现
1.卖男性娃娃的工厂
public interface IManFactory {
// 卖男性娃娃
public void saleMan(float length);
}
public class ManFactory implements IManFactory {
@Override
public void saleMan(float length) {
System.out.println("男性" + length);
}
}
2.卖女性娃娃的工厂
public interface IWomanFactory {
// 卖女性娃娃
public void saleWoman(int faceSource);
}
public class WomanFactory implements IWomanFactory {
@Override
public void saleWoman(int faceSource) {
System.out.println("女性" + faceSource);
}
}
3.中间海外代理商
/**
* 代理商
*/
public class LisonCompany implements IManFactory, IWomanFactory {
private IManFactory manFactory;
private IWomanFactory womanFactory;
public void setManFactory(IManFactory manFactory) {
this.manFactory = manFactory;
}
public void setWomanFactory(IWomanFactory womanFactory) {
this.womanFactory = womanFactory;
}
@Override
public void saleMan(float length) {
before();
manFactory.saleMan(length);
after();
}
@Override
public void saleWoman(int faceSource) {
before();
womanFactory.saleWoman(faceSource);
after();
}
public void before() {
System.out.println("售前服务");
}
public void after() {
System.out.println("售后服务");
}
}
4.测试
public class Test {
public static void main(String[] args) {
// 男性工厂
IManFactory manFactory = new ManFactory();
// 女性工厂
IWomanFactory womanFactory = new WomanFactory();
// 中间商
LisonCompany company = new LisonCompany();
// 中间代理商设置男性工厂
company.setManFactory(manFactory);
// 女客户开始购买男性娃娃,由中间商调用代理过后的方法
company.saleMan(18);
System.out.println("====================");
// 中间代理商设置男性工厂
company.setWomanFactory(womanFactory);
// 男客户开始购买男性娃娃,由中间商调用代理过后的方法
company.saleWoman(10);
}
}
5.结果
售前服务
男性18.0
售后服务
====================
售前服务
女性10
售后服务
可以看出如果是静态代理实现,中间商需要实现并聚合XX工厂,如果客户不仅要购买娃娃,还要购买洗面奶等物品,这时候又要实现并继承,然后实现其方法进行代理,如果只增加一个工厂还好,但是客户买的越来越多 不同产品,中间商就需要代理更多工厂,这样中间商的代码就越来越庞大,并不符合单一,开闭的设计原则,所有解决办法就是用动态代理。
动态代理实现
1.卖男性娃娃的工厂
public interface IManFactory {
// 卖男性娃娃
public void saleMan(float length);
}
public class ManFactory implements IManFactory {
@Override
public void saleMan(float length) {
System.out.println("男性" + length);
}
}
2.卖女性娃娃的工厂
public interface IWomanFactory {
// 卖女性娃娃
public void saleWoman(int faceSource);
}
public class WomanFactory implements IWomanFactory {
@Override
public void saleWoman(int faceSource) {
System.out.println("女性" + faceSource);
}
}
3.中间海外代理商
/**
* 代理商
*/
public class LisonCompany implements InvocationHandler {
// 聚合工厂,客户需要哪个工厂生产的产品就设置哪个工厂
private Object factory;
public void setFactory(Object factory) {
this.factory = factory;
}
// 返回一个代理过后的工厂
public Object getProxyInstance() {
return Proxy.newProxyInstance(factory.getClass().getClassLoader(), factory.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object o, Method method, Object[] params) throws Throwable {
before();
// 反射调用方法
Object result = method.invoke(factory, params);
after();
return result;
}
public void before() {
System.out.println("售前服务");
}
public void after() {
System.out.println("售后服务");
}
}
4.测试
public class Test {
public static void main(String[] args) {
// 男性工厂
IManFactory manFactory = new ManFactory();
// 女性工厂
IWomanFactory womanFactory = new WomanFactory();
// 中间商
LisonCompany company = new LisonCompany();
// 中间代理商设置男性工厂
company.setFactory(manFactory);
// 生成代理对象,代理商可在买卖前后做一些操作
IManFactory manFactoryProxy = (IManFactory) company.getProxyInstance();
// 女客户开始购买男性娃娃,从代码来看,似乎是客户直接向工厂够买,其实是由中间商调用代理过后的方法。
manFactoryProxy.saleMan(80);
System.out.println("====================");
// 中间代理商设置女性工厂
company.setFactory(womanFactory);
// 生成代理对象,代理商可在买卖前后做一些操作
IWomanFactory womanFactoryProxy = (IWomanFactory) company.getProxyInstance();
// 调用代理过后的方法
womanFactoryProxy.saleWoman(6);
}
}
5.结果
售前服务
男性80.0
售后服务
====================
售前服务
女性6
售后服务
可以看到,动态代理模式符合开闭原则,无需再原来的类(代理商)上修改,只需增加新的接口及其实现类就行,比静态代理简洁方便。
如果顾客需要新的海外产品,比如,洗面奶,只需要在新增一个洗面奶工厂类就行,中间商无需在修改,然后用户购买时,只需new出来新的工厂,中间商在设置进去就行;符合开闭原则的对外开发拓展,对内关闭修改原则。
查看代理类的结构
我们的代理类是生成在jvm内存中,所以需要把内存中的字节码文件转成class保存到硬盘,我们才能查看。
透过源码确实可以看出是在这生成的,那我们也用类似的方法,生成出我们要的代理类。
/**
* 将jvm内存中的代理类输出到桌面
*/
public class ProxyUtil {
public static void generator(Class clz, String name) {
byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]{clz});
String path = FileSystemView.getFileSystemView().getHomeDirectory().getAbsolutePath() +File.separator+ name + ".class";
System.out.println(path);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(new File(path));
fos.write(bytes);
fos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
我们就在上面的动态代理测试类中,末尾加上以下这两句,然后在运行,就可以在桌面看到两个代理文件。
//将代理类生成class文件
ProxyUtil.generator(manFactory.getClass(),manFactoryProxy.getClass().getSimpleName());
ProxyUtil.generator(womanFactory.getClass(),womanFactoryProxy.getClass().getSimpleName());
将生成的class文件直接拖到 IDEA,就可以反编译查看了,忽略不重要的方法属性,可以看到确实有个同名方法,而他调用的就是我们实现InvocationHandler的类LisonCompany的invoke方法,其中h就是我们的LisonCompany,h就是在父类Proxy 中设置的。
public final class $Proxy0 extends Proxy implements ManFactory {
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final void saleMan(float var1) throws {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
}