本文是我学习时候所记笔记,主要
学习自尚硅谷设计模式视频
文章目录
1 观察者模式
气象站设计一个开放型API,推送数据给网站,更新气象数据时候,需要同时**告知(推送)**网站
1.1 普通
public class Client {
public static void main(String[] args) {
//创建接入方 currentConditions
CurrentConditions currentConditions = new CurrentConditions();
//创建 WeatherData 并将 接入方 currentConditions 传递到 WeatherData 中
WeatherData weatherData = new WeatherData(currentConditions);
//天气情况变化
System.out.println("============天气情况变化=============");
weatherData.setData(40, 160, 20);
}
}
这样耦合性太强,当有很多个网站需要推送时候,还要逐个网站去推送,不利于维护,也不是动态网站
1.2 观察者
观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象为 Subject,依赖的对象为 Observer,Subject通知 Observer 变化,比如这里的气象站是 Subject,是 1 的一方。用户时 Observer,是多的一方。
观察者接口:observer
//观察者接口,有观察者来实现 public interface Observer {
public void update(float temperature, float pressure, float humidity);
}
气象站需要实现的接口:subject
public interface Subject {
public void registerObserver(Observer o); //注册观察者
public void removeObserver(Observer o); //移除观察者
public void notifyObserver(); //通知观察者
}
气象站本身:能够管理和通知所有的观察者(observcer)
//遍历所有的观察者,并通知 @Override
public void notifyObservers() {
// TODO Auto-generated method stub for(int i = 0; i < observers.size(); i++) {
observers.get(i).update(this.temperatrue, this.pressure, this.humidity);
}
}
主类:
public class Client {
public static void main(String[] args) {
//创建气象站WeatherData
WeatherData weatherData = new WeatherData();
//创建观察者
CurrentConditions currentConditions = new CurrentConditions();
//注册到气象站WeatherData,可以注册很多个
weatherData.registerObserver(currentConditions);
//测试
weatherData.setData(10f,100f,30.3f);
}
}
1.3 总结
总结:在原有基础上,核心类实现一个管理者(subject)的接口,同时管理多个观察者。类似于本来老板跟员工一对一加微信通知任务(需要私加好友,还要逐个通知),现在老板直接建个群,能同时管理多个员工(在群里边操作),使得命令能够及时下达至每个员工,而管理起员工又很方便
2 代理模式
2.1 介绍
-
代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
-
被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象
-
代理模式有不同的形式, 主要有三种 静态代理、动态代理 (JDK 代理、接口代理)和 Cglib 代理 (可以在内存动态的创建对象,而不需要实现接口, 他是属于动态代理的范畴) 。
2.2 静态代理
package com.atguigu.proxy.staticproxy;
//代理对象,静态代理
public class TeacherDaoProxy implements ITeacherDao{
private ITeacherDao target; // 目标对象,通过接口来聚合被代理对象
//构造器
public TeacherDaoProxy(ITeacherDao target) {
this.target = target;
}
@Override
public void teach() {
System.out.println("开始代理 完成某些操作。。。。。 ");//额外方法
target.teach(); //原有的方法
System.out.println("提交。。。。。");//额外方法
}
}
总结:通过实现相同的接口,被代理对象聚合到代理对象里(@Autowired),代理对象可以实现额外的方法,缺点两个对象都是要实现相同的接口,维护麻烦。
2.3 动态代理
package com.atguigu.proxy.dynamic;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建目标对象
ITeacherDao target = new TeacherDao();
//给目标对象,创建代理对象, 可以转成 ITeacherDao
ITeacherDao proxyInstance = (ITeacherDao)new ProxyFactory(target).getProxyInstance();
// proxyInstance=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象
System.out.println("proxyInstance=" + proxyInstance.getClass());
//通过代理对象,调用目标对象的方法
//proxyInstance.teach();
proxyInstance.sayHello(" tom ");
}
}
import java.lang.reflect.Proxy;
public class ProxyFactory {
//维护一个目标对象 , Object private Object target;
//构造器 , 对 target 进行初始化
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象 生成一个代理对象
public Object getProxyInstance() {
//说明
/*
* public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces, InvocationHandler h)
//1. ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定
//2. Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型
//3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行
的目标对象方法作为参数传入
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK 代理开始~~");
//反射机制调用目标对象的方法
//returnVal 方法的返回值
//args 方法的参数
Object returnVal = method.invoke(target, args);
System.out.println("JDK 代理提交");
return returnVal;//方法的返回值
}
});
}
}
总结三点:。。。
2.4 Cglib代理
-
目标对象需要实现接口,用jdk代理
-
目标对象不需要实现接口,用Cglib代理
MethodInterceptor在额外引入的cglib jar包里
//MethodInterceptor在额外引入的cglib jar包里
public class ProxyFactory implements MethodInterceptor {
//维护一个目标对象
private Object target;
//构造器,传入一个被代理的对象
public ProxyFactory(Object target) {
this.target = target;
}
//返回一个代理对象: 是 target 对象的代理对象
public Object getProxyInstance() {
//1. 创建一个工具类
Enhancer enhancer = new Enhancer();
//2. 设置父类
enhancer.setSuperclass(target.getClass());
//3. 设置回调函数
enhancer.setCallback(this);
//4. 创建子类对象,即代理对象
return enhancer.create();
}
//重写 intercept 方法,会调用目标对象的方法
@Override
public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
// TODO Auto-generated method stub
System.out.println("Cglib 代理模式 ~~ 开始");
Object returnVal = method.invoke(target, args);
System.out.println("Cglib 代理模式 ~~ 提交"); return returnVal;
}
}
3 职责链模式
采购员采购教学器材
如果金额 小于等于 5000, 由教学主任审批 (0<=x<=5000)
如果金额 小于等于 10000, 由院长审批 (5000<x<=10000)
如果金额 小于等于 30000, 由副校长审批 (10000<x<=30000)
如果金额 超过 30000 以上,有校长审批 ( 30000<x)
请设计程序完成采购审批项目
3.1 普通
通过 if 判断范围逐个调用handler
3.2 职责链
3.3 个人总结
当类似有多个if结构出现时,可以考虑用职责链模式,有点类似于链表遍历,这样能提高程序的解耦性,容易增删改,但可能需要注意性能方面,因为遍历太长的链,会耗费太大的性能,职责链在springmvc,springsecurity 均有使用。
4 建造者模式
需要建房子:这一过程为打桩、砌墙、封顶
房子有各种各样的,比如普通房,高楼,别墅,各种房子的过程虽然一样,但是要求不要相同的.
请编写程序,完成需求.
4.1 普通
4.2 建造者
强调建造的过程,而不是一步就new出来得到产品,建造的过程可以控制,可以按需建造。
在stringBuilder源码中有使用到
4.3 个人总结
建造者模式强调过程,而不是像普通模式一样直接new出来,强调过程的好处是不会遗漏某个步骤,类似于做菜,做菜的需要按顺序来切菜炒菜放盐,利用建造者模式就不会遗漏掉某个步骤。
同时隔离了客户跟产品,客户不需要知道产品的细节,只要直接调用不同的建造者即可得到不同产品
5 工厂模式
5.1 简单工厂
优点:工厂这边的产品修改名字的时候,不会影响到客户端(服务端改下判断逻辑就可以得到相同结果)
在需要扩展产品时候,要改工厂和产品的源码,很麻烦,不符合开闭原则(扩展开放)
5.2 工厂模式
优点:
1.仍然具有简单工厂的优点:服务器端修改了具体产品的类名以后,客户端不知道下层细节!
2.当客户端需 要扩展一个新的产品时,不需要修改作者原来的代码,只是扩展一个新的工厂而已!·
杠点:
1.我们已经知道,简单工厂也好,工厂方法也好,都有一 -个优点,就是服务器端的具体产品类名变化了以后,客户端不知道!
但是,反观我们现在的代码,客户端仍然依赖于具体的工厂的类名呀!此时,如果服务器端修改了具体工厂的类名,那么客户端也要随之一起修改!感觉折腾了一圈,又回到了原点! ! !
解释:工厂的名字,是为视为接口的。作者有责任,有义务,保证工厂的名字是稳定的。也就是说,虽然客户端依赖于工厂的具体类名,可是在IT业内,所有工厂的名字都是趋向于稳定(并不是100%不会变)。至少工厂类的名字,要比具体产品类的名字更加稳定!
2.既然产品是我们自己客户端扩展出来的,那为什么不直接自己实例化呢?毕竟这个扩展出来的Lp这个产品,我们自己就是作者。我们想怎么改类名自己都能把控! 为什么还要 为自己制作的产品做工厂呢?
解释: 因为作者在开发功能时,不仅仅只会开发一些抽象产品、 具体产品、对应的工厂,还会配套地搭配一一些提 前做好的框架。
3.现在制作出LpFactory, 是为了能把LpFactory传入给Bussiness. taste方法,所以,必须定义这个LpFactory.那为什么不从一-开始,就让Bussiness. taste方法就直接接受Food参数呢?而不是现在的FoodF actory作为参数。
解释:一开始之所以用工厂,是希望在食品的名字改变时候,客户端名字不要跟着改,现在参数是food,那一旦产品名字修改了,客户端这边也得改。
缺点:
产品一多,产品类和子工厂类的个数就要爆炸式增长了
5.3 抽象工厂
抽象工厂适合于组合产品,比如生产螺丝螺母,或者可乐炸鸡,一个子工厂可以同时生产两个产品,减少类的数量。
6 迭代器模式
6.1 类图
6.2 集合中迭代器源码
ArrayList中迭代器Itr源码
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification(); //检查集合是否被改变过
int i = cursor;
if (i >= size) //检查长度
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1; //右指针指向的元素 (第i+1个)
return (E) elementData[lastRet = i]; //返回左指针指向的元素(第i个)
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet); //删除左指针指向的元素
cursor = lastRet; //当前指针往右一位
lastRet = -1; //左指针置空,所以不能连续remove
expectedModCount = modCount; //用来检查集合是否被改变的函数
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
6.3 优缺点
优点:
为每个list实现一个子迭代器,上层调用迭代器的时候不必知道下层的遍历细节(不用理会是用数组还是链表存储)
下层可以实现多种数据存储形式,同时不会影响到上层
7 单例模式
7.1 饿汉式
-
静态常量
class Singleton{ private Singleton(){} //构造器私有化,不让外部new,只能在内部new private final static Singleton instance = new Singleton(); //在类加载的时候就完成实例化 public static Singleton getInstance(){ //提供一个公共方法,可以获取实例 return instance; } }
-
静态代码块
class Singleton{ private Singleton(){} //构造器私有化,不让外部new,只能在内部new private static Singleton instance; static{ instance = new Singleton();// 在静态代码块中完成实例化 } public static Singleton getInstance(){ //提供一个公共方法,可以获取实例 return instance; } }
7.2 懒汉式
-
线程不安全
class Singleton{ private Singleton(){} //构造器私有化,不让外部new,只能在内部new private static Singleton instance; public static Singleton getInstance(){ //提供一个公共方法,可以获取实例 if(instance == null) //还没有创建,第一次创建 instance = new Singleton(); return instance; //已经有了,直接返回 } }
缺点:有可能同时多个线程进行if判断,造成错乱
-
线程安全
在线程不安全的方法上,再加一个synchronized锁住
public static synchronized Singleton getInstance(){ //提供一个公共方法,可以获取实例 if(instance == null) //还没有创建,第一次创建 instance = new Singleton(); return instance; //已经有了,直接返回 }
缺点:效率太低,每次获取实例都得同步一下
7.3 双重检查(推荐)
class Singleton{
private Singleton(){} //构造器私有化,不让外部new,只能在内部new
private static volatile Singleton instance; //注意要加上volatile
public static Singleton getInstance(){
if(instance == null) {// 第一次检查
synchronized(Singleton.class){ //锁住
if(instance == null){ //第二次检查
instance = new Singleton();
}
}
}
return instance; //已经有了,直接返回
}
}
7.4 静态内部类(推荐)
class Singleton{
private Singleton(){} //构造器私有化,不让外部new,只能在内部new
private static class SingletonInstance(){
private static final Signleton INSTANCE = new Signleton();
}
public static Signleton getInstance(){
return SignletonInstance.INSTANCE;
}
}
优点:不会提前实例化,而且JVM装载类的时候线程安全
7.5 枚举(推荐)
Singleton instance = Singleton.INSTANCE;
Singleton instance2 = Singleton.INSTANCE;
enum Singleton{
INSTANCE;//属性
.....
.....
}
最优解,理由:
- 前面的方法中私有构造器并不保险,可以通过反射得到新的实例
- 前面的方法中,序列化前后两个对象并不相等
- 写法简单
但是,并没有延迟实例化,不能保证只有一个实例(可以自己new一个)
8 适配器模式
工作原理:类似插头不兼容问题,中间加个转换器(万能充)
- 类适配器
-
对象适配器
-
接口适配器
-
简略的MVC适配器
9 策略模式
各种鸭相当于 使用者
行为相当于 算法的提供者