适配器模式
-
模式动机
将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
-
类图
-
优缺点
- 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。
- 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。
- 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合开闭原则。
-
案例
- javax.xml.bind.annotation.adapters.XmlAdapter
- org.springframework.aop.framework.adapter.AdvisorAdapter
- org.springframework.orm.jpa.JpaVendorAdapter
- org.springframework.web.servlet.HandlerAdapter
桥接模式
- 模式动机
将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。
- 适用场景
- 抽象与具体实现之间增加更多的灵活性
- 一个类存在两个(或多个)独立变化的维度,且这两个维度都需要独立进行扩展
- 不希望使用继承,因为多层继承导致类的个数剧增
- 类图
public class ImageExample {
public static void main(String[] args) {
Image image = new PNGImage();
ImageImp imageImp = new WindowsImp();
image.setImageImp(imageImp);
image.parseFile("小龙女");
}
}
//像素矩阵类:辅助类,各种格式的文件最终都被转化为像素矩阵,不同的操作系统提供不同的方式显示像素矩阵
class Matrix {
//此处代码省略
}
//抽象图像类:抽象类
abstract class Image {
protected ImageImp imp;
public void setImageImp(ImageImp imp) {
this.imp = imp;
}
public abstract void parseFile(String fileName);
}
//抽象操作系统实现类:实现类接口
interface ImageImp {
public void doPaint(Matrix m); //显示像素矩阵m
}
//Windows操作系统实现类:具体实现类
class WindowsImp implements ImageImp {
public void doPaint(Matrix m) {
//调用Windows系统的绘制函数绘制像素矩阵
System.out.print("在Windows操作系统中显示图像:");
}
}
//Linux操作系统实现类:具体实现类
class LinuxImp implements ImageImp {
public void doPaint(Matrix m) {
//调用Linux系统的绘制函数绘制像素矩阵
System.out.print("在Linux操作系统中显示图像:");
}
}
//Unix操作系统实现类:具体实现类
class UnixImp implements ImageImp {
public void doPaint(Matrix m) {
//调用Unix系统的绘制函数绘制像素矩阵
System.out.print("在Unix操作系统中显示图像:");
}
}
//JPG格式图像:扩充抽象类
class JPGImage extends Image {
public void parseFile(String fileName) {
//模拟解析JPG文件并获得一个像素矩阵对象m;
Matrix m = new Matrix();
imp.doPaint(m);
System.out.println(fileName + ",格式为JPG。");
}
}
//PNG格式图像:扩充抽象类
class PNGImage extends Image {
public void parseFile(String fileName) {
//模拟解析PNG文件并获得一个像素矩阵对象m;
Matrix m = new Matrix();
imp.doPaint(m);
System.out.println(fileName + ",格式为PNG。");
}
}
//BMP格式图像:扩充抽象类
class BMPImage extends Image {
public void parseFile(String fileName) {
//模拟解析BMP文件并获得一个像素矩阵对象m;
Matrix m = new Matrix();
imp.doPaint(m);
System.out.println(fileName + ",格式为BMP。");
}
}
//GIF格式图像:扩充抽象类
class GIFImage extends Image {
public void parseFile(String fileName) {
//模拟解析GIF文件并获得一个像素矩阵对象m;
Matrix m = new Matrix();
imp.doPaint(m);
System.out.println(fileName + ",格式为GIF。");
}
}
-
优缺点
优点:- 分离抽象部分及其具体实现部分
- 提高了系统的可扩展性
- 符合开闭原则
- 符合合成复用原则
缺点:
- 增加了系统的理解和设计难度
- 需要正确的识别出系统中两个独立变化的维度
-
案例
- java.sql.Driver 与java.sql.DriverManager
- java.util.logging.Formatter 与 java.util.logging.Handler
组合模式
- 模式动机
组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,组合模式又可以称为“整体—部分”(Part-Whole)模式,它是一种对象结构型模式。
- 适用场景
- 希望客户端忽略组合对象与单个对象的差异
- 处理树形结构
- 类图
public class CompExample {
public static void main(String[] args) {
}
}
//抽象文件类:抽象构件
abstract class AbstractFile {
public abstract void add(AbstractFile file);
public abstract void remove(AbstractFile file);
public abstract AbstractFile getChild(int i);
public abstract void killVirus();
}
//图像文件类:叶子构件
class ImageFile extends AbstractFile {
private String name;
public ImageFile(String name) {
this.name = name;
}
public void add(AbstractFile file) {
System.out.println("对不起,不支持该方法!");
}
public void remove(AbstractFile file) {
System.out.println("对不起,不支持该方法!");
}
public AbstractFile getChild(int i) {
System.out.println("对不起,不支持该方法!");
return null;
}
public void killVirus() {
//模拟杀毒
System.out.println("----对图像文件'" + name + "'进行杀毒");
}
}
//文本文件类:叶子构件
class TextFile extends AbstractFile {
private String name;
public TextFile(String name) {
this.name = name;
}
public void add(AbstractFile file) {
System.out.println("对不起,不支持该方法!");
}
public void remove(AbstractFile file) {
System.out.println("对不起,不支持该方法!");
}
public AbstractFile getChild(int i) {
System.out.println("对不起,不支持该方法!");
return null;
}
public void killVirus() {
//模拟杀毒
System.out.println("----对文本文件'" + name + "'进行杀毒");
}
}
//视频文件类:叶子构件
class VideoFile extends AbstractFile {
private String name;
public VideoFile(String name) {
this.name = name;
}
public void add(AbstractFile file) {
System.out.println("对不起,不支持该方法!");
}
public void remove(AbstractFile file) {
System.out.println("对不起,不支持该方法!");
}
public AbstractFile getChild(int i) {
System.out.println("对不起,不支持该方法!");
return null;
}
public void killVirus() {
//模拟杀毒
System.out.println("----对视频文件'" + name + "'进行杀毒");
}
}
//文件夹类:容器构件
class Folder extends AbstractFile {
//定义集合fileList,用于存储AbstractFile类型的成员
private ArrayList<AbstractFile> fileList = new ArrayList<AbstractFile>();
private String name;
public Folder(String name) {
this.name = name;
}
public void add(AbstractFile file) {
fileList.add(file);
}
public void remove(AbstractFile file) {
fileList.remove(file);
}
public AbstractFile getChild(int i) {
return (AbstractFile) fileList.get(i);
}
public void killVirus() {
System.out.println("****对文件夹'" + name + "'进行杀毒"); //模拟杀毒
//递归调用成员构件的killVirus()方法
for (Object obj : fileList) {
((AbstractFile) obj).killVirus();
}
}
}
-
优缺点
优点:- 清楚的定义分层次的复杂对象,表示对象的全部或部分层次
- 让客户端忽略了层次的差异,方便对整个层次结构进行控制
- 简化客户端代码
- 符合开闭原则
缺点:
- 限制类型时较为复杂
- 设计变得更加抽象
-
案例
- Container#add
- HashMap#putAll
- ArrayList#addAll
- mybatis#SqlNode
装饰者模式
-
模式动机
. 动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰者模式比生成子类实现更为灵活。装饰模式是一种对象结构型模式。 -
适用场景
- 扩展一个类的功能或者给一个类添加附加职责;
- 动态的给一个对象添加功能,这些功能可以在动态的撤销;
-
类图
public class DecoratorExample { public static void main(String[] args) { Component component, componentSB;//使用抽象构件定义 component = new Window(); //定义具体构件 componentSB = new ScrollBarDecorator(component); //定义装饰后的构件 componentSB.display(); } } //抽象界面构件类:抽象构件类,为了突出与模式相关的核心代码, // 对原有控件代码进行了大量的简化 abstract class Component { public abstract void display(); } //窗体类:具体构件类 class Window extends Component { public void display() { System.out.println("显示窗体!"); } } //文本框类:具体构件类 class TextBox extends Component { public void display() { System.out.println("显示文本框!"); } } //列表框类:具体构件类 class ListBox extends Component { public void display() { System.out.println("显示列表框!"); } } //构件装饰类:抽象装饰类 class ComponentDecorator extends Component { private Component component; //维持对抽象构件类型对象的引用 public ComponentDecorator(Component component) {//注入抽象构件类型的对象 this.component = component; } public void display() { component.display(); } } //滚动条装饰类:具体装饰类 class ScrollBarDecorator extends ComponentDecorator { public ScrollBarDecorator(Component component) { super(component); } public void display() { this.setScrollBar(); super.display(); } public void setScrollBar() { System.out.println("为构件增加滚动条!"); } } //黑色边框装饰类:具体装饰类 class BlackBorderDecorator extends ComponentDecorator { public BlackBorderDecorator(Component component) { super(component); } public void display() { this.setBlackBorder(); super.display(); } public void setBlackBorder() { System.out.println("为构件增加黑色边框!"); } }
-
优缺点
优点:- 继承的有力补充,比继承灵活,不改变原有对象的情况下给一个对象扩展功能;
- 通过使用不同的装饰类以及这些装饰类的排列组合,可以实现不同的效果;
- 符合开闭原则;
缺点:
- 会出现更多的代码,更多的类,增加程序的复杂性;
- 动态装饰时以及多层装饰时会更复杂;
-
案例
- java.io
- org.springframework.cache.transaction.TransactionAwareCacheDecorator
- javax.servlet.ServletRequestWrapper
- java.io
外观模式
- 模式动机
为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称为门面模式,它是一种对象结构型模式。外观模式是迪米特法则的一种具体实现,通过引入一个新的外观角色可以降低原有系统的复杂度,同时降低客户类与子系统的耦合度。
-
适用场景
- 当子系统越来越复杂,增加外观模式提供简单调用接口;
- 构建多层系统结构,利用外观对象作为每层的入口,简化层间调用;
-
类图
-
优缺点
优点:- 简化了调用过程,无需深入了解子系统,防止带来风险;
- 减少系统以来,松散耦合;
- 更好的划分访问的层次;
- 符合迪米特法则即最少知道原则;
缺点:
- 增加子系统 扩展子系统行为容易引入风险;
- 不符合开闭原则(在增加子系统 扩展子系统时);
-
案例
- org.springframework.jdbc.support.JdbcUtils
- org.apache.catalina.connector.RequestFacade/ResponseFacade
- org.apache.catalina.session.StandardSessionFacade
- org.apache.catalina.core.ApplicationContextFacade
享元模式
-
模式动机
运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。
-
适用场景
- 常常应用于系统底层的开发,以便解决系统的性能问题;
- 系统有大量的相似对象,需要缓冲池的场景;
-
类图
public class FlyWeightExample { public static void main(String[] args) { IgoChessman black1, black2, black3, white1, white2; //通过享元工厂获取三颗黑子 black1 = IgoChessmanFactory.getIgoChessman("b"); black2 = IgoChessmanFactory.getIgoChessman("b"); black3 = IgoChessmanFactory.getIgoChessman("b"); System.out.println("判断两颗黑子是否相同:" + (black1 == black2)); //通过享元工厂获取两颗白子 white1 = IgoChessmanFactory.getIgoChessman("w"); white2 = IgoChessmanFactory.getIgoChessman("w"); System.out.println("判断两颗白子是否相同:" + (white1 == white2)); //显示棋子,同时设置棋子的坐标位置 black1.display(new Coordinates(1, 2)); black2.display(new Coordinates(3, 4)); black3.display(new Coordinates(1, 3)); white1.display(new Coordinates(2, 5)); white2.display(new Coordinates(2, 4)); } } //坐标类:外部状态类 class Coordinates { private int x; private int y; public Coordinates(int x, int y) { this.x = x; this.y = y; } public int getX() { return this.x; } public void setX(int x) { this.x = x; } public int getY() { return this.y; } public void setY(int y) { this.y = y; } } //围棋棋子类:抽象享元类 abstract class IgoChessman { public abstract String getColor(); public void display(Coordinates coord) { System.out.println("棋子颜色:" + this.getColor() + ",棋子位置:" + coord.getX() + "," + coord.getY()); } } //黑色棋子类:具体享元类 class BlackIgoChessman extends IgoChessman { public String getColor() { return "黑色"; } } //白色棋子类:具体享元类 class WhiteIgoChessman extends IgoChessman { public String getColor() { return "白色"; } } //围棋棋子工厂类:享元工厂类,使用单例模式进行设计 class IgoChessmanFactory { //使用Hashtable来存储享元对象,充当享元池 private static Hashtable<String, IgoChessman> ht; static { ht = new Hashtable<>(); IgoChessman black, white; black = new BlackIgoChessman(); ht.put("b", black); white = new WhiteIgoChessman(); ht.put("w", white); } //通过key来获取存储在Hashtable中的享元对象 public static IgoChessman getIgoChessman(String color) { return ht.get(color); } }
-
优缺点
优点:- 减少对象的创建,降低内存中对象的数量,较低系统的内存,提升效率;
- 减少内存之外的其他资源占用;
缺点:
- 关注内 外部状态,关注线程安全问题;
- 使系统 程序的逻辑复杂化;
-
案例
-
java.lang.Integer#valueOf(int)
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
} -
pool
-
代理模式
- 模式动机
给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。
-
适用场景
- 保护目标对象
- 增强目标对象
-
类图
class ProxyFactory { // 接收一个目标对象 private Object target; public ProxyFactory(Object target) { this.target = target; } // 返回对目标对象(target)代理后的对象(proxy) public Object getProxyInstance() { /* 执行代理对象方法时候触发 */ return Proxy.newProxyInstance( target.getClass().getClassLoader(), // 目标对象使用的类加载器 target.getClass().getInterfaces(), // 目标对象实现的所有接口 (proxy1, method, args) -> { // 获取当前执行的方法的方法名 String methodName = method.getName(); // 方法返回值 Object result; if ("find".equals(methodName)) { // 直接调用目标对象方法 result = method.invoke(target, args); } else { System.out.println("开启事务..."); // 执行目标对象方法 result = method.invoke(target, args); System.out.println("提交事务..."); } return result; } ); } } public class Main { public static void main(String[] args) { //静态代理 UserDaoProxy userDaoProxy = new UserDaoProxy(); User user = userDaoProxy.find(); System.out.println(user.getName()); userDaoProxy.save(user); //动态代理 IUserDao proxy = (IUserDao) new ProxyFactory( new UserDaoImpl()).getProxyInstance(); System.out.println(proxy.getClass()); proxy.save(user); } }
-
优缺点
优点:- 代理模式能将代理对象与真实被调用的目标对象分离
- 一定程度上降低了系统的耦合度,扩展性好;
- 保护目标对象
- 增强目标对象
缺点:
- 代理模式会造成系统设计中类的数目增加;
- 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢;
- 增加了系统的复杂度;
-
案例
-
java.lang.reflect.Proxy
-
org.springframework.aop.framework.AopProxy
-