根据北京尚学堂的视频所学习
结构型模式:
核心作用:是从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题。
分类:
适配器模式、代理模式、桥接模式、 装饰模式、组合模式、外观模式、享元模式
结构型模式汇总
代理模式 | 为真实对象提供一个代理,从而控制对真实对象的访问 |
适配模式 | 使原本由于接口不兼容不能一起工作的类可以一起工作 |
桥接模式 | 处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继 承结构,使各个维度可以独立的扩展在抽象层建立关联。 |
组合模式 | 将对象组合成树状结构以表示”部分和整体”层次结构,使得客户可以统一的调用叶子对象和容器对象 |
装饰模式 | 动态地给一个对象添加额外的功能,比继承灵活 |
外观模式 | 为子系统提供统一的调用接口,使得子系统更加容易使用 |
享元模式 | 运用共享技术有效的实现管理大量细粒度对象,节省内存,提高效率• 生活中的场景 |
壹、适配器(adapter)模式
生活中的场景
什么是适配器模式?
– 将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原 本由口不兼容而不能一起工于接作的那些类可以在一起工作。
• 模式中的角色
– 目标接口(Target):客户所期待的接口。目标可以是具体的或抽象 的类,也可以是接口。
– 需要适配的类(Adaptee):需要适配的类或适配者类。
– 适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成 目标接口。
/**
* 需要被适配的类
* @data :2019-6-1下午5:36:40
* @author :田坤
*/
public class Adaptee {
public static void handleRe(){
System.out.println("处理求情");
}
}
/**
* 目标接口
* @data :2019-6-1下午5:47:56
* @author :田坤
*/
public interface Target {
void handleRequest();
}
1、对象适配器
/**
* 适配器(对象适配器)
* @data :2019-6-1下午5:39:02
* @author :田坤
*/
public class Adapter implements Target{
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void handleRequest() {
adaptee.handleRe();
}
}
检测对象适配器
/**
* 测试对象适配器
* @data :2019-6-1下午5:45:08
* @author :田坤
*/
public class Test {
public void test(Target t){
t.handleRequest();
}
public static void main(String args[]) {
Test test = new Test();
Adaptee adaptee = new Adaptee();
Adapter adapter = new Adapter(adaptee);
test.test(adapter);
}
}
2、类适配器
/**
* 适配器(类适配器)
* @data :2019-6-1下午5:39:02
* @author :田坤
*/
public class AdapterClass extends Adaptee implements Target{
@Override
public void handleRequest() {
handleRe();
}
}
检测类适配器
/**
* 测试类适配器
* @data :2019-6-1下午5:45:08
* @author :田坤
*/
public class Test {
public void test(Target t){
t.handleRequest();
}
public static void main(String args[]) {
Test test = new Test();
Adaptee adaptee = new Adaptee();
AdapterClass adapterClass = new AdapterClass();
test.test(adapterClass);
}
}
类图
工作中的场景
– 经常用来做旧系统改造和升级
– 如果我们的系统开发之后再也不需要维护,那么很多模式都是没必要的,但是不幸的是,事实却是维护一个系统的代价往往是开发一个系统的数倍。
我们学习中见过的场景
– java.io.InputStreamReader(InputStream)
– java.io.OutputStreamWriter(OutputStream)
贰、代理模式
核心作用:
通过代理,控制对对象的访问! 可以详细控制访问某个(某类)对象的方法,在调用这个方法前做前置处理,调用这个方法后 做后置处理。(即:AOP的微观实现!)
– AOP(Aspect Oriented Programming面向切面编程)的核心实现机制!
代理模式(Proxy pattern):
核心角色:
• 抽象角色
– 定义代理角色和真实角色的公共对外方法
• 真实角色
– 实现抽象角色,定义真实角色所要实现的业务逻辑, 供代理角色调用。
– 关注真正的业务逻辑!
• 代理角色
– 实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加 自己的操作。
– 将统一的流程控制放到代理角色中处理!
应用场景:
– 安全代理:屏蔽对真实角色的直接访问。
– 远程代理:通过代理类处理远程方法调用(RMI)
– 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象。
比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时不可能将所有的图片都显示出来,这样就可以 使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。
• 分类:
– 静态代理(静态定义代理类)
– 动态代理(动态生成代理类)
• JDK自带的动态代理
• javaassist字节码操作库实现
• CGLIB
• ASM(底层使用指令,可维护性较差)
1、静态代理(static proxy)
/**
* 抽象对象
* @data :2019-6-1下午6:25:19
* @author :田坤
*/
public interface Star {
//面谈
void confer();
//签合同
void signContract();
//订票
void bookTicket();
//唱歌
void sing();
//收钱
void collectMoney();
}
/**
* 真实对象
* @data :2019-6-1下午6:25:04
* @author :田坤
*/
public class RealStar implements Star {
@Override
public void confer() {
System.out.println("realstar-confer");
}
@Override
public void signContract() {
System.out.println("realstar-signContract");
}
@Override
public void bookTicket() {
System.out.println("realstar-bookTicket");
}
@Override
public void sing() {
System.out.println("realstar(周杰伦)-Sing");
}
@Override
public void collectMoney() {
System.out.println("realstar-collectMoney");
}
}
/**
* 代理对象
* @data :2019-6-1下午6:25:55
* @author :田坤
*/
public class ProxyStar implements Star{
private RealStar realStar;
public ProxyStar(RealStar realStar) {
this.realStar = realStar;
}
@Override
public void confer() {
System.out.println("confer");
}
@Override
public void signContract() {
System.out.println("signContract");
}
@Override
public void bookTicket() {
System.out.println("bookTicket");
}
@Override
public void sing() {
realStar.sing();
}
@Override
public void collectMoney() {
System.out.println("collectMoney");
}
}
/**
* 检测静态代理
* @data :2019-6-1下午6:30:31
* @author :田坤
*/
public class Test {
public static void main(String args[]) {
RealStar realStar = new RealStar();
Star proxyStar = new ProxyStar(realStar);
proxyStar.confer();
proxyStar.signContract();
proxyStar.bookTicket();
proxyStar.sing();
proxyStar.collectMoney();
}
}
confer
signContract
bookTicket
realstar(周杰伦)-Sing
collectMoney
类图
2、动态代理(JDK自带的实现)
动态代理相比于静态代理的优点
–抽象角色中(接口)声明的所以方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。
JDK自带的动态代理
– java.lang.reflect.Proxy
• 作用:动态生成代理类和对象
– java.lang.reflect.InvocationHandler(处理器接口)
• 可以通过invoke方法实现对真实角色的代理访问。
• 每次通过Proxy生成代理类对象对象时都要指定对应的处理器对象
/**
* 抽象对象
* @data :2019-6-1下午6:25:19
* @author :田坤
*/
public interface Star {
//面谈
void confer();
//签合同
void signContract();
//订票
void bookTicket();
//唱歌
void sing();
//收钱
void collectMoney();
}
/**
* 真实对象
* @data :2019-6-1下午6:25:04
* @author :田坤
*/
public class RealStar implements Star {
@Override
public void confer() {
System.out.println("realstar-confer");
}
@Override
public void signContract() {
System.out.println("realstar-signContract");
}
@Override
public void bookTicket() {
System.out.println("realstar-bookTicket");
}
@Override
public void sing() {
System.out.println("realstar(周杰伦)-Sing");
}
@Override
public void collectMoney() {
System.out.println("realstar-collectMoney");
}
}
/**
* 对象处理器
* @data :2019-6-1下午7:04:41
* @author :田坤
*/
public class StarHandler implements InvocationHandler{
private Star realStar;
public StarHandler(Star realStar) {
this.realStar = realStar;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object obj = null;
System.out.println("真正的执行流程");
System.out.println("面谈,签合同,预付款,订机票");
if(method.getName().equals("sing")){
obj = method.invoke(realStar, args);
}
System.out.println("真正的方法执行后");
System.out.println("收尾款");
return obj;
}
}
/**
* 检测动态代理(JDK自带)
* @data :2019-6-1下午7:05:22
* @author :田坤
*/
public class Test0 {
public static void main(String args[]) {
Star realStar = new RealStar();
StarHandler handler = new StarHandler(realStar);
Star proxy = (Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new
Class[]{Star.class},handler);
proxy.sing();
}
}
真正的执行流程
面谈,签合同,预付款,订机票
realstar(周杰伦)-Sing
真正的方法执行后
收尾款
模拟动态内部调用过程
开发框架中应用场景:
– struts2中拦截器的实现
– 数据库连接池关闭处理
– Hibernate中延时加载的实现
– mybatis中实现拦截器插件
– AspectJ的实现
– spring中AOP的实现
• 日志拦截
• 声明式事务处理
– web service
– RMI远程方法调用
– ...
– 实际上,随便选择一个技术框架都会用到代理模式!!
叁、桥接模式(Bridge)
• 场景分析
– 商城系统中常见的商品分类,以电脑为类,如何良好的处理商品分类销售的问题?
– 这个场景中有两个变化的维度:电脑类型、电脑品牌。
桥接模式核心要点:
– 处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立 的继承结构,使各个维度可以独立的扩展在抽象层建立关联。
肆、组合模式(composite)
使用组合模式的场景:
把部分和整体的关系用树形结构来表示,从而使客户端可以使用统一的方式处理部分对 象和整体对象。
组合模式核心:
– 抽象构件(Component)角色: 定义了叶子和容器构件的共同点
– 叶子(Leaf)构件角色:无子节点
– 容器(Composite)构件角色: 有容器特征,可以包含子节点
组合模式工作流程分析:
– 组合模式为处理树形结构提供了完美的解决方案,描述了如何将容器和叶子进行递归组 合,使得用户在使用时可以一致性的对待容器和叶子。
– 当容器对象的指定方法被调用时,将遍历整个树形结构,寻找也包含这个方法的成员, 并调用执行。其中,使用了递归调用的机制对整个结构进行处理。
使用组合模式,模拟杀毒软件架构设计
package com.tk.composite;
import java.util.ArrayList;
import java.util.List;
/**
* 抽象组件构建
* @author 84741
*
*/
public interface AbstractFile {
void KillVirus(); //杀毒
}
/**
* 文件(叶子)
*/
class Imgaefile implements AbstractFile{
private String name;
public Imgaefile(String name) {
super();
this.name = name;
}
@Override
public void KillVirus() {
System.out.println("对图片文件"+name+"进行杀毒");
}
}
class Vodiofile implements AbstractFile{
private String name;
public Vodiofile(String name) {
super();
this.name = name;
}
@Override
public void KillVirus() {
System.out.println("对影像文件"+name+"进行杀毒");
}
}
class Textfile implements AbstractFile{
private String name;
public Textfile(String name) {
super();
this.name = name;
}
@Override
public void KillVirus() {
System.out.println("对文本文件"+name+"进行杀毒");
}
}
/**
* 文件夹(容器)
* @author 田坤
* @time 2019年6月11日 上午8:43:51
*/
class Folder implements AbstractFile{
private String name;
private List<AbstractFile> children = new ArrayList<AbstractFile>();
public Folder(String name) {
super();
this.name = name;
}
public void add(AbstractFile af) {
if(af!=null)
children.add(af);
}
public void remove(AbstractFile af) {
if(af!=null)
children.remove(af);
}
public AbstractFile get(int i) {
return children.get(i);
}
@Override
public void KillVirus() {
System.out.println("正在处理"+name+"文件夹.......");
for (AbstractFile abstractFile : children) {
abstractFile.KillVirus();
}
}
}
测试类
package com.tk.composite;
public class Test {
public static void main(String[] args) {
AbstractFile f1,f2;
f1 = new Textfile("hello.txt");
f2 = new Vodiofile("lol比赛");
Folder folder = new Folder("我的收藏");
folder.add(f1);
folder.add(f2);
AbstractFile f3,f4;
f3 = new Vodiofile("rngS9夺冠");
f4 = new Imgaefile("s9RNG 捧杯");
Folder folder2 = new Folder("S9圆梦");
folder2.add(f3);
folder2.add(f4);
folder.add(folder2);
folder.KillVirus();
a
}
}
正在处理我的收藏文件夹.......
对文本文件hello.txt进行杀毒
对影像文件lol比赛进行杀毒
正在处理S9圆梦文件夹.......
对影像文件rngS9夺冠进行杀毒
对图片文件s9RNG 捧杯进行杀毒
• 开发中的应用场景:
– 操作系统的资源管理器
– GUI中的容器层次图
– XML文件解析
– OA系统中,组织结构的处理
– Junit单元测试框架
• 底层设计就是典型的组合模式,TestCase(叶子)、TestUnite(容器) 、Test接口(抽象)
• Junit单元测试框架底层设计
底层设计就是典型的组合模式,TestCase(叶子)、TestUnite(容器) 、Test接口(抽象)
伍、装饰(器)模式(decoractor)
职责:
- 动态的为一个对象增加新的功能
- 装饰模式是一种用于代替继承的技术,无须通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀
实现细节:
– Component抽象构件角色:
• 真实对象和装饰对象有相同的接口。这样,客户端对象就能够以与真实对象相同的方式同装饰对象交互。
– ConcreteComponent 具体构件角色(真实对象):
• io流中的FileInputStream、FileOutputStream
– Decorator装饰角色:
• 持有一个抽象构件的引用。装饰对象接受所有客户端的请求,并把这些请求转发给真实的对象这样,就能在真实对象调用前后增加新的功能。
– ConcreteDecorator具体装饰角色:
• 负责给构件对象增加新的责任。
//– Component抽象构件角色:
public interface ICar {
void move();
}
//– ConcreteComponent 具体构件角色(真实对象):
class Car implements ICar {
@Override
public void move() {
System.out.println("在陆地上移动");
}
}
//– Decorator装饰角色:
class SuperCar implements ICar{
protected ICar car;
public SuperCar(ICar car) {
super();
this.car = car;
}
@Override
public void move() {
car.move();
}
}
//– ConcreteDecorator具体装饰角色:
class AICar extends SuperCar{
public AICar(ICar car) {
super(car);
}
public void autoMove() {
System.out.println("自动跑");
}
public void move() {
super.move();
autoMove();
}
}
//– ConcreteDecorator具体装饰角色:
class WaterCar extends SuperCar{
public WaterCar(ICar car) {
super(car);
}
public void WaterMove() {
System.out.println("水上跑");
}
public void move() {
super.move();
WaterMove();
}
}
//– ConcreteDecorator具体装饰角色:
class FlyCar extends SuperCar{
public FlyCar(ICar car) {
super(car);
}
public void FlyMove() {
System.out.println("天上飞");
}
public void move() {
super.move();
FlyMove();
}
}
测试类
public class Test {
public static void main(String[] args) {
Car car = new Car();
car.move();
System.out.println("添加水上跑功能.......");
WaterCar water = new WaterCar(car);
water.move();
System.out.println("添加天上飞和自动跑功能......");
FlyCar fly_anto = new FlyCar(new AICar(car));
fly_anto.move();
}
}
在陆地上移动
添加水上跑功能.......
在陆地上移动
水上跑
添加天上飞和自动跑功能......
在陆地上移动
自动跑
天上飞
• 开发中使用的场景:
– IO中输入流和输出流的设计
– Swing包中图形界面构件功能
– Servlet API 中提供了一个request对象的Decorator设计模式的默认实 现类HttpServletRequestWrapper,HttpServletRequestWrapper 类,增强了request对象的功能。
– Struts2中,request,response,session对象的处理
IO流实现细节:
– Component抽象构件角色:
• io流中的InputStream、OutputStream、Reader、Writer
– ConcreteComponent 具体构件角色:
• io流中的FileInputStream、FileOutputStream
– Decorator装饰角色:
• 持有一个抽象构件的引用:io流中的FilterInputStream、FilterOutputStream
– ConcreteDecorator具体装饰角色:
• 负责给构件对象增加新的责任。Io流中的BufferedOutputStream、BufferedInputStream等。
总结:
– 装饰模式(Decorator)也叫包装器模式(Wrapper)
– 装饰模式降低系统的耦合度,可以动态的增加或删除对象的职责,并 使得需要装饰的具体构建类和具体装饰类可以独立变化,以便增加新 的具体构建类和具体装饰类。
优点:
– 扩展对象功能,比继承灵活,不会导致类个数急剧增加
– 可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更 加强大的对象
– 具体构建类和具体装饰类可以独立变化,用户可以根据需要自己增加 新的具体构件子类和具体装饰子类。
缺点:
– 产生很多小对象。大量小对象占据内存,一定程度上影响性能。
– 装饰模式易于出错,调试排查比较麻烦。
陆、外观模式(dacade)
迪米特法则(最少知识原则):
– 一个软件实体应当尽可能少的与其他实体发生相互作用。
外观模式核心:
– 为子系统提供统一的入口。封装子系统的复杂性,便于客户端调用。
案例(类图)
– 注册公司流程(不使用外观模式)
注册公司流程(使用外观模式)
柒、享元模式
• 场景:
– 内存属于稀缺资源,不要随便浪费。如果有很多个完全相同或相似的 对象,我们可以通过享元模式,节省内存。
• 核心:
– 享元模式以共享的方式高效地支持大量细粒度对象的重用。
– 享元对象能做到共享的关键是区分了内部状态和外部状态。
• 内部状态:可以共享,不会随环境变化而改变
• 外部状态:不可以共享,会随环境变化而改变
• 享元模式实现:
– FlyweightFactory享元工厂类
• 创建并管理享元对象,享元池一般设计成键值对
– FlyWeight抽象享元类
• 通常是一个接口或抽象类,声明公共方法,这些方法可以向外界提供对象 的内部状态,设置外部状态。
– ConcreteFlyWeight具体享元类
• 为内部状态提供成员变量进行存储
– UnsharedConcreteFlyWeight非共享享元类
• 不能被共享的子类可以设计为非共享享元类
/**
* 享元类
* @author 田坤
* @time 2019年6月11日 上午10:45:25
*/
public interface ChessFlyWeight {
void setColor(String color);
String getColor();
void display(coordinate c);
}
// – ConcreteFlyWeight具体享元类
class ConcreteChess implements ChessFlyWeight{
private String color;
public ConcreteChess(String color) {
super();
this.color = color;
}
@Override
public void setColor(String color) {
this.color = color;
}
@Override
public String getColor() {
return color;
}
@Override
public void display(coordinate c) {
System.out.println("该棋子的颜色:"+getColor());
System.out.println("该棋子的坐标:"+c.getX()+"--"+c.getY());
}
}
/**
* 外部状态 UnsharedConcrete
* @author 田坤
* @time 2019年6月11日 上午10:42:31
*/
public class coordinate {
private int x,y;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public coordinate(int x, int y) {
super();
this.x = x;
this.y = y;
}
public coordinate() {
super();
}
@Override
public String toString() {
return "coordinate [x=" + x + ", y=" + y + "]";
}
}
/**
* 享元工厂类
* @author 田坤
* @time 2019年6月11日 上午10:53:18
*/
public class ChessFlyWeightFactory {
//享元池
private static Map<String,ChessFlyWeight> map = new HashMap<String, ChessFlyWeight>();
public static ChessFlyWeight getChess(String color) {
if(map.get(color)!=null)
return map.get(color);
else {
ChessFlyWeight chessFlyWeight = new ConcreteChess(color);
map.put(color, chessFlyWeight);
return chessFlyWeight;
}
}
}
测试类
public class Test {
public static void main(String[] args) {
ChessFlyWeight chess1 = ChessFlyWeightFactory.getChess("黑色");
ChessFlyWeight chess2 = ChessFlyWeightFactory.getChess("黑色");
System.out.println(chess1);
System.out.println(chess2);
ChessFlyWeightFactory.getChess("黑色").display(new coordinate(10, 20));
ChessFlyWeightFactory.getChess("黑色").display(new coordinate(20, 20));
}
}
com.tk.hengyuan.ConcreteChess@4f19c297
com.tk.hengyuan.ConcreteChess@4f19c297
该棋子的颜色:黑色
该棋子的坐标:10--20
该棋子的颜色:黑色
该棋子的坐标:20--20
享元模式开发中应用的场景:
– 享元模式由于其共享的特性,可以在任何“池”中操作,
比如:线程池、数据库连接池。
– String类的设计也是享元模式
• 优点
– 极大减少内存中对象的数量
– 相同或相似对象内存中只存一份,极大的节约资源,提高系统性能
– 外部状态相对独立,不影响内部状态
• 缺点
– 模式较复杂,使程序逻辑复杂化
– 为了节省内存,共享了内部状态,分离出外部状态,而读取外部状态 使运行时间变长。用时间换取了空间。
结构型模式汇总
代理模式 | 为真实对象提供一个代理,从而控制对真实对象的访问 |
适配模式 | 使原本由于接口不兼容不能一起工作的类可以一起工作 |
桥接模式 | 处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继 承结构,使各个维度可以独立的扩展在抽象层建立关联。 |
组合模式 | 将对象组合成树状结构以表示”部分和整体”层次结构,使得客户可以统一 的调用叶子对象和容器对象 |
装饰模式 | 动态地给一个对象添加额外的功能,比继承灵活 |
外观模式 | 为子系统提供统一的调用接口,使得子系统更加容易使用 |
享元模式 | 运用共享技术有效的实现管理大量细粒度对象,节省内存,提高效率 |