结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。
由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性

一.代理模式

由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时动态生成。动态代理又有JDK代理和CGLib代理两种。
结构
抽象主题(Subject)类: 通过接口或抽象类声明真实主题和代理对象实现的业务方法。
真实主题(Real Subject)类: 实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
代理(Proxy)类 : 提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

1.静态代理

image.png

  1. //买票接口
  2. public interface SellTickets {
  3. //买票功能
  4. public void sell();
  5. }
  1. //火车站
  2. public class TrainStation implements SellTickets{
  3. @Override
  4. public void sell() {
  5. System.out.println("买火车票");
  6. }
  7. }
  1. //买票代理点
  2. public class ProxyPoint implements SellTickets{
  3. private TrainStation trainStation=new TrainStation();
  4. @Override
  5. public void sell() {
  6. System.out.println("代理点收取代理费用");
  7. trainStation.sell();
  8. }
  9. }

直接访问的是ProxyPoint类对象,也就是说ProxyPoint作为访问对象 和目标对象的中介。同时也对sell方法进行了增强(代理点收取一些服务费用)。

2.动态代理

JDK动态代理
Java中提供了一个动态代理类 Proxy,Proxy并不是我们上述所说的代理对象的类,而是提供了一个创建代理对象的静态方法(newProxyInstance方法)来获取代理对象

  1. public class ProxyFactory {
  2. SellTickets station =new TrainStation();
  3. public SellTickets getSellTickets(){
  4. /* newProxyInstance()方法参数说明:
  5. ClassLoader loader : 类加载器,用于加载代理类,使用真实对象的类加载 器即可
  6. Class<?>[] interfaces : 真实对象所实现的接口,代理模式真实对象和代 理对象实现相同的接口
  7. InvocationHandler h : 代理对象的调用处理程序 */
  8. SellTickets st=(SellTickets) Proxy.newProxyInstance(
  9. station.getClass().getClassLoader(),
  10. station.getClass().getInterfaces(),
  11. new InvocationHandler() {
  12. @Override
  13. /* InvocationHandler中invoke方法参数说明:
  14. proxy : 代理对象
  15. method : 对应于在代理对象上调用的接口方法的 Method 实 例
  16. args : 代理对象调用接口方法时传递的实际参数 */
  17. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  18. System.out.println("代理买票了");
  19. Object invoke = method.invoke(station, args);
  20. return invoke;
  21. }
  22. }
  23. );
  24. return st;
  25. }
  26. }

CGLIB动态代理
如果没有定义SellTickets接口,只定义了TrainStation(火车站类)。很显然JDK代理是无法使用了,因为JDK动态代理要求必须定义接口,对接口进行代理。
CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。
导包

  1. <dependency>
  2. <groupId>cglib</groupId>
  3. <artifactId>cglib</artifactId>
  4. <version>3.1</version>
  5. </dependency>
  1. public class ProxyFactory implements MethodInterceptor {
  2. private TrainStation target = new TrainStation();
  3. public TrainStation getProxyObject() {
  4. //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
  5. Enhancer enhancer = new Enhancer();
  6. // 设置父类的字节码对象
  7. enhancer.setSuperclass(target.getClass());
  8. //设置回调函数
  9. enhancer.setCallback(this);
  10. //创建代理对象
  11. TrainStation obj = (TrainStation) enhancer.create();
  12. return obj;
  13. }
  14. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  15. System.out.println("代理买票了");
  16. Object invoke = method.invoke(target, objects);
  17. return invoke;
  18. }
  19. }

二.适配器模式

将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
适配器模式分为类适配器模式和对象适配器模式,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些
结构
适配器模式(Adapter)包含以下主要角色:
目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。

1.类适配器模式

定义一个适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件。
image.png

  1. //SDcard 接口
  2. public interface SDcard {
  3. public String read();
  4. public void write();
  5. }
  6. //SDcard 实现类
  7. public class SDcardImpl implements SDcard{
  8. public String read() {
  9. return "SD read";
  10. }
  11. public void write() {
  12. System.out.println("SD write");
  13. }
  14. }
  15. //适配者类的接口
  16. public interface TFcard {
  17. public String read();
  18. public void write();
  19. }
  20. //TFcard 的实现类
  21. public class TFcardImpl implements TFcard{
  22. public String read() {
  23. return "read";
  24. }
  25. public void write() {
  26. System.out.println("write");
  27. }
  28. }

适配器

  1. public class SDAdapterTF extends TFcardImpl implements SDcard{
  2. @Override
  3. public String read() {
  4. System.out.println("tf");
  5. return super.read();
  6. }
  7. @Override
  8. public void write() {
  9. super.write();
  10. }
  11. }

