适配器模式
适配器就是一种适配中间件,它存在于不匹配的了两者之间,用于连接两者,使不匹配变得匹配。
手机充电需要将220V的交流电转化为手机锂电池需要的5V直流电
知识补充:手机充电器输入的电流是交流,通过变压整流输出电流是直流的。。
类适配器
️2️⃣Adaptee 源角色 :220V的交流电(家用电一般是220V)
//家用电压
public class HomeElectri {
//输出220v交流电,AC指的是交流电
public int outputAC220V() {
int output = 220;
return output;
}
}
1️⃣Target 目标角色 : 手机锂电池需要的5V直流电
目标类只需要定义方法,由适配器来转化:
//手机充电接口
public interface MobileCharge {
//需要5v的直流电
//DC指的是直流电
int outputDC5V();
}
3️⃣Adapter 适配器角色 :手机充电器
我为什么这么写,又去继承又去实现的?
作为适配器,需要拿到俩边的资源。通过继承拿到源角色的能力(输出220V)通过实现接口,知道自己的目的角色。
//手机的适配器(手机充电器)
public class PhoneAdapter extends HomeElectri implements MobileCharge {
@Override
public int outputDC5V() {
int output = outputAC220V();
return (output / 44);
}
}
对象适配器
不继承HomeElectri 类,而是持有HomeElectri 类的实例。
提供一个包装类Adapter,这个包装类包装了一个Adaptee的实例,从而此包装类能够把Adaptee的API与Target类的API链接起来。
//手机的适配器(手机充电器)
public class PhoneAdapter implements MobileCharge {
HomeElectri homeElectri;
public PhoneAdapter(HomeElectri homeElectri) {
this.homeElectri = homeElectri;
}
@Override
public int outputDC5V() {
int output = homeElectri.outputAC220V();
return (output / 44);
}
}
PhoneAdapter adapter=new PhoneAdapter(new HomeElectri()); System.out.println("输出电压: "+adapter.outputDC5V());
接口适配器
接口的适配器是这样的:接口中往往有多个抽象方法,但是我们写该接口的实现类的时候,必须实现所有这些方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,
如:MouseListener是一个接口,里面有鼠标的各种事件
public interface MouseListener extends EventListener { public void mouseClicked(MouseEvent e); public void mousePressed(MouseEvent e); public void mouseReleased(MouseEvent e); public void mouseEntered(MouseEvent e); public void mouseExited(MouseEvent e); }
此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法
MouseAdapter有2个特点
第一:它是抽象类。
第二:每个方法MouseAdapter都做了空实现
public abstract class MouseAdapter implements MouseListener, MouseWheelListener, MouseMotionListener { public void mouseClicked(MouseEvent e) {} public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mouseWheelMoved(MouseWheelEvent e){} public void mouseDragged(MouseEvent e){} public void mouseMoved(MouseEvent e){} }
而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。
addMouseListener()这个方法需要MouseListener的一个参数
JButton jButton = new JButton("点我有惊喜"); jButton.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { System.out.println("惊不惊喜,意不意外"); } });
装饰器模式
装饰器模式核心思想
在不改变原有类的基础上给类新增额外的功能,符合开闭原则: 对扩展开放,对修改关闭
1.装饰模式是继承的一个替代模式,通过组合的方式完成继承的功能,但却避免了继承的侵入性
2.装饰类和被装饰类可以独立发展,不会相互耦合
原有的:
//制作蛋糕 public interface MakCake { //cake -蛋糕 void createCake(); }
它的实现类
public class MakeCakeImpl implements MakCake { //制作蛋糕的通用基础逻辑 @Override public void createCake() { System.out.println("使用面粉、鸡蛋、牛奶等...制作一个基础蛋糕"); } }
抽象装饰器
抽象装饰器: 通用的装饰的装饰器,其内部必然,有一个属性指向,其实现一般是一个抽象类
被装饰类和所有的装饰类必须实现同一个接口,而且必须持有被装饰的对象,可以无限装饰
//制作蛋糕的抽象装饰器
public abstract class CakeDecorator implements MakCake{
private MakCake makCake;
public CakeDecorator(MakCake makCake) {
this.makCake = makCake;
}
@Override
public void createCake() {
makCake.createCake();
}
}
具体装饰器角色:向“制作蛋糕”添加新的职责
给蛋糕添加水果
public class FruitAddDecorator extends AbsCakeDecorator{ public FruitAddDecorator(MakCake makCake) { super(makCake); } @Override public void createCake() { super.createCake(); decorateMethod(); } // 定义自己的修饰逻辑 private void decorateMethod() { System.out.println("加一份新鲜的水果,如草莓..."); } }
给蛋糕添加饼干
public class BiscuitAddDecorator extends AbsCakeDecorator { public BiscuitAddDecorator(MakCake makCake) { super(makCake); } @Override public void createCake() { super.createCake(); decorateMethod(); } // 定义自己的修饰逻辑 private void decorateMethod() { System.out.println("加一份好吃的饼干,如奥利奥..."); } }
测试
public static void main(String[] args) { //制作最基础的蛋糕面饼 MakCake cake = new MakeCakeImpl(); //为蛋糕添加水果 cake= new FruitAddDecorator(cake); cake.createCake(); }
控制台:
无限装饰?
什么是无限装饰?使用装饰器模式一层一层的对最底层被包装类进行功能扩展
了。
- 对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,得到功能更为强大的对象。
public static void main(String[] args) {
//制作最基础的蛋糕面饼
MakCake cake = new MakeCakeImpl();
//为蛋糕添加水果
cake= new FruitAddDecorator(cake);
// 第二次修饰,蛋糕添加饼干
cake = new BiscuitAddDecorator(cake);
cake.createCake();
}
先看结果
new 构造器分析
如上,再new装饰器的时候,构造器做啥了
//为蛋糕添加水果 cake= new FruitAddDecorator(cake);
1 进入FruitAddDecorator
2 AbsCakeDecorator中的makCake赋值为MakeCakeImpl的实例
-----------------------------------
代码再走一行
// 第二次修饰,蛋糕添加饼干 cake = new BiscuitAddDecorator(cake);
3. 进入BiscuitAddDecorator
4.又进入父类AbsCakeDecorator,AbsCakeDecorator中的makCake赋值为FruitAddDecorator的实例
调用方法分析
public static void main(String[] args) {
//制作最基础的蛋糕面饼
MakCake cake = new MakeCakeImpl();
//为蛋糕添加水果
cake= new FruitAddDecorator(cake);
// 第二次修饰,蛋糕添加饼干
cake = new BiscuitAddDecorator(cake);
cake.createCake();
}
分析完前面的new构造器之后,cake.createCake()做啥了???
此时cake是BiscuitAddDecorator的引用
1. 会先调用BiscuitAddDecorator的createCake方法,
2. 走super的createCake(),进入其父类AbsCakeDecorator的方法
3.1 从idea提示看的出此时makCake是FruitAddDecorator的实例,那么接着进入FruitAddDecorator的createCake()
3.2.走super的createCake(),再进入AbsCakeDecorator,此时makCake是MakeCakeImpl
的实例
3.3.进入MakeCakeImpl,控制台打印输出
打印完成后回到3.1,再打印输出
再回到第一步,BiscuitAddDecorator的createCake(),打印输出
在java哪里见过
这是我在学习安全的并发容器类时用到过的,比如
List<Object> synchronizedList = Collections.synchronizedList(new ArrayList<>());
我们来看SynchronizedList,它是Collections的静态内部类。
public class Collections {
static class SynchronizedList<E> extends SynchronizedCollection<E> implements List<E> {
final List<E> list;
SynchronizedList(List<E> list) {
super(list);
this.list = list;
}
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);}
}
public E remove(int index) {
synchronized (mutex) {return list.remove(index);}
}
//...很多方法都用synchronized代码块包裹了,做成了并发安全
}
}
代理模式
代理模式的特点在于隔离:隔离调用类和被调用类的关系,通过一个代理类去调用
可以在不改变原有目标对象的基础上,对目标对象增加额外的扩展功能
静态代理
被代理对象与代理对象需要实现相同的接口或者是继承相同父类,因此要
定义一个接口或抽象类。
public interface Calculator { int add(int i, int j); }
接口的实现类
public class CalculatorImpl implements Calculator { @Override public int add(int i, int j) { int result = i + j; return result; } }
现在想要在add方法执行之前,执行后打印返回结果。做一个增强,很容易想到这么做。。但是一看就不符合开闭原则。。
增加一个代理类,在代理类里打印日志,在代理类里去调用目标对象的方法
public class CalculatorProxy implements Calculator{
private CalculatorImpl calculatorImpl;
public CalculatorProxy(CalculatorImpl calculatorImpl) {
this.calculatorImpl = calculatorImpl;
}
@Override
public int add(int i, int j) {
System.out.println("[日志] add 方法执行前,参数是:" + i + "," + j);
int result = calculatorImpl.add(i, j);
System.out.println("[日志] add 方法结束,运算结果是:" + result);
return result;
}
}
测试:
public static void main(String[] args) { //创建目标对象 CalculatorImpl calculator = new CalculatorImpl(); //创建代理对象 CalculatorProxy calculatorProxy = new CalculatorProxy(calculator); //使用代理对象去调用方法,隔离目标对象和客户端 calculatorProxy.add(1,3); }
jdk动态代理
jdk动态代理是基于接口的一种代理方式,目标对象一定要实现接口。
原理是,利用反射机制,动态生成匿名类继承Proxy类并且实现了要代理的接口