四、结构型

1. 适配器(Adapter)

Intent

把一个类接口转换成另一个用户需要的接口。

image.png

Class Diagram

Target:定义客户端需要的跟特定领域相关的接口。

Adaptee:已经存在的接口,通常能满足客户端的功能要求,
但是接口与客户端要求的特定领域接口不一致,需要被适配

Adapter:适配器,把Adaptee适配成为Client需要的Target。

image.png

Implementation1

美国的电饭煲是在电压为 110V 下工作,而中国的电饭煲在电压 220V 下工作。要求将在美国使用的电饭煲适配成能在中国使用。

  1. /**
  2. * Adaptee:已经存在的接口,通常能满足客户端的功能要求。
  3. * 但是接口与客户端要求的特定领域接口不一致,需要被适配。
  4. */
  5. public interface CHINA220 {
  6. //220 V电压接口
  7. void connect();
  8. }
  1. public class CHINA220Impl implements CHINA220{
  2. @Override
  3. public void connect() {
  4. System.out.println("220V 接通电源,开始工作...");
  5. }
  6. }
  1. /**
  2. * Target:定义客户端需要的跟特定领域相关的接口。
  3. */
  4. public interface USA110 {
  5. //110 V电压接口
  6. void connect();
  7. }
  1. /**
  2. * Adapter:适配器,把Adaptee适配成为Client需要的Target。
  3. *
  4. * 将220V(Adapter)适配成110V(Target)
  5. */
  6. public class PowerAdapter implements USA110{
  7. private CHINA220 china220;
  8. public PowerAdapter(CHINA220 china220){
  9. this.china220=china220;
  10. }
  11. @Override
  12. public void connect() {
  13. china220.connect();
  14. }
  15. }
  1. /**
  2. * 该电器在110V下工作
  3. */
  4. public class USACooker {
  5. private USA110 usa110;
  6. public USACooker(USA110 usa110) {
  7. this.usa110 = usa110;
  8. }
  9. public void cook(){
  10. usa110.connect();
  11. System.out.println("开始煮饭...");
  12. }
  13. }
  1. public class Client {
  2. public static void main(String[] args) {
  3. // 110 V 电压下直接 cook
  4. USA110 usa110 = new USA110Impl();
  5. USACooker usaCooker = new USACooker(usa110);
  6. usaCooker.cook();
  7. // 220 V 电压下需要适配成 110V,才可 cook
  8. CHINA220 china220 = new CHINA220Impl();
  9. PowerAdapter adapter = new PowerAdapter(china220);
  10. USACooker usaCooker2 = new USACooker(adapter);
  11. usaCooker2.cook();
  12. }
  13. }
  1. 110V 接通电源,开始工作...
  2. 开始煮饭...
  3. 220V 接通电源,开始工作...
  4. 开始煮饭...

Implementation2

鸭子(Duck)和火鸡(Turkey)拥有不同的叫声,Duck 的叫声调用 quack() 方法,而 Turkey 调用 gobble() 方法。

要求将 Turkey 的 gobble() 方法适配成 Duck 的 quack() 方法,从而让火鸡冒充鸭子!

  1. public interface Duck {
  2. void quack();
  3. }
  1. public interface Turkey {
  2. void gobble();
  3. }
  1. public class WildTurkey implements Turkey {
  2. @Override
  3. public void gobble() {
  4. System.out.println("gobble!");
  5. }
  6. }
  1. public class TurkeyAdapter implements Duck {
  2. Turkey turkey;
  3. public TurkeyAdapter(Turkey turkey) {
  4. this.turkey = turkey;
  5. }
  6. @Override
  7. public void quack() {
  8. turkey.gobble();
  9. }
  10. }
  1. public class Client {
  2. public static void main(String[] args) {
  3. Turkey turkey = new WildTurkey();
  4. Duck duck = new TurkeyAdapter(turkey);
  5. duck.quack();
  6. }
  7. }

JDK

2. 桥接(Bridge)

Intent

将抽象与实现分离开来,使它们可以独立变化。

Class Diagram

  • Abstraction:定义抽象类的接口
  • Implementor:定义实现类接口

image.png

Implementation

RemoteControl 表示遥控器,指代 Abstraction。

TV 表示电视,指代 Implementor。

