设计模式学习笔记

单例设计模式

单例设计模式保证全局只有一个实例,通常用于资源的共享,比如 spring 中的 bean 默认 就是单例的,所有类注入的对象都是同一个。

在类中绑定一个静态的资源也是单例模式的体现,用来存储一些数据。

业务场景,之前做爬虫的时候需要缓存每个用户访问的地址,用一个 map 去缓存,但是也是存风险的,如果用户量很大,那么就会导致内存的溢出。线上就是报 OOM 的问题

public class DataCache {
    // 静态资源,所有实例共享
    private static Map<String, Object> cache = new HashMap<>();

    // 获取缓存中的数据
    public static Object get(String key) {
        return cache.get(key);
    }

    // 设置缓存数据
    public static void put(String key, Object value) {
        cache.put(key, value);
    }
}

单列设计模式有两种实现方式,一个是饿汉设计, 一个是懒汉设计。

饿汉设计是类加载的时候就进行了初始化,懒汉设计是请求过来的时候才会实例化。

面试的时候一般会考察双重校验锁去写单例设计模式。

懒汉单例设计模式,只有在程序进来的时候才会去初始化对象。并且存在线程的安全问题,多个线程同时进来,发现 instance 都为空,那么都会创建对象,这样就不能保证是单例的了。

public  static Sing instance =null;
public static Sing  getInstance(){
    if (instance==null){
        instance=new Sing();
    }
    return instance;
}

饿汉单例设计模式:随着类的加载而加载,有些可能没有用到就加载了,消耗系统的资源。

    public static Sing instance = new Sing();
    private Sing() {
    }

    public static Sing getInstance() {
        return instance;
    }
    
    public void sing() {
        System.out.println("正在唱歌...");
    }

正常使用饿汉单例模式,使用双重校验锁来解决问题。

//双重校验所   为什么要用volatile关键字?
//volatile关键字的作用是:当多个线程同时操作一个变量时,可以保证该变量的可见性,即一个线程修改了变量的值,其他线程能立即得知。直接从内存中读写,不从工作区。
volatile还可以聊到JMM内存模型
//volatile关键字主要用于解决指令重排序的问题,禁止指令重排序并且
public static volatile Sing instance = null;

private Sing() {

}

//为什么 getInstance() 方法加了同步锁?
//因为 getInstance() 方法需要保证线程安全,即多个线程同时调用 getInstance() 方法时,只有一个线程能成功地创建实例,其他线程都需要等待。
//为了保证线程安全,需要在 getInstance() 方法上加同步锁。  在最外面用一个null减少每一个请求过来都加同步锁消耗系统的资源
public static Sing getInstance() {
    if (instance == null) {
        synchronized (Sing.class) {
            if (instance == null) {
                instance = new Sing();
            }
        }
    }
    return instance;
}

第一个 instance==null,避免了所有的请求都去获取锁,只有在对象为空的情况下面采取获取锁。第二个 null 就是对象为空需要新建一个对象出来。

策略模式

策略模式用来简化大量的 if -else 的判断,将想要实现的算法封装在接口中,具体的实现交给不同的实现类去现实,提供一个 StrategyContext 用来判断选择哪一个类去执行,后面需要扩展的时候只需要新建一个类,去实现这个接口,即可。符合了开闭原则,业务逻辑修改的时候,原来的代码不会被修改。

接口提供一个方法,具体的实现交给子类去实现

public interface FindType {

    public String find();
}
public class FontFindType  implements FindType{
    @Override
    public String find() {
        return "fontFindType";
    }
}
public class MaterFindType implements FindType{
    @Override
    public String find() {
        return "materialFindType";
    }
}

策略类的封装,传入接口的对象(使用了多态的特性),提供一个方法给外部去调用。

//策略模式的封装类
public class StrateFactory {
    public  FindType strategy;
    public  StrateFactory(FindType strategy) {
        this.strategy = strategy;
    }
    public String find() {
        return  strategy.find();
    }
}

策略类的实现

package 策略模式;

public class Controller {

    public static String  test(){
        //如果每一次都需要加一个类型, 都会破坏Controller的封装性
        FindType findType = new FontFindType();  //创建一个FindType对象 使用多态的方式
        StrateFactory strateFactory = new StrateFactory(findType);
        return   strateFactory.find();
    }

    public static void main(String[] args) {
        System.out.println(test());
    }
}

根据传入不同的对象返回每一个具体的对象的实现。

代理设计模式

使用代理对象来表示真实的对象,在不改变原来对象的情况下面可以给原来对象加一些内容。

主要作用是拓展目标对象的内容,可以做一些日志、缓存、事务等,真正的业务还是由原来的类去完成,代理类提供了一些公共的服务。

代理又分为静态代理和动态代理

静态代理,两个类都要实现这个接口,将要代理的对象传入要代理类中去,重写代理方法,进行增强处理

