设计模式-单例、策略、代理、建造、工厂

文章目录

单例设计模式

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

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

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

plain 复制代码
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 都为空,那么都会创建对象,这样就不能保证是单例的了。

java 复制代码
public  static Sing instance =null;
public static Sing  getInstance(){
    if (instance==null){
        instance=new Sing();
    }
    return instance;
}

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

plain 复制代码
    public static Sing instance = new Sing();
    private Sing() {
    }

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

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

java 复制代码
//双重校验所   为什么要用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 用来判断选择哪一个类去执行,后面需要扩展的时候只需要新建一个类,去实现这个接口,即可。符合了开闭原则,业务逻辑修改的时候,原来的代码不会被修改。

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

plain 复制代码
public interface FindType {

    public String find();
}
plain 复制代码
public class FontFindType  implements FindType{
    @Override
    public String find() {
        return "fontFindType";
    }
}
plain 复制代码
public class MaterFindType implements FindType{
    @Override
    public String find() {
        return "materialFindType";
    }
}

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

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

策略类的实现

plain 复制代码
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());
    }
}

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

代理设计模式

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

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

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

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

java 复制代码
public interface WXService {
    public  void  sendMsg(String msg);
}
java 复制代码
public class WXServiceImpl  implements WXService {
    @Override
    public void sendMsg(String msg) {
        System.out.println("WXServiceImpl sendMsg: "+msg);
    }
}
java 复制代码
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 方法。

java 复制代码
public interface Car {
    void run();
}

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

java 复制代码
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 接口

java 复制代码
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 生成应的代理对象,调用代理对象的方法

java 复制代码
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 创建的,

比如创建这个对象

java 复制代码
  LaunchOptions options = LaunchOptions.builder().headless(false).timeout(30000).executablePath(chromePath).build();

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

java 复制代码
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,通过建造者模式,简化了类的创建方式,提高了代码的可阅读性。

java 复制代码
    HttpRequest build = new Builder().build();

工厂设计模式

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

简单工厂模式就是根据传入的不同参数来创建对象

定义一个 car 的父类

java 复制代码
public class Car {
    public void run() {

    }
}

小米 car 类

java 复制代码
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 的代码,不符合开闭原则。

java 复制代码
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)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂

相关推荐
lpfasd1238 分钟前
状态模式(State Pattern)
java·设计模式·状态模式
代码老y12 分钟前
前端开发中的可访问性设计:让互联网更包容
java·服务器·前端·数据库
jakeswang14 分钟前
Java 项目中实现统一的 追踪ID,traceId实现分布式系统追踪
java·后端·架构
寒山李白19 分钟前
Java 传输较大数据的相关问题解析和面试问答
java·开发语言·面试·传输
白总Server31 分钟前
Golang dig框架与GraphQL的完美结合
java·大数据·前端·javascript·后端·go·graphql
lightgis1 小时前
个人支出智能分析系统
java
春生野草1 小时前
MyBatis中关于缓存的理解
java·缓存·mybatis
oioihoii1 小时前
C++11 Generalized(non-trivial) Unions:从入门到精通
java·开发语言·c++
颯沓如流星1 小时前
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
java·重构·装饰器模式
惜鸟2 小时前
springboot 项目的包结构设计(一)
java·spring boot