桥接模式将遥控器和电视分离开来,从而可以独立改变遥控器或者电视的实现。

  1. public abstract class TV {
  2. public abstract void on();
  3. public abstract void off();
  4. public abstract void tuneChannel();
  5. }
  1. public class Sony extends TV {
  2. @Override
  3. public void on() {
  4. System.out.println("Sony.on()");
  5. }
  6. @Override
  7. public void off() {
  8. System.out.println("Sony.off()");
  9. }
  10. @Override
  11. public void tuneChannel() {
  12. System.out.println("Sony.tuneChannel()");
  13. }
  14. }
  1. public class RCA extends TV {
  2. @Override
  3. public void on() {
  4. System.out.println("RCA.on()");
  5. }
  6. @Override
  7. public void off() {
  8. System.out.println("RCA.off()");
  9. }
  10. @Override
  11. public void tuneChannel() {
  12. System.out.println("RCA.tuneChannel()");
  13. }
  14. }
  1. public abstract class RemoteControl {
  2. protected TV tv;
  3. public RemoteControl(TV tv) {
  4. this.tv = tv;
  5. }
  6. public abstract void on();
  7. public abstract void off();
  8. public abstract void tuneChannel();
  9. }
  1. public class ConcreteRemoteControl1 extends RemoteControl {
  2. public ConcreteRemoteControl1(TV tv) {
  3. super(tv);
  4. }
  5. @Override
  6. public void on() {
  7. System.out.println("ConcreteRemoteControl1.on()");
  8. tv.on();
  9. }
  10. @Override
  11. public void off() {
  12. System.out.println("ConcreteRemoteControl1.off()");
  13. tv.off();
  14. }
  15. @Override
  16. public void tuneChannel() {
  17. System.out.println("ConcreteRemoteControl1.tuneChannel()");
  18. tv.tuneChannel();
  19. }
  20. }
  1. public class ConcreteRemoteControl2 extends RemoteControl {
  2. public ConcreteRemoteControl2(TV tv) {
  3. super(tv);
  4. }
  5. @Override
  6. public void on() {
  7. System.out.println("ConcreteRemoteControl2.on()");
  8. tv.on();
  9. }
  10. @Override
  11. public void off() {
  12. System.out.println("ConcreteRemoteControl2.off()");
  13. tv.off();
  14. }
  15. @Override
  16. public void tuneChannel() {
  17. System.out.println("ConcreteRemoteControl2.tuneChannel()");
  18. tv.tuneChannel();
  19. }
  20. }
  1. public class Client {
  2. public static void main(String[] args) {
  3. RemoteControl remoteControl1 = new ConcreteRemoteControl1(new RCA());
  4. remoteControl1.on();
  5. remoteControl1.off();
  6. remoteControl1.tuneChannel();
  7. RemoteControl remoteControl2 = new ConcreteRemoteControl2(new Sony());
  8. remoteControl2.on();
  9. remoteControl2.off();
  10. remoteControl2.tuneChannel();
  11. }
  12. }

JDK

  • AWT (It provides an abstraction layer which maps onto the native OS the windowing support.)
  • JDBC

3. 组合(Composite)

Intent

将对象组合成树形结构来表示“整体/部分”层次关系,允许用户以相同的方式处理单独对象和组合对象。

Class Diagram

Leaf:叶子节点对象,定义和实现叶子对象的行为,不再包含其它的子节点对象。

Composite:组合对象,通常会存储子组件,定义包含子组件的那些组件的行为,并实现在组件接口中定义的与子组件有关的操作。

组件(Component)类是组合类(Composite)和叶子类(Leaf)的父类,可以把组合类看成是树的中间节点。

组合对象拥有一个或者多个组件对象,因此组合对象的操作可以委托给组件对象去处理,而组件对象可以是另一个组合对象或者叶子对象。

image.png

Implementation1