2.对象适配器模式

对象适配器模式可釆用将现有组件库中已经实现的组件引入适配器类中,该类同时实现当前系统的业务接口
适配器类

  1. public class SDAdapterTF implements SDcard{
  2. private TFcard tfcard;
  3. public SDAdapterTF(TFcard tfcard){
  4. this.tfcard=tfcard;
  5. }
  6. public String read() {
  7. System.out.println("tf");
  8. return tfcard.read();
  9. }
  10. public void write() {
  11. tfcard.write();
  12. }
  13. }

三.装饰着模式

指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。
结构
装饰(Decorator)模式中的角色:
抽象构件(Component)角色 :定义一个抽象接口以规范准备接收附加责任的对象。
具体构件(Concrete Component)角色 :实现抽象构件,通过装饰角色为其添加一些职责。抽象装饰(Decorator)角色 : 继承或实现抽象构件,并包含具体构件的实例,可以通过其子 类扩展具体构件的功能。
具体装饰(ConcreteDecorator)角色 :实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
image.png

  1. //快餐接口
  2. abstract class FastFood {
  3. private float price;
  4. private String desc;
  5. public FastFood() { }
  6. public FastFood(float price, String desc) {
  7. this.price = price;
  8. this.desc = desc;
  9. }
  10. public void setPrice(float price) {
  11. this.price = price;
  12. }
  13. public float getPrice() {
  14. return price;
  15. }
  16. public String getDesc() {
  17. return desc;
  18. }
  19. public void setDesc(String desc) {
  20. this.desc = desc;
  21. }
  22. //获取价格
  23. public abstract float cost();
  24. }
  25. //炒饭
  26. class FriedRice extends FastFood {
  27. public FriedRice() {
  28. super(10, "炒饭");
  29. }
  30. public float cost() {
  31. return getPrice();
  32. }
  33. }
  34. //配料类
  35. abstract class Garnish extends FastFood {
  36. private FastFood fastFood;
  37. public FastFood getFastFood() {
  38. return fastFood;
  39. }
  40. public void setFastFood(FastFood fastFood) {
  41. this.fastFood = fastFood;
  42. }
  43. public Garnish(FastFood fastFood, float price, String desc) {
  44. super(price,desc);
  45. this.fastFood = fastFood;
  46. }
  47. }
  48. //培根配料
  49. class Bacon extends Garnish {
  50. public Bacon(FastFood fastFood) {
  51. super(fastFood,2,"培根");
  52. }
  53. @Override
  54. public float cost() {
  55. return getPrice() + getFastFood().getPrice();
  56. }
  57. @Override
  58. public String getDesc() {
  59. return super.getDesc() + getFastFood().getDesc();
  60. }
  61. }

四.桥接模式

将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
结构
桥接(Bridge)模式包含以下主要角色:
抽象化(Abstraction)角色 :定义抽象类,并包含一个对实现化对象的引用。扩展抽象化(Refined Abstraction)角色 :是抽象化角色的子类,实现父类中的业务方法, 并通过组合关系调用实现化角色中的业务方法。
实现化(Implementor)角色 :定义实现化角色的接口,供扩展抽象化角色调用。
具体实现化(Concrete Implementor)角色 :给出实现化角色接口的具体实现。
image.png

  1. //视频文件
  2. interface VideoFile {
  3. void decode(String fileName);
  4. }
  5. //avi文件
  6. class AVIFile implements VideoFile {
  7. public void decode(String fileName) {
  8. System.out.println("avi视频文件:"+ fileName);
  9. }
  10. }
  11. //rmvb文件
  12. class REVBBFile implements VideoFile {
  13. public void decode(String fileName) {
  14. System.out.println("rmvb文件:" + fileName);
  15. }
  16. }
  1. //操作系统版本
  2. abstract class OperatingSystem {
  3. protected VideoFile videoFile;
  4. public OperatingSystem(VideoFile videoFile) {
  5. this.videoFile = videoFile;
  6. }
  7. public abstract void play(String fileName);
  8. }
  9. //Windows版本
  10. class Windows extends OperatingSystem {
  11. public Windows(VideoFile videoFile) {
  12. super(videoFile);
  13. }
  14. public void play(String fileName) {
  15. videoFile.decode(fileName);
  16. }
  17. }

五.外观模式

