一Spring框架基础--2设计模式
1.3 spring用到的设计模式
1.3.1 责任链模式
有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象决定处理该请求。但是发出者并不清楚到底最终那个对象会处理该请求,所以,责任链模式可以实现,在隐瞒客户端的情况下,对系统进行动态的调整。
![image-20210729174604052](/i/l/?n=23&i=blog/2492369/202303/2492369-20230309121837500-1169831918.jpg)
public abstract class AbstractHandler {
private Handler handler;
public Handler getHandler() {
return handler;
}
public void setHandler(Handler handler) {
this.handler = handler;
}
}
Abstracthandler类提供了get和set方法,方便MyHandle类设置和修改引用对象,MyHandle类是核心,实例化后生成一系列相互持有的对象,构成一条链
public class MyHandler extends AbstractHandler implements Handler {
private String name;
public MyHandler(String name) {
this.name = name;
}
@Override
public void operator() {
//调用链之前,业务操作
System.out.println(name+"before deal!");
if(getHandler()!=null){
getHandler().operator();
}
//调用链之后,业务操作
System.out.println(name+"after deal!");
}
}
public class Test {
public static void main(String[] args) {
MyHandler h1 = new MyHandler("h1");
MyHandler h2 = new MyHandler("h2");
MyHandler h3 = new MyHandler("h3");
h1.setHandler(h2);
h2.setHandler(h3);
h1.operator();
}
}
此处强调一点就是,链接上的请求可以是一条链,可以是一个树,还可以是一个环,模式本身不约束这个,需要我们自己去实现,同时,在一个时刻,命令只允许由一个对象传给另一个对象,而不允许传给多个对象。
1.3.2 代理模式
interceptorHandler
详细见[1.4代理模式](##1.4 代理模式)
1.3.3 工厂模式
(1)普通工厂模式
建立一个工厂,负责对实现了同一个接口的一些类,进行实例的创建。
![img](/i/l/?n=23&i=blog/2492369/202303/2492369-20230309121837886-62406998.jpg)
//创建工厂类
public class SendFactory {
public Sender produce(String type) {
if ("mail".equals(type)) {
return new MailSender();
} else if ("sms".equals(type)) {
return new SmsSender();
} else {
System.out.println("请输入正确的类型!");
return null;
}
}
}
//使用
public class FactoryTest {
public static void main(String[] args) {
SendFactory factory = new SendFactory();
Sender sender = factory.produce("sms");
sender.Send();
}
}
缺点:利用factory创建需要传入字符串,如果出错不能创建。
(2)多个工厂方法模式
对普通的factory传入字符串的改进,创建多个方法实现不同对象的创建
![image-20210728170701091](/i/l/?n=23&i=blog/2492369/202303/2492369-20230309121838207-1283058505.jpg)
public class SendFactory {
public Sender produceMail(){
return new MailSender();
}
public Sender produceSms(){
return new SmsSender();
}
}
(3)静态工厂方法模式
将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。
public class SendFactory {
//方法为static,可直接调用
public static Sender produceMail(){
return new MailSender();
}
public static Sender produceSms(){
return new SmsSender();
}
}
(4)抽象工厂模式
前面三种工厂模式一个问题,类的创建依赖factory,当需要拓展程序,需要对factory修改,违背了设计模式的闭包原则。
考虑用抽象工厂模式,创建多个相应的工厂类,一旦需要增加新的功能,就相应的创建新的factory。
![image-20210728171341737](/i/l/?n=23&i=blog/2492369/202303/2492369-20230309121838499-224869654.jpg)
public class SendMailFactory implements Provider {
@Override
public Sender produce(){
return new MailSender();
}
}
//工厂类生产的抽象接口
public interface Provider {
public Sender produce();
}
public class Test {
public static void main(String[] args) {
Provider provider = new SendMailFactory();
Sender sender = provider.produce();
sender.Send();
}
}
(5)spring中的工厂模式
工厂模式和spring IOC设计思想契合:某一接口的具体实现类的选择控制权从调用类中移除,转而交给第三方决定,即借由Spring的Bean配置来实现控制。
spring就是封装工程中大量重复代码的工具。spirng重要的组件是IOC,而IOC重要部分就是应用了工厂模式的代码。工厂模式依赖于Java反射机制。
反射机制是程序运行过程中,任何对象可以获取它的类和方法、属性。
//1.加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
//2.获得数据库链接
Connection conn=DriverManager.getConnection(URL, USER, PASSWORD);
Class.forName()方法就是在程序运行的过程中,动态的将Driver这个类加加载到JVM(Java虚拟机)中并初始化,然后我们就能够直接调用这个类及其方法。实际上最简单的工厂模式就是用的这个方法,在JVM运行期直接通过forName方法创建某个类
spring工厂模式,不使用new创建对象,使用共同interface接收实现类对象。因此使用时候,直接调用接口的方法,只需要改变实现此接口的实现类,就可以完成对象的创建和软件的迭代。
import java.util.ResourceBundle;
/**
* 使用此工厂类创建bean实例
*/
public class BeanFactory {
//加载配置文件(static块来静态加载)
private static ResourceBundle bundle;
static {
bundle = ResourceBundle.getBundle("instance");
}
//根据指定的key,读取配置文件的全路径,创建对象
public static <T>T getInstance(String key,Class<T> clazz){
String className = bundle.getString(key);
//反射创建实例对象
try {
return (T)Class.forName(className).newInstance();
}catch (Exception e){
throw new RuntimeException();
}
}
}
ResourceBundle这个类是用来读取classpath中文件,这个文件需要放在resources文件夹或java包的根路径下,文件名必须是*.properties这个配置文件类型。这样读取到的文件流就形成了键值对的存储形式。配置文件的示例如下(使用键值对的方式配置):
#service instance
foodTypeService=service.impl.FoodTypeService
dinnerTableService=service.impl.DinnerTableService
注意,配置文件中配置的需是接口的实现类,因为在工厂创建Bean的时候我们是使用接口来接收实现类的,这样才能够实现解耦合。
调用时候:
protected IDinnerTableService dinnerTableService = BeanFactory.getInstance("dinnerTableService",IDinnerTableService.class);
beanfactory与applicationContext的关系,[详见2.2](##2.2 ApplicationContext与BeanFactory)
1.3.4 观察者模式
详见[1.5](##1.5 事件机制event-listener—观察者模式)
spring中applicationListener的使用:
ApplicationListener是Spring事件机制的一部分,与抽象类ApplicationEvent类配合来完成ApplicationContext的事件机制。
如果容器中存在ApplicationListener的Bean,当ApplicationContext调用publishEvent方法时,对应的Bean会被触发。这一过程是典型的观察者模式的实现
![img](/i/l/?n=23&i=blog/2492369/202303/2492369-20230309121838807-1752714188.jpg)
![img](/i/l/?n=23&i=blog/2492369/202303/2492369-20230309121839249-1234034600.jpg)
![img](/i/l/?n=23&i=blog/2492369/202303/2492369-20230309121839564-1595686491.jpg)
1.5.1 spring中的事件处理
Spring 的核心是 ApplicationContext,它负责管理 beans 的完整生命周期。当加载 beans 时,ApplicationContext 发布某些类型的事件。例如,当上下文启动时,ContextStartedEvent 发布,当上下文停止时,ContextStoppedEvent 发布。
(通过 ApplicationEvent 类和 ApplicationListener 接口来提供在 ApplicationContext 中处理事件。如果一个 bean 实现 ApplicationListener,那么每次 ApplicationEvent 被发布到 ApplicationContext 上,那个 bean 会被通知)
Spring提供的标准事件
(这些事件,都是对applicationcontext进行相关操作时,相对应的事件)
注意:spring的事件处理是单线程,当一个线程发布,直至所有接收者得到该消息,否则该进程被阻塞,流程不会继续。
(1)监听上下文context事件
通过 ApplicationEvent 类和 ApplicationListener 接口来提供在 ApplicationContext 中处理事件。如果一个 bean 实现 ApplicationListener,那么每次 ApplicationEvent 被发布到 ApplicationContext 上,那个 bean 会被通知。
为了监听上下文事件,一个 bean 应该实现只有一个方法 onApplicationEvent() 的 ApplicationListener 接口。
定义的两个监听事件的类,就是bean,需要实现接口,并实现onApplicationEvent方法,方法中需要传递进去spring的5种内置类型的事件,这样,当主程序中,ApplicationContext执行相对应类型的事件时候(如事件是contextstartedevent,当上下文执行context.start方法,那么实现onApplicationEvent方法的类,就是bean将收到通知,就是执行该方法。)
主函数:
配置文件bean.xml
(注意:配置文件中,仍然要把上面的两个bean的相关配置,写入xml)
1.5.2 spring中的自定义事件
步骤:
(一)自定义事件customevent需要扩展ApplicationEvent,并且,需要创建构造函数,继承自父类。传入的source是事件发布类publisherEvent的实现类publish。
(二)定义事件发布类。
事件发布类作为需要实现ApplicationEventPublisherAware,另外,需要在XML配置文件中声明这个类为bean。
容器之所以识别该bean作为事件发布者,就是因为实现了ApplicationEventPublisherAware该接口。
然后,既然作为发布类,就需要定义发布publish方法:首先实现set属性,对ApplicationEventPublisher的对象,set赋值,然后publisher.publishEvent(),加入该发布者对应的自定义的事件类,就可以等待事件发生。(customEventPublisher发布者,事件自然是customEvent事件)
(三)定义事件监听类
同上节内容。该类作为监听bean,也需要配置在XML文件中。
(四)主程序
由于事件与ApplicationContext直接关联,自然需要创建上下文对象;然后在上下文对象中获取发布者,context.getBean(publisher),获取发布者;然后获得的发布者实例对象,调用publish()方法,实现自定义事件的监听。
1.3.5 装饰模式
装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
要求在不改变原类文件和适用继承的情况下,动态地扩展一个对象地功能。它是通过创建一个包装对象,也就是装饰来包裹真实地对象。
关系图如下:
![image-20210729141508771](/i/l/?n=23&i=blog/2492369/202303/2492369-20230309121839879-845586834.jpg)
source是被装饰类,decorator是装饰类。
public class Decorator implements Sourceable {
private Sourceable source;
public Decorator(Sourceable source){
super();
this.source = source;
}
@Override
public void method() {
System.out.println("before decorator!");
source.method();
System.out.println("after decorator!");
}
}
测试类:
public class DecoratorTest {
public static void main(String[] args) {
Sourceable source = new Source();
Sourceable obj = new Decorator(source);
obj.method();
}
}
特点:
1.装饰对象和真实对象具有相同地接口。这样客户端对象就能以真实对象相同地方式和装饰对象交互
2.装饰对象包含一个真实对象地引用(GameObject)
3.装饰对象接受所有来自客户端的请求,它把这些请求转发给真实对象
4.装饰对象可以在转发这些以前或者以后增加一些附加功能,这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展
适用条件:
1.需要扩展一个类的功能,或给一个类添加附加职责
2.需要动态的给一个对象添加功能,这些功能还可以再动态的撤销
3.需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使得继承关系变得不现实
4.当不能采用生成子类的方法进行扩充时,一种情况是:可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类
优点:
1.Decorator模式与继承关系的目的都是要扩展对象的功能,但是Decorator可以提供比继承更多的灵活性
2.通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合
缺点:
1.这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性
2.装饰模式会导致设计出来许多小类,如果过度使用,会使程序变得很复杂
3.装饰模式是针对抽象组件(Component)类型编程,但是,如果要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。
装饰器模式的应用场景:
1、需要扩展一个类的功能。
2、动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)
缺点:产生过多相似的对象,不易排错!
1.3.6 模板方法模式(spring的refresh>onfresh)
一个抽象类中,有一个主方法,再定义1...n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用,先看个关系图:
![image-20210730022551491](/i/l/?n=23&i=blog/2492369/202303/2492369-20230309121840142-1354632758.jpg)
在抽象类abstract中定义一个模板方法public calculate(string),然后调用本身的其他方法完成其功能,在定义一个abstract public calculate(int)方法,定义类继承该抽象类并override抽象方法,并由原abstract类calculate(string)调用。子类实现的方法为真正的逻辑。这样,就可以用抽象类模板方法的调用,分别调用不同逻辑的实现方法。
public abstract class AbstractCalculator {
/*主方法,实现对本类其它方法的调用*/
public final int calculate(String exp,String opt){
int array[] = split(exp,opt);
return calculate(array[0],array[1]);
}
/*被子类重写的方法*/
abstract public int calculate(int num1,int num2);
public int[] split(String exp,String opt){
String array[] = exp.split(opt);
int arrayInt[] = new int[2];
arrayInt[0] = Integer.parseInt(array[0]);
arrayInt[1] = Integer.parseInt(array[1]);
return arrayInt;
}
}
public class Plus extends AbstractCalculator {
@Override
public int calculate(int num1,int num2) {
return num1 + num2;
}
}
public class StrategyTest {
public static void main(String[] args) {
String exp = "8+8";
AbstractCalculator cal = new Plus();
int result = cal.calculate(exp, "\\+");
System.out.println(result);
}
}
重点:
需要abstract class和abstract 方法,这个两个需要继承并实现具体的方法。方法主逻辑是final且非抽象的。
就是抽象类定义一个模板方法,并实现其自身调用,设置一个抽象方法,让实现类去override该方法实现具体逻辑。
1.3.7 委派模式
委派模式是一种行为型模式,它允许对象用组合去实现像继承一样的代码复用。在委派模式中,对象通过委托第二个对象(也就是被委派对象)来处理请求。
换句话说,跟代理模式有点像,都要对另外的对象做处理,但是不同的是,代理模式更多的是注重代理的过程,也就是如何去实现对被代理对象的增强或其他动作;委派模式更多的是注重结果,就是委托第三方对象来处理请求从而最终得到的效果。
![image-20210730025632217](/i/l/?n=23&i=blog/2492369/202303/2492369-20230309121840458-1627248573.jpg)
Boss
发出指令要Leader
去执行,但是Leader
不会自己去做,而是把这些指令肯跟员工实际情况分发给对应的员工去完成,这样就完成了委派。
这里的关键在leader有registry表,所有委派对象这里注册,可以根据请求选择第三方委派对象处理。
public interface Employee {
/**
* 接收任务指令工作
* @param command
*/
void working(String command);
}
public class EmployeeC implements Employee{
@Override
public void working(String command) {
System.out.println("我接到委派【" + command + "】,要开始编码coding了" );
}
}
public class EmployeeW implements Employee {
@Override
public void working(String command) {
System.out.println("我接到委派【" + command + "】,要开始写需求writing了" );
}
}
leader有所有委派者的注册信息
public class Leader {
private final Map<String, Employee> register = new HashMap<>();
public Leader() {
register.put("writing", new EmployeeW());
register.put("coding", new EmployeeC());
}
public void doing(String command) {
Optional.ofNullable(register.get(command)).orElseThrow(() -> new RuntimeException("命令不存在")).working(command);
}
}
public class Boss {
public void command(String command, Leader leader) {
leader.doing(command);
}
}
委派流程就实现了,
new Boss().command("coding", new Leader());
标签:String,--,Spring,模式,class,对象,new,设计模式,public
From: https://www.cnblogs.com/LBJboy/p/17197931.html