结构型模式
一、设计模式总分类
创建型模式( 5种 )
用来帮助我们创建对象
- 单例模式
- 工厂模式
- 抽象工厂模式
- 建造者模式
- 原型模式
结构型模式( 7种 )
核心作用:是从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题。
- 适配器模式
- 代理模式
- 桥接模式
- 装饰模式
- 组合模式
- 外观模式
- 享元模式
行为型模式( 11种 )
- 模版方法模式
- 命令模式
- 迭代器模式
- 观察者模式
- 中介者模式
- 备忘录模式
- 解释器模式
- 状态模式
- 策略模式
- 职责链模式
- 访问者模式
二、适配器模式
作用
将一个类的接口转换成客户希望的另外一个接口。
Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。
应用场景
- 经常用来做旧系统改造和升级
- java.io.InputStreamReader(InputStream)
- java.io.OutputStreamWriter(OutputStream)
演示代码
um类图
package ah.szxy.adapter;
/**
* 1.处理请求
* @author chy
*
*/
public interface Target {
void handleReq();
}
package ah.szxy.adapter;
/**
* 2.被适配的类
* @author chy
*
*/
public class Adaptee {
public void request(){
System.out.println("可以完成客户请求的需要的功能!");
}
}
package ah.szxy.adapter;
/**
* 3. 适配器 (类适配器方式)
* @author Administrator
*
*/
public class Adapter extends Adaptee implements Target {
@Override
public void handleReq() {//处理请求
super.request();
}
}
package ah.szxy.adapter;
/**
* 4. 适配器 (对象适配器方式,使用了组合的方式跟被适配对象整合)
* (相当于usb和ps/2的转接器)
* @author Administrator
*
*/
public class Adapter2 implements Target {
private Adaptee adaptee;
@Override
public void handleReq() {
adaptee.request();
}
public Adapter2(Adaptee adaptee) {
super();
this.adaptee = adaptee;
}
}
package ah.szxy.adapter;
/**
* 5 .客户端类
* (相当于例子中的笔记本,只有USB接口)
* @author chy
*
*/
public class Client {
public void test1(Target t){
t.handleReq();
}
public static void main(String[] args) {
Client c = new Client();
Adaptee a = new Adaptee();
//测试类适配器
Target t = new Adapter();
//测试对象适配器
//Target t = new Adapter2(a);
c.test1(t);
}
}
测试结果
三、代理模式
作用
- 通过代理,控制对对象的访问! 可以详细控制访问某个(某类)对象的方法,在调用这个方法前做前置处理,调用这个方法后做后置处理。(即:AOP的微观实现!)
- AOP(Aspect Oriented Programming面向切面编程)的核心实现机制!
面向切面常用术语:
应用场景
- 安全代理:屏蔽对真实角色的直接访问。
- 远程代理:通过代理类处理远程方法调用(RMI)
- 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象。
分类
静态代理(静态定义代理类)
动态代理(动态生成代理类)
- JDK自带的动态代理
- javaassist字节码操作库实现
- CGLIB
- ASM(底层使用指令,可维护性较差)
演示代码
静态代理
package ah.szxy.staticproxy;
/**
* 1. 明星接口
* @author chy
*
*/
public interface Star {
/**
* 面条
*/
void confer();
/**
* 签协议
*/
void signContract();
/**
* 订票
*/
void bookTicket();
/**
* 唱歌
*/
void sing();
/**
* 收钱
*/
void collectMoney();
}
package ah.szxy.staticproxy;
/**
* 2 .真实明星类
* @author chy
*
*/
public class RealStar implements Star {
@Override
public void confer() {
// TODO Auto-generated method stub
System.out.println("RealStar.confer()");
}
@Override
public void signContract() {
// TODO Auto-generated method stub
System.out.println("RealStar.signContract()");
}
@Override
public void bookTicket() {
// TODO Auto-generated method stub
System.out.println("RealStar.bookTicket()");
}
@Override
public void sing() {
// TODO Auto-generated method stub
System.out.println("RealStar.sing(明星本人)");
}
@Override
public void collectMoney() {
// TODO Auto-generated method stub
System.out.println("RealStar.collectMoney()");
}
}
package ah.szxy.staticproxy;
/**
* 3. 明星代理类
* @author chy
*
*/
public class ProxyStar implements Star {
//引入明星
private Star star;
//带参构造 ,可以帮助区分是哪个明星
public ProxyStar(Star star) {
super();
this.star = star;
}
@Override
public void confer() {
System.out.println("ProxyStar.confer()");
}
@Override
public void signContract() {
// TODO Auto-generated method stub
System.out.println("ProxyStar.signContract()");
}
@Override
public void bookTicket() {
// TODO Auto-generated method stub
System.out.println("ProxyStar.bookTicket()");
}
@Override
public void sing() {
System.out.println("----//只要唱歌时才调用明星------");
star.sing();
}
@Override
public void collectMoney() {
// TODO Auto-generated method stub
System.out.println("ProxyStar.collectMoney()");
}
}
package ah.szxy.staticproxy;
/**
* 测试静态代理类
* @author 曹海洋
*
*/
public class Client {
public static void main(String[] args) {
RealStar real = new RealStar();
ProxyStar proxy = new ProxyStar(real);
proxy.confer();
proxy.signContract();
proxy.bookTicket();
proxy.sing();
proxy.collectMoney();
}
}
测试结果
uml类图
动态代理
package ah.szxy.dynamicproxy;
/**
* 1 .明星接口
* @author chy
*
*/
public interface Star {
/**
* 面条
*/
void confer();
/**
* 签协议
*/
void signContract();
/**
* 订票
*/
void bookTicket();
/**
* 唱歌
*/
void sing();
/**
* 收钱
*/
void collectMoney();
}
package ah.szxy.dynamicproxy;
/**
* 2. 真实明星类
* @author chy
*
*/
public class RealStar implements Star {
@Override
public void confer() {
// TODO Auto-generated method stub
System.out.println("RealStar.confer()");
}
@Override
public void signContract() {
// TODO Auto-generated method stub
System.out.println("RealStar.signContract()");
}
@Override
public void bookTicket() {
// TODO Auto-generated method stub
System.out.println("RealStar.bookTicket()");
}
@Override
public void sing() {
// TODO Auto-generated method stub
System.out.println("RealStar.sing(明星本人)");
}
@Override
public void collectMoney() {
// TODO Auto-generated method stub
System.out.println("RealStar.collectMoney()");
}
}
package ah.szxy.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 3 .继承动态调用的类
* @author chy
*
*/
public class StarHandler implements InvocationHandler {
Star realStar;
public StarHandler(Star realStar) {
super();
this.realStar = realStar;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object object = null;
System.out.println("真正的方法执行前!");
System.out.println("面谈,签合同,预付款,订机票");
if(method.getName().equals("sing")){
object = method.invoke(realStar, args);
}
System.out.println("真正的方法执行后!");
System.out.println("收尾款");
return object;
}
}
package ah.szxy.dynamicproxy;
/**
*4. 模拟动态生成的代理的结构
* @author chy
*
*/
public class ProxyStar implements Star {
StarHandler handler;
public ProxyStar(StarHandler handler) {
super();
this.handler = handler;
}
public void bookTicket() {
// handler.invoke(this,当前方法 , args);
}
public void collectMoney() {
// handler.invoke(this,当前方法 , args);
}
public void confer() {
// handler.invoke(this,当前方法 , args);
}
public void signContract() {
// handler.invoke(this,当前方法 , args);
}
public void sing() {
// handler.invoke(this,当前方法 , args);
}
}
package ah.szxy.dynamicproxy;
import java.lang.reflect.Proxy;
/**
* 测试动态代理
* @author chy
*
*/
public class Client {
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();
}
}
测试结果
JDK动态代理
动态代理与静态代理的比较
抽象角色中(接口)声明的所以方法都被转移到调用处理器一个集中的方法中处理 ,
我们可以更加灵活和统一的处理众多的方法。
四、桥接模式
作用
处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继承结构,使各个维度可以独立的扩展在抽象层建立关联。
应用场景
商城系统中常见的商品分类,以电脑为类,如何良好的处理商品分类销售的问题?
这个场景中有两个变化的维度:电脑类型、电脑品牌。
实际开发中的应用场景
演示代码
package ah.szxy.bridge;
/**
* 1. 品牌
* @author chy
*
*/
public interface Brand {
void sale();
}
class Lenovo implements Brand {
@Override
public void sale() {
System.out.println("销售联想电脑");
}
}
class Dell implements Brand {
@Override
public void sale() {
System.out.println("销售Dell电脑");
}
}
class Shenzhou implements Brand {
@Override
public void sale() {
System.out.println("销售神舟电脑");
}
}
package ah.szxy.bridge;
/**
* 2. 电脑类型的维度
* @author chy
*
*/
public class Computer2 {
protected Brand brand;
public Computer2(Brand b) {
this.brand = b;
}
public void sale(){
brand.sale();
}
}
class Desktop2 extends Computer2 {
public Desktop2(Brand b) {
super(b);
}
@Override
public void sale() {
super.sale();
System.out.println("销售台式机");
}
}
class Laptop2 extends Computer2 {
public Laptop2(Brand b) {
super(b);
}
@Override
public void sale() {
super.sale();
System.out.println("销售笔记本");
}
}
package ah.szxy.bridge;
/**
* 3 .测试桥接模式
* @author chy
*
*/
public class Client {
public static void main(String[] args) {
//销售联想的笔记本电脑
Computer2 c = new Laptop2(new Lenovo());
c.sale();
//销售神舟的台式机
Computer2 c2 = new Desktop2(new Shenzhou());
c2.sale();
}
}
测试结果
uml类图
未使用桥接模式
使用桥接模式
五、装饰模式
作用
装饰模式(Decorator)也叫包装器模式(Wrapper)装饰模式降低系统的耦合度,可以动态的增加或删除对象的职责,并使得需要装饰的具体构建类和具体装饰类可以独立变化,以便增加新的具体构建类和具体装饰类。
优点
扩展对象功能,比继承灵活,不会导致类个数急剧增加
可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更加强大的对象
具体构建类和具体装饰类可以独立变化,用户可以根据需要自己增加新的具体构件子类和具体装饰子类。
缺点
产生很多小对象。大量小对象占据内存,一定程度上影响性能。
装饰模式易于出错,调试排查比较麻烦。
实现细节
- Component抽象构件角色:
• 真实对象和装饰对象有相同的接口。这样,客户端对象就能够以与真实对象相同的方式同装饰对象交互。 - ConcreteComponent 具体构件角色(真实对象):
• io流中的FileInputStream、FileOutputStream - Decorator装饰角色:
• 持有一个抽象构件的引用。装饰对象接受所有客户端的请求,并把这些请求转发给真实的对象
。这样,就能在真实对象调用前后增加新的功能。 - ConcreteDecorator具体装饰角色:
• 负责给构件对象增加新的责任。
应用场景
- IO中输入流和输出流的设计
- Swing包中图形界面构件功能
- Servlet API 中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper,HttpServletRequestWrapper类,增强了request对象的功能。
- Struts2中,request,response,session对象的处理
演示代码
uml类图
package ah.szxy.decorator;
/**
* 1.抽象构建
* ICar接口 ,定义车属性
* Car实现接口
* SuperCar实现接口
* FlyCar继承SuperCar
* WaterCar继承FlyCar
* AICar继承FlyCar
* @author chy
*
*/
public interface ICar {
void move();
}
//ConcreteComponent 具体构件角色(真实对象)
class Car implements ICar {
@Override
public void move() {
System.out.println("陆地上跑!");
}
}
//Decorator装饰角色 ,重写了父类的方法 ,作用是,每次调用move时, 都会打印这就话
class SuperCar implements ICar {
protected ICar car;
public SuperCar(ICar car) {
super();
this.car = car;
}
@Override
public void move() {
car.move();
}
}
//ConcreteDecorator具体装饰角色
class FlyCar extends SuperCar {
public FlyCar(ICar car) {
super(car);
}
public void fly(){
System.out.println("天上飞!");
}
@Override
public void move() {
super.move();
fly();
}
}
//ConcreteDecorator具体装饰角色
class WaterCar extends SuperCar {
public WaterCar(ICar car) {
super(car);
}
public void swim(){
System.out.println("水上游!");
}
@Override
public void move() {
super.move();
swim();
}
}
//ConcreteDecorator具体装饰角色
class AICar extends SuperCar {
public AICar(ICar car) {
super(car);
}
public void autoMove(){
System.out.println("自动跑!");
}
@Override
public void move() {
super.move();
autoMove();
}
}
package ah.szxy.decorator;
import java.io.FileInputStream;
import java.io.InputStreamReader;
/**
* 2 .测试装饰模式
* @author chy
*
*/
public class Client {
public static void main(String[] args) {
Car car = new Car();
car.move();
System.out.println("增加新的功能,飞行----------");
FlyCar flycar = new FlyCar(car);
flycar.move();
System.out.println("增加新的功能,水里游---------");
WaterCar waterCar = new WaterCar(car);
waterCar.move();
/**
* 自我总结 :
* 包装类使用后 ,再调用该方法,相当于new 后面的对象都调用了该方法
* 例如 :waterCar2.move();等价于
* car.move()
* FiyCar.move()
* WaterCar.move();
*/
System.out.println("增加两个新的功能,飞行,水里游-------");
WaterCar waterCar2 = new WaterCar(new FlyCar(car));
waterCar2.move();
// Reader r = new BufferedReader(new InputStreamReader(new FileInputStream(new File("d:/a.txt"))));
}
}
测试结果
装饰模式与桥接模式区别
两个模式都是为了解决过多子类对象问题。但他们的诱因不一样。
桥模式是对象自身现有机制沿着多个维度变化,是既有部分不稳定。装饰模式是为了增加新的功能。
六、组合模式
作用
组合模式为处理树形结构提供了完美的解决方案,描述了如何将容器和叶子进行递归组合,使得用户在使用时可以一致性的对待容器和叶子。 当容器对象的指定方法被调用时,将遍历整个树形结构,寻找也包含这个方法的成员, 并调用执行。其中,使用了递归调用的机制对整个结构进行处理。
应用场景
演示代码
package ah.szxy.composite;
/**
* 1. 抽象组件
* @author chy
*
*/
public interface Component {
void operation();
}
//叶子组件 extends接口后,可以实现它父类的方法,但是不是必须的
interface Leaf extends Component {
}
//容器组件
interface Composite extends Component {
void add(Component c);
void remove(Component c);
Component getChild(int index);
}
package ah.szxy.composite;
import java.util.ArrayList;
import java.util.List;
//2. 抽象构建
public interface AbstractFile {
void killVirus(); //杀毒
}
class ImageFile implements AbstractFile {
private String name;
public ImageFile(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+",进行查杀!");
}
}
class VideoFile implements AbstractFile {
private String name;
public VideoFile(String name) {
super();
this.name = name;
}
@Override
public void killVirus() {
System.out.println("---视频文件:"+name+",进行查杀!");
}
}
class Folder implements AbstractFile {
private String name;
//定义容器,用来存放本容器构建下的子节点
private List<AbstractFile> list = new ArrayList<AbstractFile>();
public Folder(String name) {
super();
this.name = name;
}
public void add(AbstractFile file){
list.add(file);
}
public void remove(AbstractFile file){
list.remove(file);
}
public AbstractFile getChild(int index){
return list.get(index);
}
@Override
public void killVirus() {
System.out.println("---文件夹:"+name+",进行查杀");
for (AbstractFile file : list) {
file.killVirus();
}
}
}
package ah.szxy.composite;
/**
* 3. 测试组合模式
* @author chy
*
*/
public class Client {
public static void main(String[] args) {
AbstractFile f2,f3,f4,f5;
Folder f1 = new Folder("我的图片");
f2 = new ImageFile("你楸啥.jpg");
f3 = new TextFile("瞅你咋地.txt");
f1.add(f2);
f1.add(f3);
Folder f11 = new Folder("电影");
f4 = new VideoFile("有话好好说.avi");
f5 = new VideoFile("后会无期.avi");
f11.add(f4);
f11.add(f5);
f1.add(f11);
// f2.killVirus();
f1.killVirus();
}
}
测试结果
uml类图
七、外观模式
作用
应用场景
开发中常见的场景
- 频率很高。哪里都会遇到。各种技术和框架中,都有外观模式的使用。
- 如:JDBC封装后的commons提供的DBUtils类,Hibernate提供的工具类、Spring JDBC工具类
基本案例
注册公司流程(使用外观模式)见如下代码
演示代码
package ah.szxy.facade;
public interface 工商局 {
void checkName(); //核名
}
class 海淀区工商局 implements 工商局 {
@Override
public void checkName() {
System.out.println("检查名字是否有冲突!");
}
}
package ah.szxy.facade;
public interface 税务局 {
void taxCertificate(); //办理税务登记证
}
class 海淀税务局 implements 税务局 {
@Override
public void taxCertificate() {
System.out.println("在海淀税务局办理税务登记证!");
}
}
package ah.szxy.facade;
public interface 质检局 {
void orgCodeCertificate(); //办理组织机构代码证
}
class 海淀质检局 implements 质检局 {
@Override
public void orgCodeCertificate() {
System.out.println("在海淀区质检局办理组织机构代码证!");
}
}
package ah.szxy.facade;
public interface 银行 {
void openAccount(); //开户
}
class 中国工商银行 implements 银行 {
@Override
public void openAccount() {
System.out.println("在中国工商银行开户!");
}
}
package ah.szxy.facade;
/**
* 办理注册公司流程的门面对象
* 只需要调用这个方法 ,即可完成注册公司所有步骤
* @author chy
*
*/
public class RegisterFacade {
public void register(){
工商局 a = new 海淀区工商局();
a.checkName();
质检局 b = new 海淀质检局();
b.orgCodeCertificate();
税务局 c = new 海淀税务局();
c.taxCertificate();
银行 d = new 中国工商银行();
d.openAccount();
}
}
package ah.szxy.facade;
/***
* 测试外观模式
*
* 不使用外观模式时
* 注册公司所需要的模式都需要自己来跑,
* 但是注册公司的模式固定 ,可以进行封装
* @author chy
*
*/
public class Client1 {
public static void main(String[] args) {
// 工商局 a = new 海淀区工商局();
// a.checkName();
// 质检局 b = new 海淀质检局();
// b.orgCodeCertificate();
// 税务局 c = new 海淀税务局();
// c.taxCertificate();
// 银行 d = new 中国工商银行();
// d.openAccount();
new RegisterFacade().register();
}
}
uml类图
不使用外观模式
使用外观模式
八、享元模式
作用
内存属于稀缺资源,不要随便浪费。如果有很多个完全相同或相似的对象,我们可以通过享元模式,节省内存
享元模式以共享的方式高效地支持大量细粒度对象的重用。
享元对象能做到共享的关键是区分了内部状态和外部状态。
- 内部状态:可以共享,不会随环境变化而改变
- 外部状态:不可以共享,会随环境变化而改变
应用场景
享元模式开发中应用的场景
享元模式由于其共享的特性,可以在任何“池”中操作,比如:线程池、数据库连接池。
String类的设计也是享元模式
实际中的应用
演示代码
package ah.szxy.flyweight;
/**
* 1. 外部状态UnSharedConcreteFlyWeight
* 定义棋子坐标
* @author Administrator
*
*/
public class Coordinate {
private int x,y;
public Coordinate(int x, int y) {
super();
this.x = x;
this.y = 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;
}
}
package ah.szxy.flyweight;
/**
* 2.享元类
* 棋子颜色设置 ,位置设置
* @author chy
*
*/
public interface ChessFlyWeight {
void setColor(String c);
String getColor();
void display(Coordinate c);
}
class ConcreteChess implements ChessFlyWeight {
private String color;
public ConcreteChess(String color) {
super();
this.color = color;
}
@Override
public void display(Coordinate c) {
System.out.println("棋子颜色:"+color);
System.out.println("棋子位置:"+c.getX()+"----"+c.getY());
}
@Override
public String getColor() {
return color;
}
@Override
public void setColor(String c) {
this.color = c;
}
}
package ah.szxy.flyweight;
import java.util.HashMap;
import java.util.Map;
/**
* 3. 享元工厂类
* @author Administrator
*
*/
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 cfw = new ConcreteChess(color);
map.put(color, cfw);
return cfw;
}
}
}
package ah.szxy.flyweight;
/**
* 测试享元模式
* @author chy
*
*/
public class Client {
public static void main(String[] args) {
ChessFlyWeight chess1 = ChessFlyWeightFactory.getChess("黑色");
ChessFlyWeight chess2 = ChessFlyWeightFactory.getChess("黑色");
System.out.println(chess1);
System.out.println(chess2);
System.out.println("增加外部状态的处理===========");
chess1.display(new Coordinate(10, 10));
chess2.display(new Coordinate(20, 20));
}
}
测试结果
uml类图
九、结构型模式总结
结构型模式分类 | 作用 |
---|---|
代理模式 | 为真实对象提供一个代理,从而控制对真实对象的访问 |
适配模式 | 使原本由于接口不兼容不能一起工作的类可以一起工作 |
桥接模式 | 处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继承结构,使各个维度可以独立的扩展在抽象层建立关联。 |
组合模式 | 将对象组合成树状结构以表示”部分和整体”层次结构,使得客户可以统一 的调用叶子对象和容器对象 |
装饰模式 | 动态地给一个对象添加额外的功能,比继承灵活 |
外观模式 | 为子系统提供统一的调用接口,使得子系统更加容易使用 |
享元模式 | 运用共享技术有效的实现管理大量细粒度对象,节省内存,提高效率 |