常用的设计模式总结

本文是我学习时候所记笔记,主要
学习自尚硅谷设计模式视频

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 职责链模式

采购员采购教学器材

  1. 如果金额 小于等于 5000, 由教学主任审批 (0<=x<=5000)

  2. 如果金额 小于等于 10000, 由院长审批 (5000<x<=10000)

  3. 如果金额 小于等于 30000, 由副校长审批 (10000<x<=30000)

  4. 如果金额 超过 30000 以上,有校长审批 ( 30000<x)

请设计程序完成采购审批项目

3.1 普通

通过 if 判断范围逐个调用handler

3.2 职责链

image-20201205155406129

3.3 个人总结

当类似有多个if结构出现时,可以考虑用职责链模式,有点类似于链表遍历,这样能提高程序的解耦性,容易增删改,但可能需要注意性能方面,因为遍历太长的链,会耗费太大的性能,职责链在springmvc,springsecurity 均有使用。

4 建造者模式

  1. 需要建房子:这一过程为打桩、砌墙、封顶

  2. 房子有各种各样的,比如普通房,高楼,别墅,各种房子的过程虽然一样,但是要求不要相同的.

  3. 请编写程序,完成需求.

4.1 普通

image-20201205161959673

4.2 建造者

image-20201205164202181

强调建造的过程,而不是一步就new出来得到产品,建造的过程可以控制,可以按需建造。

在stringBuilder源码中有使用到

4.3 个人总结

建造者模式强调过程,而不是像普通模式一样直接new出来,强调过程的好处是不会遗漏某个步骤,类似于做菜,做菜的需要按顺序来切菜炒菜放盐,利用建造者模式就不会遗漏掉某个步骤。

同时隔离了客户跟产品,客户不需要知道产品的细节,只要直接调用不同的建造者即可得到不同产品

5 工厂模式

5.1 简单工厂

image-20201205210158376
image-20201205211611147

优点:工厂这边的产品修改名字的时候,不会影响到客户端(服务端改下判断逻辑就可以得到相同结果)

在需要扩展产品时候,要改工厂和产品的源码,很麻烦,不符合开闭原则(扩展开放)

5.2 工厂模式

image-20201205213821670

image-20201205213857423
优点:

1.仍然具有简单工厂的优点:服务器端修改了具体产品的类名以后,客户端不知道下层细节!

2.当客户端需 要扩展一个新的产品时,不需要修改作者原来的代码,只是扩展一个新的工厂而已!·

杠点:

1.我们已经知道,简单工厂也好,工厂方法也好,都有一 -个优点,就是服务器端的具体产品类名变化了以后,客户端不知道!
但是,反观我们现在的代码,客户端仍然依赖于具体的工厂的类名呀!此时,如果服务器端修改了具体工厂的类名,那么客户端也要随之一起修改!感觉折腾了一圈,又回到了原点! ! !

解释:工厂的名字,是为视为接口的。作者有责任,有义务,保证工厂的名字是稳定的。也就是说,虽然客户端依赖于工厂的具体类名,可是在IT业内,所有工厂的名字都是趋向于稳定(并不是100%不会变)。至少工厂类的名字,要比具体产品类的名字更加稳定!

2.既然产品是我们自己客户端扩展出来的,那为什么不直接自己实例化呢?毕竟这个扩展出来的Lp这个产品,我们自己就是作者。我们想怎么改类名自己都能把控! 为什么还要 为自己制作的产品做工厂呢?
解释: 因为作者在开发功能时,不仅仅只会开发一些抽象产品、 具体产品、对应的工厂,还会配套地搭配一一些提 前做好的框架。

3.现在制作出LpFactory, 是为了能把LpFactory传入给Bussiness. taste方法,所以,必须定义这个LpFactory.那为什么不从一-开始,就让Bussiness. taste方法就直接接受Food参数呢?而不是现在的FoodF actory作为参数。

解释:一开始之所以用工厂,是希望在食品的名字改变时候,客户端名字不要跟着改,现在参数是food,那一旦产品名字修改了,客户端这边也得改。

缺点:

产品一多,产品类和子工厂类的个数就要爆炸式增长了

5.3 抽象工厂

image-20201205220640215
image-20201205221303523

抽象工厂适合于组合产品,比如生产螺丝螺母,或者可乐炸鸡,一个子工厂可以同时生产两个产品,减少类的数量。

6 迭代器模式

6.1 类图

image-20201209133255548
image-20201209133952762

6.2 集合中迭代器源码

image-20201209134348585
image-20201209144239127
AbstractList迭代器中的源码

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 适配器模式

工作原理:类似插头不兼容问题,中间加个转换器(万能充)

  • 类适配器

image-20201209164357279

  • 对象适配器
    image-20201209165233714

  • 接口适配器
    image-20201209170832215
    image-20201209172804728

  • 简略的MVC适配器
    image-20201209172127566

9 策略模式

image-20201209174138731
各种鸭相当于 使用者

行为相当于 算法的提供者

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值