又名门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体的细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。
结构
外观(Facade)模式包含以下主要角色:
外观(Facade)角色:为多个子系统对外提供一个共同的接口。
子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。
image.png

  1. public class TV {
  2. public void on(){
  3. System.out.println("开电视剧");
  4. }
  5. public void off(){
  6. System.out.println("关电视剧");
  7. }
  8. }
  9. public class Light {
  10. public void on(){
  11. System.out.println("开灯");
  12. }
  13. public void off(){
  14. System.out.println("关灯");
  15. }
  16. }
  1. public class SmartApplicationFacade {
  2. private Light light;
  3. private TV tv;
  4. public SmartApplicationFacade(Light light, TV tv) {
  5. this.light = light;
  6. this.tv = tv;
  7. }
  8. public void say(String message){
  9. if (message.contains("on")){
  10. on();
  11. }else if(message.contains("off")){
  12. off();
  13. }else {
  14. System.out.println("???????");
  15. }
  16. }
  17. private void off() {
  18. light.off();
  19. tv.off();
  20. }
  21. private void on() {
  22. light.on();
  23. tv.on();
  24. }
  25. }

六.组合模式

又名部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
结构
组合模式主要包含三种角色:
抽象根节点(Component):定义系统各层次对象的共有方法和属性,可以预先定义一些默认行为和属性。
树枝节点(Composite):定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构。
叶子节点(Leaf):叶子节点对象,其下再无分支,是系统层次遍历的最小单位。
image.png

  1. //菜单组件 不管是菜单还是菜单项,都应该继承该类
  2. abstract class MenuComponent {
  3. protected String name;
  4. protected int level;
  5. //添加菜单
  6. public void add(MenuComponent menuComponent){
  7. throw new UnsupportedOperationException();
  8. }
  9. //移除菜单
  10. public void remove(MenuComponent menuComponent){
  11. throw new UnsupportedOperationException();
  12. }
  13. //获取指定的子菜单
  14. public MenuComponent getChild(int i){
  15. throw new UnsupportedOperationException();
  16. }
  17. //获取菜单名称
  18. public String getName(){
  19. return name;
  20. }
  21. public void print(){
  22. throw new UnsupportedOperationException();
  23. }
  24. }

这里的MenuComponent定义为抽象类,因为有一些共有的属性和行为要在该类中实现,Menu和 MenuItem类就可以只覆盖自己感兴趣的方法,而不用搭理不需要或者不感兴趣的方法,举例来说,Menu类可以包含子菜单,因此需要覆盖add()、remove()、getChild()方法,但是MenuItem就不应该有这些方法。这里给出的默认实现是抛出异常,你也可以根据自己的需要改写默认实现。

  1. class Menu extends MenuComponent {
  2. private List<MenuComponent> menuComponentList;
  3. public Menu(String name, int level) {
  4. this.level = level;
  5. this.name = name;
  6. menuComponentList = new ArrayList<MenuComponent>();
  7. }
  8. @Override
  9. public void add(MenuComponent menuComponent) {
  10. menuComponentList.add(menuComponent);
  11. }
  12. @Override
  13. public void remove(MenuComponent menuComponent) {
  14. menuComponentList.remove(menuComponent);
  15. }
  16. @Override
  17. public MenuComponent getChild(int i) {
  18. return menuComponentList.get(i);
  19. }
  20. @Override
  21. public void print() {
  22. for (int i = 1; i < level; i++) {
  23. System.out.print("--");
  24. }
  25. System.out.println(name);
  26. for (MenuComponent menuComponent : menuComponentList) {
  27. menuComponent.print();
  28. }
  29. }
  30. }

Menu类已经实现了除了getName方法的其他所有方法,因为Menu类具有添加菜单,移除菜单和获取子菜单的功能。

  1. class MenuItem extends MenuComponent {
  2. public MenuItem(String name,int level) {
  3. this.name = name;
  4. this.level = level;
  5. }
  6. @Override public void print() {
  7. for (int i = 1; i < level; i++) {
  8. System.out.print("--");
  9. }
  10. System.out.println(name);
  11. }
  12. }

MenuItem是菜单项,不能再有子菜单,所以添加菜单,移除菜单和获取子菜单的功能并不能实现。

七.享元模式

运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似对象的开销,从而提高系统资源的利用率。
结构
享元(Flyweight )模式中存在以下两种状态:
1. 内部状态,即不会随着环境的改变而改变的可共享部分。
2. 外部状态,指随环境改变而改变的不可以共享的部分。享元模式的实现要领就是区分应用中的这两种状态,并将外部状态外部化。
享元模式的主要有以下角色:
抽象享元角色(Flyweight):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公 共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
具体享元(Concrete Flyweight)角色 :它实现了抽象享元类,称为享元对象;在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
非享元(Unsharable Flyweight)角色 :并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
享元工厂(Flyweight Factory)角色 :负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
image.png
提供了一个工厂类(BoxFactory),用来管理享元对象(也就是AbstractBox子类对象),该工厂类对象只需要一个,所以可以使用单例模式。并给工厂类提供一个获取形状的方法。