设计模式

一、工厂模式(用于生产对象)

  • 前置代码
public interface Animal{}
public class Cat implements Animal(){}
public class Dog implements Animal(){}
  1. 普通模式
    缺陷:名字输入错误,则无法得到对象
public class AnimalFactory(){
	public Animal getBean(String name){
		if(name.equals('cat'))
			return new Cat();
		if(name.equals('dog'))
			return new Dog();
	}
}

2.普通方法模式
缺陷:每次获取对象前都要先创建工厂对象,浪费内存

public class AnimalFactory(){
	public Animal getCat(){
		return new Cat();
	}
	public Animal getDog(){
		return new Dog();
	}
}

3.静态方法模式(常用)
Spring中的运用:注册单例的Bean时,用的就是工厂模式

public class AnimalFactory(){
	public static Animal getCat(){
		return new Cat();
	}
	public static Animal getDog(){
		return new Dog();
	}
}

一、工厂方法模式

  • 定义工厂Bean接口,要求每个工厂Bean都要有getObject方法
public interface FactoryBean(){
	public Object getObject();
}
  • 定义猫工厂
public class CatFactory implement FactoryBean(){
	public Object getObject(){
		return new Cat();
	}
} 
  • 定义狗工厂
public class DogFactory implement FactoryBean(){
	public Object getObject(){
		return new Dog();
	}
} 
  • 具体体现
public void main(object[] args){
	//因为猫、狗工厂都实现接口FactoryBean,可以通过多态完成引用
	FactoryBean fb=new CatFactory();
	//此时因为fb引用的是猫工厂,所以调用getObject方法时,执行的是红色处代码,获得猫对象
	Cat cat=(Cat)fb.getObject();
	
	//假设此处将new CatFactory()替换为new DogFactory(),执行的是蓝色处的代码,获得狗对象
	FactoryBean fb=new DogFactory();
	Dog dog=(Dog)fb.getObject();
}

Spring应用:
①IoC容器管理的Bean分为两种,一种是普通Bean,一种是工厂Bean
②Spring整合第三方框架时,原理正是将第三方框架关键普通Bean的生产交给Spring的IoC容器管理,比如MyBatis的SqlSessionFactory,可以看到xml配置文件中配置的

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

查看源码可以知道SqlSessionFactoryBean实现了FactoryBean接口,这个接口正是定义了该类是一个工厂Bean,工厂Bean肯定要生产Bean,它生产的正是SqlSessionFactory;
如何生产?实现接口就要重写接口中所有的方法,而FactoryBean接口中存在T getObject()方法,不同的工厂根据该方法返回自己该生产的对象,SqlSessionFactory工厂自然返回SqlSessionFactory对象

此处注意,当BeanFactory使用getBean方法时传入“sqlSessionFactory”时,获得的不是SqlSessionFactoryBean的对象,而是getObject()返回的对象SqlSessionFactory

三、单例模式(应用场景:使用打印机,创建连接池等)

  • 懒汉式(需要时才创建对象)
    缺点:无线程保护,放入多线程中,很可能A、B两个线程同时访问,两者同时走到****处,然后创建了2个对象
public class Singleton{
	private static Singleton instance=null;
	private Singleton(){}	
	public static Singleton getInstance(){
		if(instance==null){
			****
			instance=new Singleton();
		}
		return instance;
	}
}
  • 懒汉式改进:加锁
    改进:在获取实例时加锁,如果有线程正在获取实例,阻止其他线程调用该方法,避免多条线程同时处于红线处的情况,保证了安全性
    缺点:性能有所降低,实际上只有第一次获取对象时,需要判断有没有对象
public static synchronized Singleton getInstance(){
	if(instance==null){
		instance=new Singleton();
	}
	return instance;
}
  • 懒汉式改进:加一步判断
    改进:提升了一些性能
    缺点:这里涉及到JVM内部优化机制,由于创建对象和赋值操作是分两步进行,且优先级是不固定的,线程A创建变量,引用对象,释放锁,此时对象还未完成初始化
    线程B进行,获得对象引用,并进行调用,发现对象初始化还未完成(clinit()方法未执行完毕),报错
