代理模式
(1)基本介绍:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象。好处:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
(2)被代理的对象可以说远程对象/创建开销大的对象或需要安全控制的对象
(3)代理模式有不同的形式,主要有静态代理/动态代理(JDK代理或接口代理)和Cglib代理(可以在内存动态的创建对象,而不需要实现接口,也属于动态代理的范畴)
(4)代理模式示意图:
静态代理
基本介绍:静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类
应用实例: 1)定义一个接口:ITeacherDao
public interface ITeacherDao {
void teach();//授课方法
}
- 目标对象TeacherDao实现接口ITeacherDao
public class TeacherDao implements ITeacherDao{
@Override
public void teach() {
System.out.println("老师授课中。。。");
}
}
- 使用静态代理方式,就需要在代理对象TeacherDaoProxy中也实现ITeacherDao,同时定义一个ITeacherDao类型的属性和有参构造函数TeacherDaoProxy(ITeacherDao)以持有目标对象TeacherDao的引用,方便使用目标对象的方法
/**
* 代理对象,静态代理
*/
public class TeacherDaoProxy implements ITeacherDao {
private ITeacherDao iTeacherDao;//目标对象,通过接口来聚合
public TeacherDaoProxy(ITeacherDao iTeacherDao) {
this.iTeacherDao = iTeacherDao;
}
@Override
public void teach() {
System.out.println("代理开始。。。");//额外代码
iTeacherDao.teach();//核心功能(代码)
System.out.println("代理结束。。。");//增强功能
}
}
4)调用的时候通过调用代理对象的方法来调用目标对象
public class Client {
public static void main(String[] args) {
//创建目标对象(被代理对象)
TeacherDao teacherDao = new TeacherDao();
//创建代理对象,同时将被代理对象传递给代理对象
TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);
//通过代理对象,调用到被代理对象的方法
//即:执行的是代理对象的方法,代理对象再去调用目标对象的方法
teacherDaoProxy.teach();
}
}
5)静态代理模式示意图:
命令模式
1)命令模式使得请求发送者与请求接受者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
2)在命令模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命令),同时命令模式也支持可撤销的操作。
3)通俗易懂的理解:将军发布命令士兵去执行。其中有几个角色:将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)
Invoker是调用者(将军),Receiver是被调用者(士兵),MyCommand是命令,实现了Command接口,持有对象。
. Invoker 调用者角色
. Command 命令角色,需要执行的所有命令都在这里
. Receiver 接受者角色,知道如何实施和执行一个请求,可以是接口或抽象类
. ConcreteCommand 将一个接受者对象与一个动作绑定,调用接受者相应的操作实现
public interface Command {
//执行动作(操作)
void execute();
//撤销动作(操作)
void undo();
}
/**
* 没有任何命令,即空执行:用于初始化每个按钮,当调用空命令时,对象什么都不做
* 其实,这样式是一种设计模式,可以省掉空判断
*/
public class NoCommand implements Command {
@Override
public void execute() {
}
@Override
public void undo() {
}
}
public class LightOnCommand implements Command {
//聚合接受者LightReceiver
private LightReceiver lightReceiver;
public LightOnCommand(LightReceiver lightReceiver) {
this.lightReceiver = lightReceiver;
}
@Override
public void execute() {
//调用接受者的方法
lightReceiver.on();
}
@Override
public void undo() {
//调用接受者的方法
lightReceiver.off();
}
}
public class LightOffCommand implements Command {
private LightReceiver lightReceiver;
public LightOffCommand(LightReceiver lightReceiver) {
this.lightReceiver = lightReceiver;
}
@Override
public void execute() {
lightReceiver.off();
}
@Override
public void undo() {
lightReceiver.on();
}
}
public class LightReceiver {
public void on(){
System.out.println("电灯打开了。。。");
}
public void off(){
System.out.println("电灯关闭了");
}
}
public class RemoteController {
//开 按钮的命令数组
Command[] onCommands;
Command[] offCommands;
//执行撤销的命令
Command undoCommand;
//构造器,完成对按钮初始化
public RemoteController() {
onCommands = new Command[5];
offCommands = new Command[5];
for (int i = 0; i < 5;i++){
onCommands[i] = new NoCommand();
offCommands[i] = new NoCommand();
}
}
//给我们的按钮设置你需要的命令
public void setCommand(int no, Command onCommand, Command offCommand){
onCommands[no] = onCommand;
offCommands[no] = offCommand;
}
public void onButtonWasPushed(int no){
//找到你按下的开的按钮,并调用对应方法
onCommands[no].execute();
//记录这次的记录,用于撤销
undoCommand = onCommands[no];
}
//按下关按钮
public void offButtonWasPushed(int no){
//找到你按下的开的按钮,并调用对应方法
offCommands[no].execute();
//记录这次的记录,用于撤销
undoCommand = offCommands[no];
}
//按下撤销按钮
public void undoButtonWasPushed(){
undoCommand.undo();
}
}
访问者模式
适配器模式
类适配器模式
基本介绍:Adapter类,通过继承src类,实现dst类接口,完成src->dst的适配
类适配器模式应用实例:
1)充电器本身相当于Adapter,220V交流电相当于src(即被适配者),我们的目标dst是5V直流电
2)图解:
//被适配的类
public class Voltage220V {
//输出220电压
public int output220V(){
int src = 220;
System.out.println("电压 = " + src + "伏");
return src;
}
}
//适配接口
public interface IVoltage5V {
public int output5V();
}
//适配器类
public class VoltageAdapter extends Voltage220V implements IVoltage5V {
@Override
public int output5V() {
//获取到220V电压
int srcV = output220V();
int dstV = srcV /44;//转成5V
return dstV;
}
}
public class Phone {
//充电
public void charing(IVoltage5V iVoltage5V){
if (iVoltage5V.output5V() == 5){
System.out.println("电压5V,可以充电。。。");
}else if (iVoltage5V.output5V() > 5){
System.out.println("电压大于5V,不能充电");
}
}
}
public class Client {
public static void main(String[] args) {
System.out.println("==类适配器模式==");
Phone phone = new Phone();
phone.charing(new VoltageAdapter());
}
}
类适配器模式注意事项和细节:
1)Java是单继承机制,所以类适配器需要继承src类,这一点算是一个缺点,因为这要求dst必须是接口,有一点的局限性;
2)src类的方法在Adapter中都会暴露出来,也增加了使用的成本
3)由于其继承了src类,所以它可以根据需求重写src类的方法,使得Adapter的灵活性增强了。
对象适配器模式
1)基本思路和类的适配器模式相同,只是将Adapter类作修改,不是继承src类,而是持有src类的实例,以解决兼容性的问题。即:持有src类,实现dst类接口,完成src-dst的适配
2)根据“合成复用原则”,在系统中尽量使用关联关系替代继承关系。
3)对象适配器模式是适配器模式常用的一种
图解:
对上面的应用实例改造,只需改动适配器类Adapter和client客户端调用
//适配器类
public class VoltageAdapter implements IVoltage5V {
private Voltage220V voltage220V;//关联关系-聚合
//通过构造器,传入一个Voltage220V 实例
public VoltageAdapter(Voltage220V voltage220V){
this.voltage220V = voltage220V;
}
@Override
public int output5V() {
int dst = 0;
if (voltage220V != null){
int src = voltage220V.output220V();//获取220V 电压
dst = src / 44;
System.out.println("输出电压 = " + dst);
}
return dst;
}
}
public class Client {
public static void main(String[] args) {
System.out.println("==对象适配器模式==");
Phone phone = new Phone();
phone.charing(new VoltageAdapter(new Voltage220V()));
}
}
接口适配器模式
1)当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择性的覆盖父类的某些方法来实现需求。
2)适用于一个接口不想使用其所有的方法的情况。
public interface Interface4 {
public void m1();
public void m2();
public void m3();
public void m4();
}
//在AbsAdapter 我们将Interface4 的方法进行默认实现
public abstract class AbsAdapter implements Interface4{
@Override
public void m1() {
}
@Override
public void m2() {
}
@Override
public void m3() {
}
@Override
public void m4() {
}
}
public class Client {
public static void main(String[] args) {
AbsAdapter absAdapter = new AbsAdapter(){
//只需要去覆盖我们需要使用的接口方法(匿名内部类)
@Override
public void m1() {
System.out.println("使用了m1的方法");
}
};
absAdapter.m1();
}
}
适配器模式在SpringMVC框架应用的源码分析
1)SpringMVC中的HandlerAdapter,就使用了适配器模式 2)SpringMVC处理请求的流程回顾:
3)使用HandlerAdapter的原因分析:可以看到处理器的类型不同,有多重实现方,那么调用方式就不是确定的,如果需要直接调用Controller方法,需要调用的时候就得不断使用if else 来进行判断是哪一种子类然后执行,那么如果后面要扩展Controller,就得修改原来的代码,这就违背了OCP原则。
原型模式
1)原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。
2)原型模式是一种创建型设计模式,允许一个对象再创建另一个可定制的对象,无需知道如何创建的细节。
3)对象.clone()
4)形象的理解:孙大圣拔出猴毛,变出其他孙大圣。
克隆羊问题:
现在有一只羊tom,姓名:tom,年龄为:1,颜色:白色,请编写程序创建和tom羊属性完全相同的10只羊。
传统的方式的缺点:
(1)在创建新的对象时,总时需要重新获取原始对象的属性,如果创建的对象比较复杂,效率比较低
(2)总是需要重新初始化对象,而不是动态地获得对象运行的时的状态,不够灵活
(3)改进的思路分析:Java中的Object类是所有类的根类,Object类提供了一个clone()方法,该方法可以将一个Java对象复制一份,但是需要实现clone的Java类必须要实现一个接口Cloneable,该接口表示该类能够复制且具有复制的能力 =》 原型模式
(4)图解:
public class Sheep implements Cloneable{
private String name;
private int age;
private String color;
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
// get() and set()
// toString()
//克隆该实例,使用默认的clone方法来完成
@Override
protected Object clone(){
Sheep sheep = null;
try {
sheep = (Sheep) super.clone();
}catch (Exception e){
System.out.println(e.getMessage());
}
return sheep;
}
}
public class client {
public static void main(String[] args) {
System.out.println("原型模式完成对象的克隆");
Sheep sheep = new Sheep("tom",1,"白色");
Sheep sheep2 = (Sheep)sheep.clone();//克隆
Sheep sheep3 = (Sheep)sheep.clone();//克隆
System.out.println("sheep2 = " + sheep2);
System.out.println("sheep3 = " + sheep3);
}
}
如果需要添加其他属性address等,直接修改原型Sheep即可。
原型模式再Spring框架中源码分析:
1)Spring中原型bean的创建,就是原型模式的应用(扩展)
深入讨论-浅拷贝和深拷贝
浅拷贝的介绍:
1)对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
2)对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
3)前面我们的克隆羊就是浅拷贝。
4)浅拷贝是使用默认的clone()方法来实现sheep=(Sheep)super.clone()
深拷贝的介绍:
1)复制对象的所有基本数据类型的成员变量值
2)为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象。
3)深拷贝实现方式1:重写clone方法来实现深拷贝
4)深拷贝实现方式2:通过对象序列话实现深拷贝
建造者模式
基本介绍:
1)建造者模式(Builder Pattern)又叫生成器模式,属于创建型模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方式可以构造出不同表现(属性)的对象。
2)建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
建造者模式的四个角色
1)Product(产品角色):一个具体的产品对象。
2)Builder(抽象建造者):创建一个Product对象的各个部件指定的接口/抽象类。
3)ConcreteBuilder(具体建造者):实现接口,构建和装配各个部件。
4)Director(指挥者):构建一个使用Builder接口的对象。。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生成过程,二是负责控制产品对象的生产过程。
建造者模式解决盖房需求应用实例:
1)需要建房子:这一过程为打桩、砌墙、封顶。不管是普通房子也好,别墅也好都需要经历这些过程。
2)思路分析图解:
/**
* 产品角色 -> Product
* 定义产品的基本属性
*/
public class House {
private String baise;
private String wall;
private String roofed;
//geter and seter
}
// 抽象的建造者
public abstract class HouseBuilder {
protected House house = new House();
//将建造的流程写好,抽象的方法
public abstract void buildBasic();
public abstract void buildWalls();
public abstract void roofed();
//建造房子好,将产品(房子)返回
public House buildHouse(){
return house;
}
}
/**
* 具体建造者角色 -> ConcreteBuilder
* 负责实现抽象的建造过程,按指定的规格建造特定的产品
*/
public class CommonHouse extends HouseBuilder {
@Override
public void buildBasic() {
System.out.println("普通房子打地基5米");
}
@Override
public void buildWalls() {
System.out.println("普通房子砌墙10cm");
}
@Override
public void roofed() {
System.out.println("普通房子屋顶");
}
}
/**
* 具体建造者角色 -> ConcreteBuilder
* 负责实现抽象的建造过程,按指定的规格建造特定的产品
*/
public class HighHouse extends HouseBuilder {
@Override
public void buildBasic() {
System.out.println("高楼的打地基100米");
}
@Override
public void buildWalls() {
System.out.println("高楼的砌墙20cm");
}
@Override
public void roofed() {
System.out.println("高楼的透明屋顶");
}
}
/**
* 指挥者,指定制作流程,并返回产品
*/
public class HouseDirector {
HouseBuilder houseBuilder = null;
//构造器传入 houseBuilder
public HouseDirector(HouseBuilder houseBuilder){
this.houseBuilder =houseBuilder;
}
//通过setter 传入 houseBuilder,不必再重新创建新的指挥者
public void setHouseBuilder(HouseBuilder houseBuilder){
this.houseBuilder = houseBuilder;
}
//如何处理建造房子的流程,交给指挥者(组装各个部分零件)
public House constructHouse(){
houseBuilder.buildBasic();
houseBuilder.buildWalls();
houseBuilder.roofed();
return houseBuilder.buildHouse();
}
}
public class client {
public static void main(String[] args) {
//盖普通房子
CommonHouse commonHouse = new CommonHouse();
//准备创建房子的指挥者
HouseDirector houseDirector = new HouseDirector(commonHouse);
House house = houseDirector.constructHouse();
//盖高楼
HighHouse highBuilder = new HighHouse();
//重置建造者
houseDirector.setHouseBuilder(highBuilder);
//完成盖房子,返回产品(高楼)
houseDirector.constructHouse();
}
}
建造者模式在JDK的应用和源码分析
1)Appendable接口定义了许多append抽象方法,即Appendable为抽象建造者,定义了抽象方法。
2)AbstractStringBuilder抽象类实现了Appendable接口方法,这里的AbstractStringBuilder已经是具体建造者,只是不能实例化。
3)StringBuilder即是指挥者角色,又是具体建造者。建造方法的实现是由AbstractStringBuilder完成的,StringBuilder只是继承了AbstractStringBuilder,使用了父类方法而已。
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence{
/*
* 使用了父类的方法
*/
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
...
}
/*
*实现了Appendable接口中的抽象方法,但此类仍然是抽象类,不能实例化
*/
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/*
* 实现了Appendable接口中的方法
*/
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
...
}
public interface Appendable {
/*
*建造过程的抽象方法
*/
Appendable append(CharSequence csq, int start, int end) throws IOException;
Appendable append(CharSequence csq) throws IOException;
Appendable append(char c) throws IOException;
}