商品类别树的管理,比如有如下所示的商品类别树:

  1. - 服装
  2. - 男装
  3. - 衬衣
  4. - 夹克
  5. - 女装
  6. - 裙子
  7. - 套装
  • 根节点,比如服装,它没有父节点,它可以包含其它的节点
  • 树枝节点,有一类节点可以包含其它的节点,称之为树枝节点,比如男装、女装
  • 叶子节点,有一类节点没有子节点,称之为叶子节点,比如衬衣、夹克、裙子、套装
  1. /**
  2. * 组件抽象类
  3. */
  4. public abstract class Component {
  5. /**
  6. * 输出组件自身的名称
  7. */
  8. public abstract void doOperation(String preStr);
  9. /**
  10. * 向组合对象中加入组件对象
  11. * @param child 被加入组合对象中的组件对象
  12. */
  13. public void add(Component child) {
  14. throw new UnsupportedOperationException("对象不支持这个功能");
  15. }
  16. /**
  17. * 从组合对象中移出某个组件对象
  18. * @param child 被移出的组件对象
  19. */
  20. public void remove(Component child) {
  21. throw new UnsupportedOperationException("对象不支持移除功能");
  22. }
  23. /**
  24. * 返回某个索引对应的组件对象
  25. * @param index 需要获取的组件对象的索引,索引从0开始
  26. * @return 索引对应的组件对象
  27. */
  28. public Component getChildren(int index) {
  29. throw new UnsupportedOperationException("对象不支持获取子节点功能");
  30. }
  31. }
  1. /**
  2. * 叶子对象
  3. */
  4. public class Leaf extends Component{
  5. //叶子对象元素信息
  6. private String name;
  7. public Leaf(String name){
  8. this.name=name;
  9. }
  10. @Override
  11. public void doOperation(String preStr) {
  12. System.out.println(preStr+"-"+name);
  13. }
  14. }
  1. /**
  2. * 组合对象,可以包含其它组合对象或者叶子对象
  3. */
  4. public class Composite extends Component{
  5. private String name;
  6. /**
  7. * 用来存储组合对象中包含的子组件对象
  8. */
  9. private List<Component> childComponents;
  10. public Composite(String name){
  11. this.name=name;
  12. }
  13. @Override
  14. public void add(Component child) {
  15. if(childComponents==null){
  16. childComponents=new ArrayList<>();
  17. }
  18. childComponents.add(child);
  19. }
  20. /**
  21. * /**
  22. * 输出组合对象自身的结构
  23. * @param preStr 主要是按照层级拼接的空格,实现向后缩进
  24. */
  25. @Override
  26. public void doOperation(String preStr) {
  27. System.out.println(preStr+"+"+this.name);
  28. //如果还包含有子组件,那么就输出这些子组件对象
  29. if(this.childComponents!=null){
  30. //然后添加一个空格,表示向后缩进一个空格
  31. preStr+=" ";
  32. //输出当前对象的子对象了
  33. for(Component c : childComponents){
  34. //递归输出每个子对象
  35. c.doOperation(preStr);
  36. }
  37. }
  38. }
  39. }
  1. ublic class Client {
  2. public static void main(String[] args) {
  3. //定义所有的组合对象
  4. Component root = new Composite("服装");
  5. Component c1 = new Composite("男装");
  6. Component c2 = new Composite("女装");
  7. //定义所有的叶子对象
  8. Component leaf1 = new Leaf("衬衣");
  9. Component leaf2 = new Leaf("夹克");
  10. Component leaf3 = new Leaf("裙子");
  11. Component leaf4 = new Leaf("套装");
  12. //按照树的结构来组合组合对象和叶子对象
  13. root.add(c1);
  14. root.add(c2);
  15. c1.add(leaf1);
  16. c1.add(leaf2);
  17. c2.add(leaf3);
  18. c2.add(leaf4);
  19. //调用根对象的输出功能来输出整棵树
  20. root.doOperation("");
  21. }
  22. }

输出结果:

  1. +服装
  2. +男装
  3. -衬衣
  4. -夹克
  5. +女装
  6. -裙子
  7. -套装