public static Singleton getInstance(){
	//a------
	if(instance==null){
		//b-------
		synchronized(instance){
			//c------
			if(instance==null){
				instance=new Singleton();
			}
		}
	}
	return instance;
}
解释:
Ⅰ	A、B两条线程同时在a处,A、B都判断没有对象,进入b处
Ⅱ	遇到锁,A线程拿到锁,进入c处,判断对象不存在,创建对象,此时线程B只能在b处等待
Ⅲ	A线程创建对象完毕,归还锁,B线程进入c处,发现对象已经存在,退出,不创建对象
  • 懒汉式改进:使用静态内部类维护(常用
public class Singleton{
	private Singleton(){};
	//定义静态内部类,只要被调用时才会加载到方法区
	private static class Inner(){
		private static Singleton instance=new Singleton();
	}
	public static Singleton getInstance(){
		return Inner.instance;
	}
} 
  • 饿汉式
public class Singleton{
	private static Singleton instance;
	Private Singleton(){};
	static{
		instance=new Singleton();
	}
} 

四、适配器模式(消除由于接口不匹配造成的类的兼容性问题,有点像转接头)

  • ps4插头:
public interface ps4{
	void isPs4();
}
  • 一台拥有usb插头的机器
public class Usber{
	void isUsb(){
		System.out.println("拥有usb插头");
	}
}
  • 类适配器
    解决问题:我拥有的机器是USB插头,但是现在插孔是ps4的
    解释:设计新机器Adapter,不但继承USB特性(继承),还额外设计了PS4插头!(实现接口)
public class Adapter extends Usber implements ps4{
	void isPs4(){
		System.out.println("拥有ps4插头");
	}
}
  • 对象适配器
    解决问题:我拥有的机器是USB插头,但是现在插孔是ps4的
    解释:将我的机器Usber包起来(内置对象)!,USB插头仍然用Usber的,然后再添加新的ps4插头!
    Adapter里面包裹着一个usber,usb插头直接用它的就行,实现ps4接口,获得新的Ps4插头!
public class Adapter implements ps4{
	private Usb usb;
	public Adapter(Usb usb){
		this.usb=usb;
	}	
	void isUsb(){
		usb.isUsb();
	}	
	void isPs4(){
		System.out.println("拥有ps4插头");
	}
}

③接口适配器(区别于前两者)

public interface A{
	void a();
	void b();
	void c();
}

public abstract class Adapter implement A{
	void a();
	void b();
	void c();
}

public Target extends Adapter{
	void a(){
		System.out.println("目标方法a");
	}
}

有一个接口,存在非常多方法,如果直接实现这个接口,需要重写全部方法,但是我只想重写其中一个方法,于是设计中间的抽象适配器类(有点像装饰者模式的模板类),然后让目标类继承适配器类,重写我要的哪个方法就可以了
个人看法:JDK的HttpServletRequest等接口类提供了一个模板类HttpServletRequestWrapper,用的就是这个接口适配器

Spring应用:SpringMVC应用到了适配器模式

请求访问

前端DispatcherServlet判断该请求的URL是否符合处理要求,如果是,进行处理

通过HandlerMapping起到路由作用,找到对应的处理器Handler

通过框架级接口HandlerAdapter对其进行封装,再通过统一的适配器接口执调用handler方法

而handler方法底层通过java反射调用对应的方法返回视图逻辑名或ModelAndView对象(该对象包含视图逻辑名和模型数据对象)给到前端DispatcherServlet

DispatcherServlet调用视图解析器ViewResolver对其解析,将视图逻辑名解析成真实视图对象

对数据模型对象进行渲染成相应的资源,作为响应返回给请求

五、装饰者模式(增强方法,装饰类持有被装饰类的对象引用,一般在要增强一个类的方法,但是却不知道其类名的时候使用,毕竟如果知道其类名,直接继承该类重写目标方法即可)

前置代码:

  • Person接口,拥有方法eat,sleep
public interface Person{
    void eat();
    void sleep();
}
  • Programmer类,实现了Person接口,重写方法
public class Programmer implements Person{
    public void eat() {
        System.out.println("快速吃饭");
    }
    public void sleep() {
        System.out.println("睡得少,经常通宵");
    }
}
  • 工具类,用于获得Person接口实现类的对象,这样就可以隐藏Person引用的实现类是哪个
public class Utils {
    public static Person getPerson(){
        return new Programmer();
    }
}
  • main方法
public static void main(String[] args) {
	Person person= Utils.getPerson();
	person.eat();
}
需求:此时想增强eat()方法,但是又不知道person引用的对象属于哪个类
  • 声明装饰类,持有被装饰类的引用
public class PersonWrapper implements Person{
    Person person;
    public PersonWrapper (Person person){
        //装饰类持有被装饰类的引用
        this.person=person;
    }
    //需要增强的方法做改造(有点像静态代理)
    public void eat() {
        System.out.println("喝杯奶茶");
        person.eat();
    }
    //不需要增强的方法直接引用原有方法
    public void sleep(){
        person.sleep();
    }
}
  • main方法
public static void main(String[] args) {
	//构造的是装饰类
    Person person= new PersonWrapper(Utils.getPerson());
    person.eat();
}

Spring中找不到应用,但是JDK中有,比如HttpServletrequestWrapper等类

六、代理模式

  • 前置代码:
  • 声明MemoryDevice接口,有memory方法
public interface MemoryDevice{
	public void memory();
}
  • 声明Upan类,MemoryDevice接口,重写memory方法
public class Upan implements MemoryDevice{
	public void memory(){
		//执行存储过程
	}
}

①静态代理

  • 手动编码代理类Proxy,代理Upan类,所以要持有Upan类的对象,实现MemoryDevice接口
public class Proxy() implements MemoryDevice{
	private Upan upan;	
	//持有被代理类的引用
	private Proxy(Upan upan){
		this.upan=upan;
	}
	public void memory(){
		//在存储前做点事(比如开启事务)
		upan.memory();
		//在存储后做点事(比如提交事务/回滚事务)
	}
}

缺点:
Ⅰ代理类和被代理类实现相同接口,当接口新增方法,两者都要实现该方法,维护繁琐
Ⅱ通过硬编码完成,当代理对象量大的时候,人工将无法胜任

②动态代理

  • 准备调用处理器,指示该如何批量处理方法
public class MyInvocationHandler implements InvocationHandler {
	//持有被代理类对象
    private Upan upan;
    public MyInvocationHandler(Upan upan){
        this.upan=upan;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName=method.getName();
		//执行方法时,判断方法名符合某个要求,则做相应处理
        if("memory".equals(methodName)){
            System.out.println("执行存储过程前做点事");
            method.invoke(upan,args);
        }
        if("method".equals(methodName)){
            System.out.println("执行方法前做点事");
            method.invoke(upan,args);
        }
        return proxy;
    }
}

实现步骤
a) 创建一个InvocationHandler实现类的对象