public interface WXService {
    public  void  sendMsg(String msg);
}
public class WXServiceImpl  implements WXService {
    @Override
    public void sendMsg(String msg) {
        System.out.println("WXServiceImpl sendMsg: "+msg);
    }
}
public class WXProxy  implements WXService{
    private WXService service;
    public WXProxy(WXService service){
        this.service = service;
    }
    @Override
    public void sendMsg(String msg) {
        System.out.println("代理之前操作");
        service.sendMsg(msg);
        System.out.println("代理之后操作");
    }

    public static void main(String[] args) {
        WXProxy proxy = new WXProxy(new WXServiceImpl());
        proxy.sendMsg("hello");
    }
}

静态代理实现较为简单,但是每一个代理对象都要去创建一个类较为繁琐,可以使用动态代理创建,只需要一个代理类即可

动态代理 是基于反射的,通过反射动态获取类的信息和方法。

动态代理的核心 一个是 InvocationHandler 接口和 Proxy 类,

Proxy 用来 new 一个代理对象。三个参数,ClassLoader 加载代理对象,Interfaces 代理类提供的接口 信息,h 实现 InvocationHanlder 接口的对象

Car 接口,包含了 run 方法。

public interface Car {
    void run();
}

有一个小米 car 实现了 Car 的接口,打印时速两百

package 代理设计模式.动态代理;

public class XMCar implements Car {
    private   String name;

    public XMCar() {
    }

    public XMCar(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println("小米汽车 时速 200km/h");
    }
}

实现 InvocationHandler 接口

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class CarInvocationHandler implements InvocationHandler {
    private Object target;

    public CarInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //拿到代理对象的属性
        Field name = target.getClass().getDeclaredField("name");
        name.setAccessible(true);
        System.out.println(name.get(target)+"正在被购买");
        //调用这个mthod方法
        method.invoke(target);
        return  null;
    }
}

通过 CarFactory 生成应的代理对象,调用代理对象的方法

import java.lang.reflect.Proxy;

public class CarFactory {
    public static Object  getProxy(Object obj){
        CarInvocationHandler handler=new CarInvocationHandler(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
    }
    public static void main(String[] args) {
        Car car=new XMCar("小米汽车");
        Car proxyCar=(Car)getProxy(car);
        proxyCar.run();
    }
}

新增加一个汽车类 只需要调用 getProxy 生成 代理类即可,不用修改原来的类,符合开闭原则。

Proxy 的核心类 用来创建一个代理对象的,传入三个参数有,loader 类加载器,用来加载代理对象,代理对象实现的接口数组,实现了 InvocationHandler 的接口。

自定义处理逻辑,传入 InvocationHandler 来自定义处理逻辑。 Proxy 动态生成代理类、Mehto 代理类对象的方法,args 方法的参数。

这是是 JDK 的动态代理,还有一种是 CGLIB 动态代理,JDK 代理只能代理实现接口的类或者直接代理接口,而 CGLIB 可以代理任何的类。

建造者模式

很多时候创建对象都通过 XXXBuilder 创建的,

比如创建这个对象

  LaunchOptions options = LaunchOptions.builder().headless(false).timeout(30000).executablePath(chromePath).build();

一个对象由多个参数的时候可以使用静态内部类来简化创建。

package design_patterns.建造者模式;

public class HttpRequest {
    private String url;
    private String method;
    private String body;

    @Override
    public String toString() {
        return "HttpRequest{" +
                "url='" + url + '\'' +
                ", method='" + method + '\'' +
                ", body='" + body + '\'' +
                '}';
    }

    public HttpRequest(Builder builder) {
        this.url = builder.url;
        this.method = builder.method;
        this.body = builder.body;
    }

    public static class Builder {
        private String url;
        private String method;
        private String body;

        public Builder setUrl(String url) {
            this.url = url;
            return this;
        }

        public Builder setMethod(String method) {
            this.method = method;
            return this;
        }

        public Builder setBody(String body) {
            this.body = body;
            return this;
        }

        public HttpRequest build() {
            return new HttpRequest(this);
        }
    }

    public static void main(String[] args) {
        Builder builder = new Builder();
        builder.setUrl("http://www.baidu.com");
        builder.setMethod("GET");
        builder.setBody(null);
        HttpRequest request = builder.build();
        System.out.println(request.toString());
    }
}

或者这样创建 HttpRequest,通过建造者模式,简化了类的创建方式,提高了代码的可阅读性。

    HttpRequest build = new Builder().build();

工厂设计模式

常用的工厂模式包括,简单工厂模、工厂方法模式、抽象工厂模式

简单工厂模式就是根据传入的不同参数来创建对象
定义一个 car 的父类

public class Car {
    public void run() {

    }
}

小米 car 类

public class XMCar extends Car {
    private  String name;
    private Integer speed;

    public XMCar(String name, Integer speed) {
        this.name = name;
        this.speed = speed;
    }

    public XMCar() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getSpeed() {
        return speed;
    }

