适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
适配模式(Adapter)
简介:将一类的接口转换成客户希望的另外一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作那些类可以一起工作。
分类
类适配器模式:
Adapter 类,通过继承 source 类,实现 Destination 类接口,完成 source->Destination 的适配。
-对象适配器模式:
将 Adapter 类作修改,不是继承 source 类,而是持有 source 类的实例,以解决兼容性的问题。 即:持有 source 类,实现 Destination 类接口,完成source->Destination 的适配
接口适配器模式:
当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求适用于一个接口不想使用其所有的方法的情况
适配器模式分类三种体现形式思想都大同小异,这里就只介绍最常用的 对象适配器模式;
/**
* 适配器:里面封装了source源到destination目标的过程
*/
class VoltageAdapter implements Voltage5V{
private Voltage220V voltage220V;
public VoltageAdapter(Voltage220V voltage220V) {
this.voltage220V = voltage220V;
}
@Override
public double getVoltage() {
return voltage220V.getVoltage() / 40;
}
}
把逻辑看似合理的逻辑合理化
桥接模式(Bridge)
简介:将抽象部分与它的实现部分相分离,使他们可以独立的变化。
注意事项及细节
实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性
对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成
桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本
public abstract class Abstraction {
protected Implementor implementor;
public abstract void call();
}
class Folded extends Abstraction {
private String name = "折叠式";
Folded(Implementor implementor) {
this.implementor = implementor;
}
@Override
public void call() {
System.out.println(this.implementor.getName() + this.name + "正在通话中");
}
}
class Upright extends Abstraction {
private String name = "直立式";
Upright(Implementor implementor) {
this.implementor = implementor;
}
@Override
public void call() {
System.out.println(this.implementor.getName() + this.name + "正在通话中");
}
}
class Slide extends Abstraction {
private String name = "滑盖式";
Slide(Implementor implementor) {
this.implementor = implementor;
}
@Override
public void call() {
System.out.println(this.implementor.getName() + this.name + "正在通话中");
}
}
package com.javaxl.design.bridge;
/**
* 手机品牌
*/
public interface Implementor {
String getName();
}
class HW implements Implementor{
private String name = "华为";
@Override
public String getName() {
return name;
}
}
class Mi implements Implementor{
private String name = "小米";
@Override
public String getName() {
return name;
}
}
class Vivo implements Implementor{
private String name = "Vivo";
@Override
public String getName() {
return name;
}
}
public class Client {
public static void main(String[] args) {
Folded folded = new Folded(new HW());
folded.call();
Upright upright = new Upright(new Mi());
upright.call();
Slide slide = new Slide(new Vivo());
slide.call();
}
}
注意:
1、桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程
2、桥接模式要求正确识别出系统中两个独立变化的维度(抽象、和实现),因此其使用范围有一定的局限性,即需要有这样的应用场景。
装饰者模式(Decorator)
定义:
-
装饰者模式一般用于对原有功能进行增强/装饰
-
动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性
-
/** * 饮料包括单体咖啡+调料 */ public abstract class Drink { protected double price; protected int n; public abstract double getPrice(); } /** * 单体咖啡 */ abstract class Coffee extends Drink { } /** * 单体果汁 */ abstract class Juice extends Drink { } class ChinaCoffee extends Coffee { ChinaCoffee(double price, int n) { this.price = price; this.n = n; } @Override public double getPrice() { return this.price * this.n; } } package com.javaxl.design.decorator.after; /** * */ public class DecoratorDrink extends Drink { private Drink drink; public DecoratorDrink(Drink drink, double price, int n) { this.drink = drink; this.price = price; this.n = n; } @Override public double getPrice() { return this.price * this.n + drink.getPrice(); } } class ADecoratorDrink extends DecoratorDrink { public ADecoratorDrink(Drink drink, double price, int n) { super(drink, price, n); } } class BDecoratorDrink extends DecoratorDrink { public BDecoratorDrink(Drink drink, double price, int n) { super(drink, price, n); } } package com.javaxl.design.decorator.after; /** * */ public class Client { public static void main(String[] args) { ChinaCoffee chinaCoffee = new ChinaCoffee(6,1); // 假定A类调料2元一份,B类调料3元一份 Drink order = new ADecoratorDrink(chinaCoffee, 2, 2); System.out.println("中式咖啡1份+A调料2份,最终价格为:"+order.getPrice()); // 思考1:如果我要下单中式咖啡1份+A调料3份+B调料2份,计算出最终的价格,那代码该怎么改动呢? order = new ADecoratorDrink(order,2,1); System.out.println("式咖啡1份+A调料3份,最终价格为:"+order.getPrice()); order = new BDecoratorDrink(order,3,2); System.out.println("式咖啡1份+A调料3份+B调料2份,最终价格为:"+order.getPrice()); // 思考2:在原有的咖啡订单下,追加B调料2份,计算出最终的价格,那代码该怎么改动呢? order = new BDecoratorDrink(order,3,2); System.out.println("式咖啡1份+A调料3份+B调料4份,最终价格为:"+order.getPrice()); } }
外观模式(Facade)
-
角色
-
Client 类 外观模式的调用者
-
ComponentA 子系统A 电脑
-
ComponentB 子系统B 投影仪
-
Facade HomeTheaterFacade 家庭影院
-
注意事项及细节
-
屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性
-
对客户端与子系统的耦合关系 - 解耦,让子系统内部的模块更易维护和扩展
-
当系统需要进行分层设计时,可以考虑使用 Facade 模式
-
/**
* 电脑(故意写两个用不上的功能,依次体现外观模式的优点)
*/
public class ComponentA {
public void m1(){
System.out.println("电脑功能一...");
}
public void m2(){
System.out.println("电脑功能二...");
}
public void on(){
System.out.println("电脑打开...");
}
public void off(){
System.out.println("电脑关闭...");
}
}
//投影仪
class ComponentB {
public void on(){
System.out.println("投影仪打开...");
}
public void off(){
System.out.println("投影仪关闭...");
}
}
//音箱
class ComponentC {
public void on(){
System.out.println("音箱打开...");
}
public void off(){
System.out.println("音箱关闭...");
}
}
//、灯光
class ComponentD {
public void on(){
System.out.println("灯光调亮...");
}
public void off(){
System.out.println("灯光调暗...");
}
}
//零食
class ComponenE {
public void on(){
System.out.println("零食拿出来...");
}
public void off(){
System.out.println("零食收起来...");
}
}
public class Client {
public static void main(String[] args) {
new ComponentA().on();
new ComponentB().on();
new ComponentC().on();
new ComponentD().off();
new ComponenE().on();
System.out.println("电影开始了...");
System.out.println();
new ComponenE().off();
new ComponentD().on();
new ComponentC().off();
new ComponentB().off();
new ComponentA().off();
System.out.println("电影结束了...");
}
}v
代理模式(Proxy)
分类
-
静态代理
-
角色
-
接口 ITeacherDao
-
目标对象 TeacherDAO
-
代理类 TeacherDAOProxy
-
细节 代理对象与目标对象要实现相同的接口 调用的时候通过调用代理对象的方法来调用目标对象
-
public class TeacherDAO implements ITeacherDao { public void teach() { System.out.println("老师传授知识"); } } //目标接口 interface ITeacherDao { void teach(); } //代理类 class TeacherDAOProxy implements ITeacherDao { private ITeacherDao teacherDAO; public TeacherDAOProxy(ITeacherDao teacherDAO) { this.teacherDAO = teacherDAO; } @Override public void teach() { System.out.println("老师正式授课前的准备工作,如学生全部签到..."); teacherDAO.teach(); System.out.println("老师结束授课,如下课铃声响起..."); } } public class Client { public static void main(String[] args) { TeacherDAOProxy proxy = new TeacherDAOProxy(new TeacherDAO()); proxy.teach(); } }
-
动态代理
-
jdk代理
-
角色
-
接口 ITeacherDao
-
目标对象 TeacherDAO
-
代理类 TeacherDAOProxy
-
细节 不需要实现接口,但是目标对象要实现接口,否则不能用动态代理 代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象 代理类所在包:java.lang.reflect.Proxy
-
/** * 目标接口 */ interface ITeacherDao { String teach(); ITeacherDao sleep(int minutes); } class TeacherDao implements ITeacherDao{ @Override public String teach() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); return sdf.format(new Date())+":老师传授知识"; } @Override public ITeacherDao sleep(int minutes) { System.out.println("老师睡了" + minutes + "分钟"); return this; } } //真实代理类的外衣 class TeacherDaoProxy{ private ITeacherDao target; public TeacherDaoProxy(ITeacherDao target) { this.target = target; } public Object xxx(){ return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object obj = null; String methodName = method.getName(); System.out.println("目标方法" + methodName + ":jdk代理开始..."); System.out.println("真实代理对象:"+proxy.getClass()); System.out.println("目标对象:"+target.getClass()); if("sleep".equals(methodName)){ // method.invoke(target, args); // obj = proxy; obj = method.invoke(target, args); }else { // proxy是真实代理类,method是目标方法,args是目标方法携带的参数 obj = method.invoke(target, args); } System.out.println("目标方法" + methodName + ":jdk代理结束..."); return obj; } }); } } public class Client { public static void main(String[] args) { TeacherDaoProxy proxy = new TeacherDaoProxy(new TeacherDao()); ITeacherDao ins = (ITeacherDao) proxy.xxx(); System.out.println("===========代理类实例被使用 begin============="); System.out.println(ins); System.out.println("===========代理类实例被使用 end============="); System.out.println(ins.teach()); // System.out.println(proxy.execute()); System.out.println("===========代理类实例被使用 begin============="); ins.sleep(10); System.out.println("===========代理类实例被使用 end============="); ins.sleep(20).sleep(60); } }
-
- Cglib代理
- 角色
- 接口 ITeacherDao
- 目标对象 TeacherDAO
- 代理类 TeacherDAOProxy
- 细节
- 目标对象与代理对象都不需要实现接口
- Cglib 代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展
- Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口.它广泛的被许多 AOP 的框架使用
- Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类
- 注意:
- 1)需要引入 cglib 的 jar 文件
- 2)在内存中动态构建子类,注意代理的类不能为 final,否则报错java.lang.IllegalArgumentException:
- 3)目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
class TeacherDao {
public String teach() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(new Date()) + ":老师传授知识";
}
public TeacherDao sleep(int minutes) {
System.out.println("老师睡了" + minutes + "分钟");
return this;
}
}
//真实代理类的外衣
class TeacherDaoProxy implements MethodInterceptor {
private Object target;
public TeacherDaoProxy(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();
}
/**
* @param proxyIns 由CGLib动态生成的代理类实例
* @param method 上文中实体类所调用的被代理的方法引用
* @param args 参数值列表
* @param methodProxy 生成的代理类对方法的代理引用
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object proxyIns, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
String methodName = method.getName();
Object res;
System.out.println("目标方法" + methodName + ":cglib代理开始...");
System.out.println("真实代理对象:" + proxyIns.getClass());
System.out.println("目标对象:" + target.getClass());
if ("sleep".equals(methodName)) {
// method.invoke(target, args);
// obj = proxy;
res = method.invoke(target, args);
// res = methodProxy.invokeSuper(proxyIns,args);
res = proxyIns;
} else {
// proxy是真实代理类,method是目标方法,args是目标方法携带的参数
res = method.invoke(target, args);
}
System.out.println("目标方法" + methodName + ":cglib代理结束...");
return res;
}
}
public class Client {
public static void main(String[] args) {
TeacherDao proxy = (TeacherDao) new TeacherDaoProxy(new TeacherDao()).getProxyInstance();
proxy.sleep(111).sleep(222);
}
}