Upan upan = new Upan();
InvocationHandler ih = new MyInvocationHandler(upan);

b)JVM帮我们动态生产代理类的Class对象

Class<?> proxyClass = Proxy.getProxyClass(
upan.getClass().getClassLoader(), upan.getClass().getInterfaces());

c)通过代理类的Class对象获取其需要InvocationHandler参数的构造器

Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);

d)构造实例,获取代理对象
MemoryDevice memoryDevice= (MemoryDevice) constructor.newInstance(ih);
以上4步可以通过MemoryDevice memoryDevice= (MemoryDevice)Proxy.newProxyInstance(upan.getClass().getClassLoader(),upan.getClass().getInterfaces(),ih);一步完成,底层封装的就是以上4步
Spring应用:AOP底层就是通过动态代理完成的

七、观察者模式

  • 声明观察者接口
public interface Observer {
    void update();
}
  • 观察者一:百度新闻记者
public class BaiduReporter implements Observer{
    public void report() {
        System.out.println("百度新闻:今日......");
    }
}
  • 观察者二:腾讯新闻记者
public class TencentReporter implements Observer {
    public void report() {
        System.out.println("腾讯新闻:今日......");
    }
}
  • 声明会议接口,拥有方法add,del,notifyObservers等方法
public interface Conference {
    void add(Observer observer);
    void del(Observer observer);
    void notifyObservers();
}
  • 声明抽象实现类会议,重写方法,让方法add邀请记者,del删除记者,notifyObservers通知所有记者等方法
public abstract class AbstractConference implements Conference {
    private List<Observer> observers = new ArrayList<Observer>();
    public void add(Observer observer){
        observers.add(observer);
    }
    public void del(Observer observer){
        observers.remove(observer);
    }
    public void notifyObservers(){
        for (Observer o:observers) {
            o.report();
        }
    }
}
  • 声明实现类全国人民代表大会
public class NPC extends AbstractConference {
    public void operation(){
        System.out.println("全国人民代表大会顺利召开");
        notifyObservers();
    }
}
  • main方法
public static void main(String[] args) {
	//创建全国人民代表大会对象
	NPC mySubject = new NPC();
	//邀请腾讯记者(添加观察员一)
	mySubject.add(new TencentReporter());
	//邀请百度记者(添加观察员二)
	mySubject.add(new BaiduReporter());
	//开会,此时被邀请的记者都会执行被notifyObservers();通知
	//通知调用了每个观察员的report方法进行报道
	mySubject.operation();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值