Implementation2

  1. public abstract class Component {
  2. protected String name;
  3. public Component(String name) {
  4. this.name = name;
  5. }
  6. public void print() {
  7. print(0);
  8. }
  9. abstract void print(int level);
  10. abstract public void add(Component component);
  11. abstract public void remove(Component component);
  12. }
  1. public class Composite extends Component{
  2. private List<Component> child;
  3. public Composite(String name){
  4. super(name);
  5. if(child==null){
  6. child=new ArrayList<>();
  7. }
  8. }
  9. @Override
  10. void print(int level) {
  11. for (int i = 0; i < level; i++) {
  12. System.out.print(" ");
  13. }
  14. System.out.println("+" + name);
  15. for (Component component : child) {
  16. component.print(level + 1);
  17. }
  18. }
  19. @Override
  20. public void add(Component component) {
  21. child.add(component);
  22. }
  23. @Override
  24. public void remove(Component component) {
  25. child.remove(component);
  26. }
  27. }
  1. public class Leaf extends Component {
  2. public Leaf(String name) {
  3. super(name);
  4. }
  5. @Override
  6. void print(int level) {
  7. for (int i = 0; i < level; i++) {
  8. System.out.print(" ");
  9. }
  10. System.out.println("-"+name);
  11. }
  12. @Override
  13. public void add(Component component) {
  14. throw new UnsupportedOperationException("对象不支持这个功能");
  15. }
  16. @Override
  17. public void remove(Component component) {
  18. throw new UnsupportedOperationException("对象不支持这个功能");
  19. }
  20. }
  1. public class Client {
  2. public static void main(String[] args) {
  3. Component root=new Composite("root");
  4. Component node1=new Leaf("1");
  5. Component node2=new Composite("2");
  6. Component node3=new Leaf("3");
  7. Component node21=new Leaf("21");
  8. Component node22=new Composite("22");
  9. Component node221=new Leaf("221");
  10. root.add(node1);
  11. root.add(node2);
  12. root.add(node3);
  13. node2.add(node21);
  14. node2.add(node22);
  15. node22.add(node221);
  16. root.print();
  17. }
  18. }

输出结果:

  1. +root
  2. -1
  3. +2
  4. -21
  5. +22
  6. -221
  7. -3

JDK

  • javax.swing.JComponent#add(Component)
  • java.awt.Container#add(Component)
  • java.util.Map#putAll(Map)
  • java.util.List#addAll(Collection)
  • java.util.Set#addAll(Collection)

4. 装饰(Decorator)

Intent

为对象动态添加功能。

Class Diagram

装饰者(Decorator)和具体组件(ConcreteComponent)都继承自组件(Component),具体组件的方法实现不需要依赖于其它对象,而装饰者组合了一个组件,这样它可以装饰其它装饰者或者具体组件。所谓装饰,就是把这个装饰者套在被装饰者之上,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最低层,因为只有具体组件的方法实现不需要依赖于其它对象。

image.png

Implementation1

给普通手机装饰上彩铃等功能。

  1. /**
  2. * Component:组件对象的接口,可以给这些对象动态的添加职责。
  3. */
  4. public interface Phone {
  5. void call();
  6. }
  1. /**
  2. * ConcreteComponent:具体的组件对象,实现组件对象接口,
  3. * 通常就是被装饰器装饰的原始对象,也就是可以给这个对象添加职责。
  4. */
  5. public class IPhone implements Phone{
  6. @Override
  7. public void call() {
  8. System.out.println("IPhone打电话");
  9. }
  10. }
  1. /**
  2. * Decorator:所有装饰器的抽象父类,需要定义一个与组件接口一致的接口,并持有一个Component对象,其实就是持有一个被装饰的对象。
  3. * 注意,这个被装饰的对象不一定是最原始的那个对象了,
  4. * 也可能是被其它装饰器装饰过后的对象,反正都是实现的同一个接口,也就是同一类型。
  5. */
  6. public abstract class PhoneDecorator implements Phone{
  7. protected Phone phone;
  8. public PhoneDecorator(Phone phone){
  9. this.phone=phone;
  10. }
  11. @Override
  12. public void call() {
  13. phone.call();
  14. }
  15. }
  1. /**
  2. * 具体的装饰类,实现具体要向被装饰对象添加的功能。
  3. */
  4. public class MusicPhoneDecorator extends PhoneDecorator{
  5. public MusicPhoneDecorator(Phone phone) {
  6. super(phone);
  7. }
  8. @Override
  9. public void call(){
  10. phone.call();
  11. System.out.println("接完电话听点音乐");
  12. }
  13. }
  1. /**
  2. * 具体的装饰类,实现具体要向被装饰对象添加的功能。
  3. */
  4. public class RingPhoneDecorator extends PhoneDecorator {
  5. public RingPhoneDecorator(Phone phone) {
  6. super(phone);
  7. }
  8. @Override
  9. public void call(){
  10. System.out.println("接电话前响铃");
  11. phone.call();
  12. }
  13. }
  1. public class Client {
  2. public static void main(String[] args) {
  3. //普通手机
  4. Phone p=new IPhone();
  5. p.call();
  6. System.out.println("====================");
  7. //使用装饰类 -->增加了彩铃功能
  8. RingPhoneDecorator rpd=new RingPhoneDecorator(p);
  9. rpd.call();
  10. System.out.println("====================");
  11. //使用装饰类 -->增加了听音乐的功能
  12. MusicPhoneDecorator mpd=new MusicPhoneDecorator(p);
  13. mpd.call();
  14. System.out.println("====================");
  15. //装饰类可以组合 -->先响铃-->接电话-->接完电话后,就听音乐
  16. //装饰类的特点
  17. MusicPhoneDecorator mpd2=new
  18. MusicPhoneDecorator(new RingPhoneDecorator(p));
  19. mpd2.call();
  20. System.out.println("===========================");
  21. RingPhoneDecorator mpd3= new
  22. RingPhoneDecorator(new MusicPhoneDecorator(p));
  23. mpd3.call();
  24. }
  25. }