    public void setSpeed(Integer speed) {
        this.speed = speed;
    }

    public void run() {
        System.out.println("XMCar is running with speed " + speed + " km/h");
    }
}

定义一个工厂类,这个缺点是如果想要加一个奔驰,那么就要修改 factory 的代码,不符合开闭原则。

package design_patterns.工厂设计模式;

public class CarFactory {

    public static Car getCar(String carType) {
        Car car = null;
        if (carType.equals("xiaomi")) {
            car = new XMCar("小米",100);
        }
        return car;
    }

    public static void main(String[] args) {
        Car xmCar = CarFactory.getCar("xiaomi");
        xmCar.run();
    }
}

抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂

#Java#
牛牛的面试专栏 文章被收录于专栏

牛牛的面试专栏,希望自己在25年可以拿到一份大厂的SP Offer 你的点赞和收藏都是我持续更新的动力

全部评论

相关推荐

一、自我介绍与关键词1.&nbsp;请用三个关键词来介绍一下自己。——没有让主包自我介绍,我开始叙述的时候被打断了让我重新用三个关键词介绍自己。才意识到意思是只说三个词就可以(TAT)——于是主包说了数据驱动、用户洞察和跨部门沟通的能力二、技能与数据能力考察1.&nbsp;你平时所说的数据能力,具体是指哪些方面?——接下来开始画风很清奇了,开始揪我简历上的技能验证。1.&nbsp;你平时有用过&nbsp;SQL&nbsp;吗?请听题!如果要统计一个班级里&nbsp;18–20&nbsp;岁的男生有多少人,你会怎么写&nbsp;SQL?为什么要用&nbsp;distinct?(还是蛮简单的)2.&nbsp;你在实习中使用过爬虫吗?你用的是什么&nbsp;Python&nbsp;包?——主包当下真愣住了,平时用的时候也不会关注是什么包,毕竟咱们都是将github项目下载下来直接用的,主打一个能用就行1.&nbsp;&nbsp;爬取大众点评和小红书时,你是如何绕过反爬机制的?——又一个愣住了的问题,只能说人家的项目包设置了访问的频率,并且需要滑块验证,来模拟用户IP执行,而不是脚本执行。(汗流浃背)1.&nbsp;你简历里提到过&nbsp;API,那你能解释一下什么是&nbsp;API&nbsp;吗?——只好用baby&nbsp;speak的表达展开,毕竟我脑子里没有专业定义。——相当于是不同系统之间互相通信的数据接口(展开说了使用场景),API&nbsp;本质上是把人工操作变成机器可读的内容。1.&nbsp;接入&nbsp;API&nbsp;时,除了&nbsp;access&nbsp;key&nbsp;之外,还需要提供哪些参数?——没听清于是多次询问了(os:紧张!)——详细阐述在配置n8n工作流的时候是如何接API&nbsp;key、header、body,再把再把&nbsp;JSON&nbsp;输出到飞书表格的。1.&nbsp;你使用过的自动化工具有哪些?n8n&nbsp;和&nbsp;coze&nbsp;有什么区别?——还好最近在捣鼓!于是从稳定性和支付友好两点展开说了。三、业务理解与运营思维1.&nbsp;你在简历里提到过&nbsp;MTU&nbsp;和&nbsp;CAC,能解释一下它们的含义吗?2.&nbsp;如果要提升&nbsp;MTU,团队内有哪些常见的方法?除了你实习时做过的事情之外,还有哪些可以优化的点?这些问题问完之后大概20mins,面试官就问“你有什么问题想问我的吗”我懵圈,会不会太快了点,俺还没来聊够呢!于是我这样说:“因为您的面试风格跟其他的面试官确实还是很不太一样的,且您问我的一些问题当中,后续也没有太多的追问。所以就是我可能不太确认说这个岗位它所需的一些能力素养是怎么样的,且候选人进入到这个职位当中他们的主要职责是什么?”对方的解释belike:1.&nbsp;关注简历上是否有夸大的地方,重点验证简历里的内容是否真的做过而不会非常复杂2.&nbsp;关注表达&nbsp;&amp;&nbsp;讲故事能力,看你能否把复杂场景讲清楚3.&nbsp;开始的时候让你用三个关键词来介绍自己,是要看在短时间的交流当中如何体现这三个素养的其他反问:1.&nbsp;在日常商品运营工作中,需求通常从哪里来?2.&nbsp;部门的目标主要是什么?是&nbsp;GMV&nbsp;吗?3.&nbsp;作为校招生进入之后,这个岗位的具体工作职责是什么?主要会和哪些部门沟通?需要输出哪些内容?4.&nbsp;关于面试流程:后续的轮次大概是怎样安排的?(被告知不晓得QAQ)
查看13道真题和解析
点赞 评论 收藏
分享
吴offer选手:我会开另一种飞机,能申请飞行补贴吗
点赞 评论 收藏
分享
评论
3
11
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务