结构型模式
描述如何将类或者对象按某种布局组成更大的结构。它分为结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者通过组合或聚合来组合对象。
分为7种:代理模式、适配器模式、装饰者模式、桥接模式、外观模式、组合模式、享元模式。
代理模式
由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象访问对象和目标对象之间的中介。
Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时候动态生成。动态代理又有JDK代理和CGLIb代理两种。
静态代理
卖火车票的接口
java">public interface SellTickets {void sell();
}
火车站类
java">public class TrainStation implements SellTickets {public void sell() {System.out.println("火车站卖票");}
}
代售点类:代理火车站,进行卖票操作,同时对该方法做了点增强(收取费用)
java">public class ProxyPoint implements SellTickets {// 火车站类对象private TrainStation trainStation = new TrainStation();public void sell() {System.out.println("代售点收取一些服务费用");trainStation.sell();}
}
测试类
java">public class Client {public static void main(String[] args) {// 创建代售点类对象ProxyPoint proxyPoint = new ProxyPoint();// 调用方法进行卖票proxyPoint.sell();}
}
动态代理
JDK动态代理
Java 中提供了一个动态代理类 Proxy,Proxy 并不是我们上述所说的代理对象的类,而是提供了一个创建代理对象的静态方法(newProxyInstance 方法)来获取代理对象。
java">public interface SellTickets {void sell();
}
public class TrainStation implements SellTickets {public void sell() {System.out.println("火车站卖票");}
}
获取代理对象的工厂类:
java">public class ProxyFactory {// 目标对象private TrainStation station = new TrainStation();// 获取代理对象public SellTickets getProxyObject() {// 返回代理对象/*ClassLoader loader: 类加载器,用于加载代理类,可以通过目标对象获取类加载器Class<?>[] interfaces: 代理类实现的接口的字节码对象InvocationHandler h: 代理对象调用处理程序*/SellTickets proxyObject = (SellTickets) Proxy.newProxyInstance(station.getClass().getClassLoader(), // 类加载器,用于加载代理类station.getClass().getInterfaces(), // 代理类实现的接口的字节码对象new InvocationHandler() {/*** @param proxy 代理对象, 和proxyObject是同一个对象,在invoke方法中基本不用* @param method 对接口中的方法进行封装的method对象* @param args 调用方法的实际参数* @return 方法的返回值*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("代售点收取一定的服务费用(jdk动态代理 )");// 执行目标对象的方法Object obj = method.invoke(station, args);return obj;}});return proxyObject;}
}
测试类
java">public class Client {public static void main(String[] args) {// 获取代理对象// 1, 创建代理工厂对象ProxyFactory factory = new ProxyFactory();// 2, 使用factory对象的方法获取代理对象SellTickets proxyObject = factory.getProxyObject();// 3, 调用代理对象的方法proxyObject.sell();}
}
CGLIB 动态代理
上面的案例中,如果没有定义 SellTickets 接口,只定义了 TrainStation 火车站类。则 JDK 代理无法使用,因为 JDK 动态代理要求必须定义接口,它只能对接口进行代理。
CGLIB 是一个功能强大,高性能的代码生成包,它可以为没有实现接口的类提供代理,为 JDK 的动态代理提供了很好的补充。CGLIB 是第三方提供的包,所以需要引入依赖:
java"><dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>2.2.2</version>
</dependency>
代码如下:CGLIB 动态代理可以不代理接口,直接代理类。
火车站类:火车票具有卖票功能,实现 SellTickets 接口,同静态代理。
java">public class TrainStation implements SellTickets {public void sell() {System.out.println("火车站卖票");}
}
代理工厂类:用来获取代理对象
java">/*** 代理对象工厂, 用来获取代理对象*/
public class ProxyFactory implements MethodInterceptor {// 火车站对象private TrainStation station = new TrainStation();// 获取代理对象public TrainStation getProxyObject() {// 创建Enhancer对象,类似于JDK代理中的Proxy类Enhancer enhancer = new Enhancer();// 设置父类的字节码对象(指定父类)enhancer.setSuperclass(TrainStation.class);// 设置回调函数enhancer.setCallback(this);// 创建代理对象TrainStation proxyObject = (TrainStation) enhancer.create();return proxyObject;}/**** @param o 代理对象* @param method 真实对象中的方法的Method实例* @param objects 实际参数* @param methodProxy 代理对象中的方法的method实例* @return 方法执行结果*/@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("代售点收取一定的服务费用(Cglib代理)");// 要调用目标对象的方法Object invoke = method.invoke(station, objects);return invoke;}
}
适配器模式
将一个类的接口转换为客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
适配器模式分为:
类结构型模式:采用继承机制来组织接口和类。
对象结构型模式:釆用组合或聚合来组合对象。
前者由于类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。
类适配器模式
定义一个适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件。
Computer:可以直接访问 SDCard,但是不能直接访问 TFCard,即将通过适配器去访问 TFCard
java">public class Computer {// 从SD卡中读取数据public String readSD(SDCard sdCard) {if (sdCard == null) {throw new NullPointerException("sd card can not be null");}return sdCard.readSD();}
}
目标接口:SDCard 是 Computer 期望访问的接口
java">/*** SD卡的接口*/
public interface SDCard {// 从SD卡中读取数据String readSD();// 往SD卡中写数据void writeSD(String msg);
}/*** SD卡的实现类*/
public class SDCardImpl implements SDCard {public String readSD() {String msg = "sd card read a msg :hello word SD";return msg;}public void writeSD(String msg) {System.out.println("sd card write msg : " + msg);}
}
适配者类:TFCard 是要被适配的接口,适配成可以通过 SDCard 来访问
java">/*** TF卡的接口*/
public interface TFCard {// 从TF卡中读取数据String readTF();// 往TF卡中写数据void writeTF(String msg);
}/*** TF卡的实现类*/
public class TFCardImpl implements TFCard {public String readTF() {String msg = "TFCard read msg: hello word TFCard";return msg;}public void writeTF(String msg) {System.out.println("TFCard write msg: " + msg);}
}
适配器类:实现业务接口,继承已有组件
本质上还是要执行 TFCard 的功能,因此 extends TFCardImpl
但是又可以让 Computer 通过 SDCard 接口来访问,因此 implements SDCard
java">public class SDAdapterTF extends TFCardImpl implements SDCard {public String readSD() {System.out.println("adapter read tf card");return readTF();}public void writeSD(String msg) {System.out.println("adapter write tf card");writeTF(msg);}
}
测试类:
java">public class Client {public static void main(String[] args) {// 创建计算机对象Computer computer = new Computer();// 电脑读取SD卡中的数据(可直接读)String msg = computer.readSD(new SDCardImpl());System.out.println(msg);System.out.println("===============");// 使用电脑读取TF卡中的数据(无法直接读)// 定义适配器类String msg1 = computer.readSD(new SDAdapterTF());System.out.println(msg1);}
}
类适配器模式违背了合成复用原则,类适配器是客户类有一个接口规范的情况下可用,反之不可用。
对象适配器模式
实现方式:对象适配器模式可釆用将现有组件库中已经实现的组件引入适配器类中,该类同时实现当前系统的业务接口。
代码如下:类适配器模式的代码,我们只需要修改适配器类(SDAdapterTF)和测试类
适配器类:通过聚合一个适配者类,来使用它的方法
java">public class SDAdapterTF implements SDCard {// 声明适配者类private TFCard tfCard;public SDAdapterTF(TFCard tfCard) {this.tfCard = tfCard;}public String readSD() {System.out.println("adapter read tf card");return tfCard.readTF();}public void writeSD(String msg) {System.out.println("adapter write tf card");tfCard.writeTF(msg);}
}
测试类:
java">public class Client {public static void main(String[] args) {// 创建计算机对象Computer computer = new Computer();// 电脑读取SD卡中的数据(可直接读)String msg = computer.readSD(new SDCardImpl());System.out.println(msg);System.out.println("===============");// 使用电脑读取TF卡中的数据(无法直接读)// 创建适配器类对象SDAdapterTF sdAdapterTF = new SDAdapterTF(new TFCardImpl());String msg1 = computer.readSD(sdAdapterTF);System.out.println(msg1);}
}
接口适配器模式
当不希望实现一个接口中所有的方法时,可以创建一个抽象类 Adapter 实现接口所有方法,然后只需要继承该抽象类即可。
装饰者模式
定义:指在不改变现有对象结构的情况下,动态的给该对象增加一些职责(即增加其额外功能)的模式。
抽象构件角色:快餐类
java">@Data
public abstract class FastFood {private float price; // 价格private String desc; // 描述public FastFood(float price, String desc) {this.price = price;this.desc = desc;}public abstract float cost();
}
具体构件角色:炒饭类、炒面类
java">public class FriedRice extends FastFood {public FriedRice() {super(10, "炒饭");}public float cost() {return getPrice();}
}public class FriedNoodles extends FastFood {public FriedNoodles() {super(12, "炒面");}public float cost() {return getPrice();}
}
抽象装饰者角色:配料类
java">@Data
public abstract class Garnish extends FastFood {// 声明快餐类的变量private FastFood fastFood;public Garnish(FastFood fastFood, float price, String desc) {super(price, desc);this.fastFood = fastFood;}
}
具体装饰者角色:鸡蛋配料类、培根配料类
java">public class Egg extends Garnish {public Egg(FastFood fastFood) {super(fastFood, 1, "鸡蛋");}// 计算价格public float cost() {return getPrice() + getFastFood().cost();}@Overridepublic String getDesc() {return super.getDesc() + getFastFood().getDesc();}
}public class Bacon extends Garnish {public Bacon(FastFood fastFood) {super(fastFood, 2, "培根");}// 计算价格public float cost() {return getPrice() + getFastFood().cost();}@Overridepublic String getDesc() {return super.getDesc() + getFastFood().getDesc();}
}
测试类:
java">public class Client {public static void main(String[] args) {// 点一份炒饭FastFood food = new FriedRice();System.out.println(food.getDesc() + " " + food.cost() + "元");// 在上面的炒饭中加一个鸡蛋food = new Egg(food);System.out.println(food.getDesc() + " " + food.cost() + "元");// 再加一个鸡蛋food = new Egg(food);System.out.println(food.getDesc() + " " + food.cost() + "元");// 再加一个培根food = new Bacon(food);System.out.println(food.getDesc() + " " + food.cost() + "元");}
}
BufferedWriter 使用装饰者模式对 Writer 子实现类进行了增强,添加了缓冲区,提高了写数据的效率。
桥接模式
定义:将抽象和实现分离,使它们可以独立变化。它是用组合关系来代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
实现化角色:视频文件格式接口
java">public interface VideoFile {// 解码功能void decode(String fileName);
}
具体实现化角色:具体的视频文件类,avi、rmvb 等
java">public class AviFile implements VideoFile {public void decode(String fileName) {System.out.println("avi视频文件 :" + fileName);}
}public class RmvbFile implements VideoFile {public void decode(String fileName) {System.out.println("rmvb视频文件 :" + fileName);}
}
抽象化角色:操作系统抽象类
java">public abstract class OperatingSystem {// 声明videFile变量protected VideoFile videoFile;public OperatingSystem(VideoFile videoFile) {this.videoFile = videoFile;}public abstract void play(String fileName);
}
拓展抽象化角色:Windows、Mac 操作系统
java">public class Windows extends OperatingSystem {public Windows(VideoFile videoFile) {super(videoFile);}public void play(String fileName) {videoFile.decode(fileName);}
}public class Mac extends OperatingSystem {public Mac(VideoFile videoFile) {super(videoFile);}public void play(String fileName) {videoFile.decode(fileName);}
}
测试类:
java">public class Client {public static void main(String[] args) {// 创建Mac系统对象OperatingSystem system = new Mac(new AviFile());// 使用操作系统播放视频文件system.play("战狼3");}
}
外观模式
子系统角色:电灯、电视、空调
java">public class Light {public void on() {System.out.println("打开电灯。。。。");}public void off() {System.out.println("关闭电灯。。。。");}
}public class TV {public void on() {System.out.println("打开电视机。。。。");}public void off() {System.out.println("关闭电视机。。。。");}
}public class AirCondition {public void on() {System.out.println("打开空调。。。。");}public void off() {System.out.println("关闭空调。。。。");}
}
外观角色:智能音箱,用于控制各个子系统,用户主要和该对象进行交互
java">public class SmartAppliancesFacade {// 聚合电灯对象,电视机对象,空调对象private Light light;private TV tv;private AirCondition airCondition;public SmartAppliancesFacade() {light = new Light();tv = new TV();airCondition = new AirCondition();}// 通过语言控制public void say(String message) {if (message.contains("打开")) {on();} else if (message.contains("关闭")) {off();} else {System.out.println("我还听不懂你说的!!!");}}// 一键打开功能private void on() {light.on();tv.on();airCondition.on();}// 一键关闭功能private void off() {light.off();tv.off();airCondition.off();}
}
测试类:
java">public class Client {public static void main(String[] args) {// 创建智能音箱对象SmartAppliancesFacade facade = new SmartAppliancesFacade();// 控制家电facade.say("打开家电");facade.say("关闭家电");}
}
组合模式
不管是菜单还是菜单项,都应该继承自统一的接口,这里将这个统一的接口称为菜单组件。
java">/*** 菜单组件:属于抽象根节点*/
public abstract class MenuComponent {// 菜单组件的名称protected String name;// 菜单组件的层级protected int level;// 添加子菜单public void add(MenuComponent menuComponent) {throw new UnsupportedOperationException();}// 移除子菜单public void remove(MenuComponent menuComponent) {throw new UnsupportedOperationException();}// 获取指定的子菜单public MenuComponent getChild(int index) {throw new UnsupportedOperationException();}// 获取菜单或者菜单项的名称public String getName() {return name;}// 打印菜单名称的方法(包含子菜单和字菜单项)public abstract void print();
}
Menu 类实现了除了 getName 方法的其他所有方法,因为 Menu 类具有添加菜单,移除菜单和获取子菜单的功能。
java">/*** 菜单类:属于树枝节点*/
public class Menu extends MenuComponent {// 菜单可以有多个子菜单或者子菜单项private List<MenuComponent> menuComponentList = new ArrayList<>();public Menu(String name, int level) {this.name = name;this.level = level;}@Overridepublic void add(MenuComponent menuComponent) {menuComponentList.add(menuComponent);}@Overridepublic void remove(MenuComponent menuComponent) {menuComponentList.remove(menuComponent);}@Overridepublic MenuComponent getChild(int index) {return menuComponentList.get(index);}@Overridepublic void print() {// 打印菜单名称for (int i = 0; i < level; i++) {System.out.print("--");}System.out.println(name);// 打印子菜单或者子菜单项名称for (MenuComponent component : menuComponentList) {component.print();}}
}
MenuItem 是菜单项,不能再有子菜单,所以添加菜单,移除菜单和获取子菜单的功能并不能实现。
java">/*** 菜单项类:属于叶子节点*/
public class MenuItem extends MenuComponent {public MenuItem(String name, int level) {this.name = name;this.level = level;}public void print() {// 打印菜单项的名称for (int i = 0; i < level; i++) {System.out.print("--");}System.out.println(name);}
}
测试类
java">public class Client {public static void main(String[] args) {// 创建二级菜单树MenuComponent menu1 = new Menu("菜单管理", 2);menu1.add(new MenuItem("页面访问", 3));menu1.add(new MenuItem("展开菜单", 3));menu1.add(new MenuItem("编辑菜单", 3));menu1.add(new MenuItem("删除菜单", 3));menu1.add(new MenuItem("新增菜单", 3));MenuComponent menu2 = new Menu("权限管理", 2);menu2.add(new MenuItem("页面访问", 3));menu2.add(new MenuItem("提交保存", 3));MenuComponent menu3 = new Menu("角色管理", 2);menu3.add(new MenuItem("页面访问", 3));menu3.add(new MenuItem("新增角色", 3));menu3.add(new MenuItem("修改角色", 3));// 创建一级菜单MenuComponent component = new Menu("系统管理", 1);// 将二级菜单添加到一级菜单中component.add(menu1);component.add(menu2);component.add(menu3);// 打印菜单名称(如果有子菜单一块打印)component.print();}
}
享元模式
运用共享技术来有效地支持大量细粒度对象的复用,通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似对象的开销,从而提高系统资源的利用率。
俄罗斯方块有不同的形状,可以对这些形状向上抽取出 AbstractBox,用来定义共性的属性和行为。
java">/*** 抽象享元角色*/
public abstract class AbstractBox {// 获取图形的方法public abstract String getShape();// 显示图形及颜色public void display(String color) {System.out.println("方块形状:" + getShape() + ", 颜色:" + color);}
}
IBox 类、LBox 类、OBox 类等。
java">/*** 具体享元角色*/
public class IBox extends AbstractBox {public String getShape() {return "I";}
}
public class LBox extends AbstractBox {public String getShape() {return "L";}
}
public class OBox extends AbstractBox {public String getShape() {return "O";}
}
提供一个工厂类 BoxFactory,用来管理享元对象(也就是 AbstractBox 子类对象),该工厂类对象只需要一个,所以可以使用单例模式,并给工厂类提供一个获取形状的方法。
java">public class BoxFactory {private static BoxFactory factory = new BoxFactory();private HashMap<String, AbstractBox> map;// 在构造方法中进行初始化操作private BoxFactory() {map = new HashMap<>();map.put("I", new IBox());map.put("L", new LBox());map.put("O", new OBox());}// 提供一个方法获取该工厂类对象public static BoxFactory getInstance() {return factory;}// 根据名称获取图形对象public AbstractBox getShape(String name) {return map.get(name);}
}
测试类:
java">public class Client {public static void main(String[] args) {// 获取I图形对象AbstractBox box1 = BoxFactory.getInstance().getShape("I");box1.display("灰色");// 获取L图形对象AbstractBox box2 = BoxFactory.getInstance().getShape("L");box2.display("绿色");// 获取O图形对象AbstractBox box3 = BoxFactory.getInstance().getShape("O");box3.display("灰色");// 获取O图形对象AbstractBox box4 = BoxFactory.getInstance().getShape("O");box4.display("红色");System.out.println("两次获取到的O图形对象是否是同一个对象:" + (box3 == box4));}
}
valueof()方法:
java">public final class Integer extends Number implements Comparable<Integer> {public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);}private static class IntegerCache {static final int low = -128;static final int high;static final Integer cache[];static {int h = 127;String integerCacheHighPropValue =sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");if (integerCacheHighPropValue != null) {try {int i = parseInt(integerCacheHighPropValue);i = Math.max(i, 127);// Maximum array size is Integer.MAX_VALUEh = Math.min(i, Integer.MAX_VALUE - (-low) -1);} catch( NumberFormatException nfe) {}}high = h;cache = new Integer[(high - low) + 1];int j = low;for(int k = 0; k < cache.length; k++)cache[k] = new Integer(j++);// range [-128, 127] must be interned (JLS7 5.1.7)assert IntegerCache.high >= 127;}private IntegerCache() {}}
}