输出结果:

  1. IPhone打电话
  2. ====================
  3. 接电话前响铃
  4. IPhone打电话
  5. ====================
  6. IPhone打电话
  7. 接完电话听点音乐
  8. ====================
  9. 接电话前响铃
  10. IPhone打电话
  11. 接完电话听点音乐
  12. ===========================
  13. 接电话前响铃
  14. IPhone打电话
  15. 接完电话听点音乐

Implementation2

设计不同种类的饮料,饮料可以添加配料,比如可以添加牛奶,并且支持动态添加新配料。每增加一种配料,该饮料的价格就会增加,要求计算一种饮料的价格。

下图表示在 DarkRoast 饮料上新增新添加 Mocha 配料,之后又添加了 Whip 配料。DarkRoast 被 Mocha 包裹,Mocha 又被 Whip 包裹。它们都继承自相同父类,都有 cost() 方法,外层类的 cost() 方法调用了内层类的 cost() 方法。

image.png

  1. public interface Beverage {
  2. double cost();
  3. }
  1. public class DarkRoast implements Beverage {
  2. @Override
  3. public double cost() {
  4. return 1;
  5. }
  6. }
  1. public class HouseBlend implements Beverage {
  2. @Override
  3. public double cost() {
  4. return 1;
  5. }
  6. }
  1. public abstract class CondimentDecorator implements Beverage {
  2. protected Beverage beverage;
  3. }
  1. public class Milk extends CondimentDecorator {
  2. public Milk(Beverage beverage) {
  3. this.beverage = beverage;
  4. }
  5. @Override
  6. public double cost() {
  7. return 1 + beverage.cost();
  8. }
  9. }
  1. public class Mocha extends CondimentDecorator {
  2. public Mocha(Beverage beverage) {
  3. this.beverage = beverage;
  4. }
  5. @Override
  6. public double cost() {
  7. return 1 + beverage.cost();
  8. }
  9. }
  1. public class Client {
  2. public static void main(String[] args) {
  3. Beverage beverage = new HouseBlend();
  4. System.out.println(beverage.cost()); //3.0
  5. beverage = new Mocha(beverage);
  6. System.out.println(beverage.cost()); //4.0
  7. beverage = new Milk(beverage);
  8. //本来3元的饮料,加上Mocha、Milk后就变成了5元
  9. System.out.println(beverage.cost()); //5.0
  10. }
  11. }
  1. 3.0
  2. 4.0
  3. 5.0

设计原则

类应该对扩展开放,对修改关闭:也就是添加新功能时不需要修改代码。
饮料可以动态添加新的配料,而不需要去修改饮料的代码。

不可能把所有的类设计成都满足这一原则,应当把该原则应用于最有可能发生改变的地方。

JDK

  • java.io.BufferedInputStream(InputStream)
  • java.io.DataInputStream(InputStream)
  • java.io.BufferedOutputStream(OutputStream)
  • java.util.zip.ZipOutputStream(OutputStream)
  • java.util.Collections#checkedList|Map|Set|SortedSet|SortedMap

5. 外观(Facade)

Intent

提供了一个统一的接口,用来访问子系统中的一群接口,从而让子系统更容易使用。

Class Diagram

image.png

Implementation

