1. 适配器模式
将一个类的接口转化为客户希望的另外一个接口,使原本接口不兼容的类能够一起工作。
通俗的说就是适配器适用于有相关性但是不兼容的结构
举个例子:
家用电源与USB数据线有相关性,家用电源输入电压,USB输出电压,但是无法兼容,这时就需要一个适配器将电压转化为5v才能工作。
- 家用电源输出220V电压
1 2 3 4 5 6
| public class HomeBattery { int supply() { return 220; } }
|
- USB数据线只能输入5V电压
1 2 3 4 5 6 7
| public class USBLine { void charge(int volt) { if (volt != 5) throw new IllegalArgumentException("只能接收5V电压"); System.out.println("正常充电"); } }
|
- 适配之前是无法使用的
1 2 3 4 5 6 7 8 9 10 11
| public class User { @Test public void chargeForPhone() { HomeBattery homeBattery = new HomeBattery(); int homeVolt = homeBattery.supply(); System.out.println("家庭电源提供的电压是 " + homeVolt + "V");
USBLine usbLine = new USBLine(); usbLine.charge(homeVolt); } }
|
输出:
1 2 3
| 家庭电源提供的电压是 220V
java.lang.IllegalArgumentException: 只能接收 5V 电压
|
- 定义适配器
1 2 3 4 5
| public class Adapter { int convert(int homeVolt) { return homeVolt - 215; } }
|
- 使用适配器后能正常使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class User { public static void main(String[] args) { HomeBattery homeBattery = new HomeBattery(); int homeVolt = homeBattery.supply(); System.out.println("家庭电源提供的电压是" + homeVolt + "V");
Adapter adapter = new Adapter(); int chargeVolt = adapter.convert(homeVolt); System.out.println("使用适配器将家庭电压转换成了 " + chargeVolt + "V");
USBLine usbLine = new USBLine(); usbLine.charge(chargeVolt); } }
|
输出:
1 2 3
| 家庭电源提供的电压是220V 使用适配器将家庭电压转换成了 5V 正常充电
|
2. 桥接模式
将抽象部分与它的实现部分分离,使它们都可以独立地变化。
通俗的说就是如果一个对象有多种分类方式,如形状和颜色,应将每种分类方式分离出来,使用时将它们进行组合,而不是使用继承,继承会使类越来越多。
下面使桥接模式的实现:
颜色接口,包含一个获取颜色的方法
1 2 3
| public interface IColor { String getColor(); }
|
每种颜色实现该接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class Color { public static class Red implements IColor { @Override public String getColor() { return "红"; } }
public static class Blue implements IColor { @Override public String getColor() { return "蓝"; } }
public static class Yellow implements IColor { @Override public String getColor() { return "黄"; } }
public static class Green implements IColor { @Override public String getColor() { return "绿"; } } }
|
每个形状中桥接该接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class Rectangle implements IShape { private IColor color;
public void setColor(IColor color) { this.color = color; }
@Override public void draw() { System.out.println("绘制" + color.getColor() + "矩形"); } }
public class Round implements IShape{ private IColor color;
public void setColor(IColor color) { this.color = color; }
@Override public void draw() { System.out.println("绘制" + color.getColor() + "圆形"); } }
|
测试:
1 2 3 4 5 6 7 8 9 10 11
| public class Test { public static void main(String[] args) { Rectangle rectangle = new Rectangle(); rectangle.setColor(new Color.Red()); rectangle.draw();
Round round = new Round(); round.setColor(new Color.Blue()); round.draw(); } }
|
3. 组合模式
当整体与部分具有相似结构时使用组合模式
组合模式最主要的功能就是让用户可以一致对待整体和部分结构,将两者都作为一个相同的组件。
比如公司的人员分布结构:
该结构中主要分为管理者和和职员,管理者不只要管理职员,也需要管理管理者。
如果不使用组合模式,或造成大量的重复字段,而且不能统一对待管理者和职员
使用组合模式:
- 新建一个抽象组件类(提取公共字段)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public abstract class Component { private String position; private String job;
public Component(String position, String job) { this.position = position; this.job = job; }
public void work() { System.out.println("我是" + position + ",我正在" + job); }
abstract void addComponent(Component component);
abstract void removeComponent(Component component);
abstract void check();
}
|
管理者继承此类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class Manager extends Component{
private List<Component> components = new ArrayList<>();
public Manager(String position, String job) { super(position, job); }
@Override void addComponent(Component component) { components.add(component); }
@Override void removeComponent(Component component) { components.remove(components.size() - 1); }
@Override void check() { work(); for (Component component : components) { component.check(); } } }
|
职员继承此类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class Employee extends Component {
public Employee(String position, String job) { super(position, job); }
@Override void addComponent(Component component) { System.out.println("职员没有管理权限"); }
@Override void removeComponent(Component component) { System.out.println("职员没有管理权限"); }
@Override void check() { work(); } }
|
客户端测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class Test { public static void main(String[] args) { Component boss = new Manager("老板", "唱怒放的生命"); Component HR = new Employee("人力资源", "聊微信"); Component PM = new Manager("产品经理", "不知道干啥"); Component CFO = new Manager("财务主管", "看剧"); Component CTO = new Manager("技术主管", "划水"); Component UI = new Employee("设计师", "画画"); Component operator = new Employee("运营人员", "兼职客服"); Component webProgrammer = new Employee("程序员", "学习设计模式"); Component backgroundProgrammer = new Employee("后台程序员", "CRUD"); Component accountant = new Employee("会计", "背九九乘法表"); Component clerk = new Employee("文员", "给老板递麦克风"); boss.addComponent(HR); boss.addComponent(PM); boss.addComponent(CFO); PM.addComponent(UI); PM.addComponent(CTO); PM.addComponent(operator); CTO.addComponent(webProgrammer); CTO.addComponent(backgroundProgrammer); CFO.addComponent(accountant); CFO.addComponent(clerk);
boss.check(); } }
|
输出:
1 2 3 4 5 6 7 8 9 10 11
| 我是老板,我正在唱怒放的生命 我是人力资源,我正在聊微信 我是产品经理,我正在不知道干啥 我是设计师,我正在画画 我是技术主管,我正在划水 我是程序员,我正在学习设计模式 我是后台程序员,我正在CRUD 我是运营人员,我正在兼职客服 我是财务主管,我正在看剧 我是会计,我正在背九九乘法表 我是文员,我正在给老板递麦克风
|
使用组合模式后将公有字段移动到了抽象类中,降低了代码重复,在客户端统一对待管理者和职员为Component
透明方式
在 Component 中声明所有管理子对象的方法,包括 add 、remove 等,这样继承自 Component 的子类都具备了 add、remove 方法。对于外界来说叶节点和枝节点是透明的,它们具备完全一致的接口。
优点: 让 Manager 类和 Employee 类具备完全一致的行为接口,调用者可以一致对待它们。
缺点: Employee 类并不支持管理子对象,不仅违背了接口隔离原则,而且客户端可以用 Employee 类调用 addComponent 和 removeComponent 方法,导致程序出错,所以这种方式是不安全的。
安全方式
将 addComponent 和 removeComponent 方法移到 Manager 子类中去单独实现
将接口中某个对象独有的方法单独放到子类实现
优点: 符合接口隔离原则
缺点: 由于接口不一致所以无法统一对待对象
组合模式透明方式和安全方式的区别
- 透明方式:在Component中声明所有管理子对象的方法,所有实现该接口的子类都具备一样的接口
- 安全方式: 在Component中不声明管理子对象的方法,叶子节点无需实现管理子对象的方法,只需在枝节节点中实现管理子对象的方法
4.装饰器模式
增强功能的装饰模式
增强功能的装饰模式,不改变类的原有功能因此可以无限装饰,也叫透明装饰模式。
例子:
颜值接口
1 2 3
| public interface IBeauty { int getBeautyValue(); }
|
新建Me类实现颜值接口
1 2 3 4 5 6
| public class Me implements IBeauty{ @Override public int getBeautyValue() { return 100; } }
|
新建戒指装饰类
1 2 3 4 5 6 7 8 9 10
| public class RingDecorator implements IBeauty{ private final IBeauty me; public RingDecorator(IBeauty me) { this.me = me; } @Override public int getBeautyValue() { return me.getBeautyValue() + 20; } }
|
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Client { public static void main(String[] args) { IBeauty me = new Me(); System.out.println("我本来的颜值:" + me.getBeautyValue());
IBeauty meWithRing = new RingDecorator(me); System.out.println("戴上了戒指后,我的颜值:" + meWithRing.getBeautyValue());
RingDecorator meWithRingRing = new RingDecorator(meWithRing); System.out.println("戴上两个戒指后,我的颜值:" + meWithRingRing.getBeautyValue()); } }
|
输出:
1 2 3
| 我本来的颜值:100 戴上了戒指后,我的颜值:120 戴上两个戒指后,我的颜值:140
|
用于添加功能的装饰器模式
不能多次装饰,被装饰者对于客户端可以是实现任何接口的对象所以是透明的,装饰者添加了新的功能所以要区别对待,是不透明的,所以是半透明装饰模式。
例子:
房屋接口:
1 2 3
| public interface IHouse { void live(); }
|
实现类:
1 2 3 4 5 6
| public class House implements IHouse{ @Override public void live() { System.out.println("房屋的原有功能:居住功能"); } }
|
新增粘钩装饰器,继承房屋接口:
1 2 3
| public interface IStickyHookHouse extends IHouse{ void hangThings(); }
|
装饰器实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class StickyHookDecorator implements IStickyHookHouse{ private final IHouse house;
public StickyHookDecorator(IHouse house) { this.house = house; }
@Override public void handThings() { System.out.println("有了粘钩功能后,新增了挂东西的功能"); }
@Override public void live() { house.live(); } }
|
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class Client { public static void main(String[] args) { House house = new House(); house.live();
IStickyHookHouse stickyHookHouse = new StickyHookDecorator(house); stickyHookHouse.live(); stickyHookHouse.handThings();
IMirrorHouse mirrorDecorator = new MirrorDecorator(house); mirrorDecorator.live(); mirrorDecorator.lookMirror();
IMirrorHouse mirrorDecorator1 = new MirrorDecorator(stickyHookHouse); mirrorDecorator1.lookMirror(); mirrorDecorator1.live();
} }
|
输出:
1 2 3
| 房屋的原有功能:居住功能 房屋的原有功能:居住功能 有了粘钩功能后,新增了挂东西的功能
|
5. 外观模式
外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称为门面模式。
将多个子系统封装起来,提供一个更为简洁的接口供外部调用
6. 享元模式
运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式。
通俗的说就是尽可能共享对象提高复用性
7. 代理模式
给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
静态代理
例子 :
在网络请求前后,分别打印将要发送的数据和接收到数据作为日志信息。此时我们就可以新建一个网络请求的代理类,让它代为处理网络请求,并在代理类中打印这些日志信息。
网络请求接口:
1 2 3 4 5
| public interface IHttp { void request(String sendData);
void onSuccess(String receivedData); }
|
http请求工具类:
1 2 3 4 5 6 7 8 9 10 11
| public class HttpUtil implements IHttp { @Override public void request(String sendData) { System.out.println("网络请求..."); }
@Override public void onSuccess(String receiveData) { System.out.println("网络请求完成"); } }
|
http请求代理类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class HttpProxy implements IHttp {
private final IHttp http;
public HttpProxy(IHttp http) { this.http = http; }
@Override public void request(String sendData) { System.out.println("发送数据" + sendData); http.request(sendData); }
@Override public void onSuccess(String receiveData) { System.out.println("收到数据" + receiveData); http.request(receiveData); } }
|
客户端测试:
1 2 3 4 5 6 7 8 9 10
| public class Client { @Test public void test() { HttpUtil httpUtil = new HttpUtil(); HttpProxy httpProxy = new HttpProxy(httpUtil); httpProxy.request("request data"); httpProxy.onSuccess("received result"); } }
|
输出:
1 2 3 4
| 发送数据request data 网络请求... 收到数据received result 网络请求...
|
代理模式主要是为了加以控制。
动态代理
动态代理,需要把一个类传入,然后根据它正在调用的方法名判断是否需要加以控制。利用了反射技术
实现动态代理的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class HttpProxy implements InvocationHandler {
private HttpUtil httpUtil;
public IHttp getInstance(HttpUtil httpUtil) { this.httpUtil = httpUtil; return (IHttp) Proxy.newProxyInstance(HttpUtil.class.getClassLoader(), httpUtil.getClass().getInterfaces(), this); }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; if (method.getName().equals("request")) { System.out.println("发送数据" + args[0]); result = method.invoke(httpUtil, args); } else if (method.getName().equals("onSuccess")) { System.out.println("收到数据" + args[0]); result = method.invoke(httpUtil, args); } return result; } }
|
客户端测试:
1 2 3 4 5 6 7 8
| public class Client { public static void main(String[] args) { HttpUtil httpUtil = new HttpUtil(); IHttp proxy = new HttpProxy().getInstance(httpUtil); proxy.request("request data"); proxy.onSuccess("received result"); } }
|
输出:
1 2 3 4
| 发送数据request data 网络请求... 收到数据received result 网络请求完成
|
Git仓库
https://github.com/wuzheng228/designPatternGuide
参考
《深入浅出设计模式》