观看电影需要操作很多电器,使用外观模式实现一键看电影功能。

  1. public class SubSystem {
  2. public void turnOnTV() {
  3. System.out.println("turnOnTV()");
  4. }
  5. public void setCD(String cd) {
  6. System.out.println("setCD( " + cd + " )");
  7. }
  8. public void starWatching(){
  9. System.out.println("starWatching()");
  10. }
  11. }
  1. public class Facade {
  2. private SubSystem subSystem = new SubSystem();
  3. public void watchMovie() {
  4. subSystem.turnOnTV();
  5. subSystem.setCD("a movie");
  6. subSystem.starWatching();
  7. }
  8. }
  1. public class Client {
  2. public static void main(String[] args) {
  3. Facade facade = new Facade();
  4. facade.watchMovie();
  5. }
  6. }

设计原则

最少知识原则:只和你的密友谈话。也就是说客户对象所需要交互的对象应当尽可能少。

6. 享元(Flyweight)

Intent

利用共享的方式来支持大量细粒度的对象,这些对象一部分内部状态是相同的。

Class Diagram

  • Flyweight:享元对象
  • IntrinsicState:内部状态,享元对象共享内部状态
  • ExtrinsicState:外部状态,每个享元对象的外部状态不同

image.png

Implementation

  1. public interface Flyweight {
  2. void doOperation(String extrinsicState);
  3. }
  1. public class ConcreteFlyweight implements Flyweight {
  2. private String intrinsicState;
  3. public ConcreteFlyweight(String intrinsicState) {
  4. this.intrinsicState = intrinsicState;
  5. }
  6. @Override
  7. public void doOperation(String extrinsicState) {
  8. System.out.println("Object address: " + System.identityHashCode(this));
  9. System.out.println("IntrinsicState: " + intrinsicState);
  10. System.out.println("ExtrinsicState: " + extrinsicState);
  11. }
  12. }
  1. public class FlyweightFactory {
  2. private HashMap<String, Flyweight> flyweights = new HashMap<>();
  3. Flyweight getFlyweight(String intrinsicState) {
  4. if (!flyweights.containsKey(intrinsicState)) {
  5. Flyweight flyweight = new ConcreteFlyweight(intrinsicState);
  6. flyweights.put(intrinsicState, flyweight);
  7. }
  8. return flyweights.get(intrinsicState);
  9. }
  10. }
  1. public class Client {
  2. public static void main(String[] args) {
  3. FlyweightFactory factory = new FlyweightFactory();
  4. Flyweight flyweight1 = factory.getFlyweight("aa");
  5. Flyweight flyweight2 = factory.getFlyweight("aa");
  6. flyweight1.doOperation("x");
  7. flyweight2.doOperation("y");
  8. }
  9. }
  1. Object address: 1163157884
  2. IntrinsicState: aa
  3. ExtrinsicState: x
  4. Object address: 1163157884
  5. IntrinsicState: aa
  6. ExtrinsicState: y

JDK

Java 利用缓存来加速大量小对象的访问时间。

  • java.lang.Integer#valueOf(int)
  • java.lang.Boolean#valueOf(boolean)
  • java.lang.Byte#valueOf(byte)
  • java.lang.Character#valueOf(char)

7. 代理(Proxy)

Intent

控制对其它对象的访问。

代理有以下四类:

  • 远程代理(Remote Proxy):控制对远程对象(不同地址空间)的访问,它负责将请求及其参数进行编码,并向不同地址空间中的对象发送已经编码的请求。
  • 虚拟代理(Virtual Proxy):根据需要创建开销很大的对象,它可以缓存实体的附加信息,以便延迟对它的访问,例如在网站加载一个很大图片时,不能马上完成,可以用虚拟代理缓存图片的大小信息,然后生成一张临时图片代替原始图片。
  • 保护代理(Protection Proxy):按权限控制对象的访问,它负责检查调用者是否具有实现一个请求所必须的访问权限。
  • 智能代理(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作:记录对象的引用次数;当第一次引用一个对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其它对象不能改变它。

Class Diagram

  • Proxy:代理对象,通常具有如下功能:
  1. 实现与具体的目标对象一样的接口,这样就可以使用代理来代替具体的目标对象
  2. 保存一个指向具体目标对象的引用,可以在需要的时候调用具体的目标对象,
    可以控制对具体目标对象的访问,并可能负责创建和删除它
  • Subject:目标接口,定义代理和具体目标对象的接口,这样就可以在任何使用具体目标对象的地方使用代理对象
  • RealSubject:具体的目标对象,真正实现目标接口要求的功能。

image.png

Implementation

以下是一个虚拟代理的实现,模拟了图片延迟加载的情况下使用与图片大小相等的临时内容去替换原始图片,直到图片加载完成才将图片显示出来。

  1. public interface Image {
  2. void showImage();
  3. }
  1. public class HighResolutionImage implements Image {
  2. private URL imageURL;
  3. private long startTime;
  4. private int height;
  5. private int width;
  6. public int getHeight() {
  7. return height;
  8. }
  9. public int getWidth() {
  10. return width;
  11. }
  12. public HighResolutionImage(URL imageURL) {
  13. this.imageURL = imageURL;
  14. this.startTime = System.currentTimeMillis();
  15. this.width = 600;
  16. this.height = 600;
  17. }
  18. public boolean isLoad() {
  19. // 模拟图片加载,延迟 3s 加载完成
  20. long endTime = System.currentTimeMillis();
  21. return endTime - startTime > 3000;
  22. }
  23. @Override
  24. public void showImage() {
  25. System.out.println("Real Image: " + imageURL);
  26. }
  27. }
  1. public class ImageProxy implements Image {
  2. private HighResolutionImage highResolutionImage;
  3. public ImageProxy(HighResolutionImage highResolutionImage) {
  4. this.highResolutionImage = highResolutionImage;
  5. }
  6. @Override
  7. public void showImage() {
  8. while (!highResolutionImage.isLoad()) {
  9. try {
  10. System.out.println("Temp Image: " + highResolutionImage.getWidth() + " " + highResolutionImage.getHeight());
  11. Thread.sleep(100);
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. highResolutionImage.showImage();
  17. }
  18. }
  1. public class ImageViewer {
  2. public static void main(String[] args) throws Exception {
  3. String image = "http://image.jpg";
  4. URL url = new URL(image);
  5. HighResolutionImage highResolutionImage = new HighResolutionImage(url);
  6. ImageProxy imageProxy = new ImageProxy(highResolutionImage);
  7. imageProxy.showImage();
  8. }
  9. }

JDK

  • java.lang.reflect.Proxy
  • RMI

Java中的代理模式

Java对代理模式提供了内建的支持,在java.lang.reflect包下面,
提供了一个Proxy的类和一个InvocationHandler的接口。

  • Java的静态代理
  1. /**
  2. * Subject接口
  3. */
  4. public interface IUserDao {
  5. void save();
  6. }
  1. /**
  2. * RealSubject
  3. */
  4. public class UserDao implements IUserDao{
  5. @Override
  6. public void save() {
  7. System.out.println("保存数据");
  8. }
  9. }
  1. /**
  2. * Proxy
  3. * 1. 实现与具体的目标对象一样的接口,这样就可以使用代理来代替具体的目标对象
  4. * 2.保存一个指向具体目标对象的引用**,可以在需要的时候调用具体的目标对象,
  5. * 可以控制对具体目标对象的访问,并可能负责创建和删除它
  6. */
  7. public class UserDaoProxy implements IUserDao{
  8. private UserDao target;
  9. public UserDaoProxy(UserDao userDao){
  10. this.target=userDao;
  11. }
  12. @Override
  13. public void save() {
  14. System.out.println("==保存之前增强==");
  15. target.save();
  16. System.out.println("==保存之后增强==");
  17. }
  18. }
  1. public class Client {
  2. public static void main(String[] args) {
  3. UserDao userDao=new UserDao();
  4. userDao.save();
  5. System.out.println("。。。使用代理后。。。");
  6. UserDaoProxy proxy=new UserDaoProxy(userDao);
  7. proxy.save();
  8. }
  9. }

输出结果:

  1. 保存数据
  2. 。。。使用代理后。。。
  3. ==保存之前增强==
  4. 保存数据
  5. ==保存之后增强==

Java的静态代理有一个较大的缺点,就是如果Subject接口发生变化,那么代理类和具体的目标实现都要变化,不是很灵活。

  • Java的动态代理
  1. /**
  2. * Subject接口
  3. */
  4. public interface IUserDao {
  5. void save();
  6. void update();
  7. }
  1. /**
  2. * RealSubject
  3. */
  4. public class UserDao implements IUserDao {
  5. @Override
  6. public void save() {
  7. System.out.println("保存数据");
  8. }
  9. @Override
  10. public void update() {
  11. System.out.println("更新数据");
  12. }
  13. }
  1. public class PrxoyFactory {
  2. private Object target;
  3. public PrxoyFactory(Object target){
  4. this.target=target;
  5. }
  6. public Object getInstance(){
  7. return Proxy.newProxyInstance(
  8. target.getClass().getClassLoader(),
  9. target.getClass().getInterfaces(),
  10. new InvocationHandler() {
  11. //proxy - 在其上调用方法的代理实例
  12. //method - 对应于在代理实例上调用的接口方法的 Method 实例。
  13. //args - 包含传入代理实例上方法调用的参数值的对象数组,
  14. //如果接口方法不使用参数,则为 null。
  15. //基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。
  16. @Override
  17. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  18. Object obj=null;
  19. String methodName=method.getName();
  20. //只对save方法增强
  21. if("save".equals(methodName)){
  22. System.out.println("提交事务");
  23. //target 就是目标对象,arge就是方法的参数
  24. obj=method.invoke(target,args);
  25. System.out.println("结束事务");
  26. }else{
  27. obj=method.invoke(target,args);
  28. }
  29. return obj;
  30. }
  31. }
  32. );
  33. }
  34. }
  1. public class Client {
  2. public static void main(String[] args) {
  3. IUserDao userDao=new UserDao();
  4. userDao.save();
  5. userDao.update();
  6. System.out.println("。。。使用代理后。。。");
  7. PrxoyFactory factory=new PrxoyFactory(userDao);
  8. //Java的动态代理目前只能代理接口
  9. IUserDao proxy=(IUserDao)factory.getInstance();
  10. proxy.save();
  11. proxy.update();
  12. }
  13. }

输出结果:

  1. 保存数据
  2. 更新数据
  3. 。。。使用代理后。。。
  4. 提交事务
  5. 保存数据
  6. 结束事务
  7. 更新数据

动态代理跟静态代理相比,明显的变化是:

静态代理实现的时候,在Subject接口上定义很多的方法,代理类里面自然也要实现很多方法;

动态代理实现的时候,虽然Subject接口上定义了很多方法,但是动态代理类始终只有一个invoke方法。

这样当Subject接口发生变化的时候,动态代理的接口就不需要跟着变化了。

Java的动态代理目前只能代理接口,基本的实现是依靠Java的反射机制动态生成class的技术,
来动态生成被代理的接口的实现对象。

  • Java的Cglib代理

需要引入第三方 jar包,这里引入的是

  1. spring-core-3.2.0.RELEASE.jar
  1. /**
  2. * Subject接口
  3. */
  4. public interface IUserDao {
  5. void save();
  6. void update();
  7. }
  1. public class UserDao implements IUserDao {
  2. @Override
  3. public void save() {
  4. System.out.println("保存数据");
  5. }
  6. @Override
  7. public void update(){
  8. System.out.println("更新数据");
  9. }
  10. }
  1. /**
  2. * Cglib代理工厂,
  3. * 实现了MthodInterceptor
  4. */
  5. public class CglibProxyFactory implements MethodInterceptor{
  6. private Object target;
  7. public CglibProxyFactory(Object target){
  8. this.target=target;
  9. }
  10. public Object getInstance(){
  11. //1.工具类
  12. Enhancer enchancer=new Enhancer();
  13. //2.设置父类
  14. enchancer.setSuperclass(target.getClass());
  15. //3.设置回调函数
  16. enchancer.setCallback(this);
  17. //4.创建子类(代理对象)
  18. return enchancer.create();
  19. }
  20. @Override
  21. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  22. Object returnObj=null;
  23. if("save".equals(method.getName())){
  24. System.out.println("开启事务");
  25. returnObj=method.invoke(target,objects);
  26. System.out.println("关闭事务");
  27. }else{
  28. returnObj=method.invoke(target,objects);
  29. }
  30. return returnObj;
  31. }
  32. }
  1. public class Client {
  2. public static void main(String[] args) {
  3. IUserDao userDao=new UserDao();
  4. userDao.save();
  5. userDao.update();
  6. System.out.println("。。。使用代理后。。。");
  7. CglibProxyFactory factory=new CglibProxyFactory(userDao);
  8. //Cglib即可以代理接口,也可以代理对象
  9. IUserDao proxy=(IUserDao)factory.getInstance();
  10. //UserDao proxy=(UserDao)factory.getInstance();
  11. proxy.save();
  12. proxy.update();
  13. }
  14. }