几种常见的设计模式

1 单例

构造器私有化

用类方法暴露getInstance()

1.1 饿汉

public class Sington1 {
    private static Sington1 instance = new Sington1();

    private Sington1() {

    }

    public Sington1 getInstance() {
        return instance;
    }
}
复制代码

1.2 懒汉

public class Sington2 {
    private static volatile Sington2 instance = null;

    private Sington2() {
    }

    public static Sington2 getInstance() {
        if (instance == null) {
            synchronized (Sington2.class) {
                if (instance == null) {
                    instance = new Sington2();
                }
            }
        }
        return instance;
    }

}
复制代码

voliatile : 防止指令重排
new 一个对象可以分为三步:

1.分配内存(栈指针指向null),

2.构造器初始化

3.将栈内指针指向对象

,如果没有加 voliatile,可能导致指令重排,第一个线程的 2,3 重排,这时候线程1还在锁里面,线程2进去获取,发现instance!=null(这时候有内存,但是没初始化),这时候存在问题

2 工厂

2.1 简单工厂

public class PhoneFactory {
    public Phone makePhone(String phoneType) {
        if(phoneType.equalsIgnoreCase("MiPhone")){
            return new MiPhone();
        }
        else if(phoneType.equalsIgnoreCase("iPhone")) {
            return new IPhone();
        }
        return null;
    }
}
复制代码

image.png
存在问题:
当需要新的 producet的时候,需要修改 Factory 里面的if 判断语句,进行新增,长此以往,Factory将变得很臃肿

2.2 工厂方法

image.png
简单来说,就是一个工厂生产一种产品,当需要一个新的产品的时候,就去实现工厂类的接口,创建一个新的工厂类,专门用于生产某种产品。

存在问题:
工厂类爆炸,会生产大量的工厂类用于生产指定的产品

2.3 抽象工厂

image.png

将大量的工厂进行细分和抽象,提取其共性

简单理解就是,在工厂方法的时候,如果有要生产 A鼠标 A键盘 A显示器 B鼠标 B键盘 B显示器,需要6个工厂类,而抽象工厂用品牌进行区分 A工厂生产 A鼠标 A键盘 A显示器, B工厂 生产 B鼠标 B键盘B显示器

3 建造者

采用组合的形式,创建一个成员内部类进行组装,代码一看就懂了

public class Computer {
    private final String cpu;//必须
    private final String ram;//必须
    private final int usbCount;//可选
    private final String keyboard;//可选
    private final String display;//可选

    private Computer(Builder builder){
        this.cpu=builder.cpu;
        this.ram=builder.ram;
        this.usbCount=builder.usbCount;
        this.keyboard=builder.keyboard;
        this.display=builder.display;
    }
    public static class Builder{
        private String cpu;//必须
        private String ram;//必须
        private int usbCount;//可选
        private String keyboard;//可选
        private String display;//可选

        public Builder(String cup,String ram){
            this.cpu=cup;
            this.ram=ram;
        }

        public Builder setUsbCount(int usbCount) {
            this.usbCount = usbCount;
            return this;
        }
        public Builder setKeyboard(String keyboard) {
            this.keyboard = keyboard;
            return this;
        }
        public Builder setDisplay(String display) {
            this.display = display;
            return this;
        }        
        public Computer build(){
            return new Computer(this);
        }
    }
  //省略getter方法
}
复制代码
Computer computer=new Computer.Builder("因特尔","三星")
                .setDisplay("三星24寸")
                .setKeyboard("罗技")
                .setUsbCount(2)
                .build();
复制代码

4 适配器

说白了,就是增加一个中间类,实现两个原本不能互相调用的方法的相互调用

比如:
app中有一个登录接口,现在有一个新的需求,需要记录登录用户的ip信息,这个在原有的接口中并没有这个参数,所以就需要添加一个ip的参数字段。

但是,如果直接在接口上改动,这样之前的app就会出现不兼容的情况,这个时候,就可以使用适配器模式来解决这个问题了,实现新老代码兼容。

5 观察者

用一个 管理者,去管理所有的观察者,有事件发生的时候,遍历管理者列表进行通知,可以进行特定事件特定通知

//观察者
public interface Observer {
    public void update();
}

//被观察者
abstract public class Subject {

    private List<Observer> observerList = new ArrayList<Observer>();

    public void attachObserver(Observer observer) {
        observerList.add(observer);
    }

    public void detachObserver(Observer observer){
        observerList.remove(observer);
    }

    public void notifyObservers(){
        for (Observer observer: observerList){
            observer.update();
        }
    }
}
spirng中的事件派发器
Naocs中的异步修改(下线什么这列的)
复制代码

6 责任链

netty,AOP,sentinel, sprngmvc

就是一个方法调用完了,去调用另一个方法

public class FilterChain {
    List<Filter> filters = new ArrayList<>();

    public FilterChain() {
        filters.add(new FilterEgg());
        filters.add(new FilterAoBing());
        filters.add(new FilterBaiCai());
        filters.add(new FilterJiTou());
    }
    public void processData(String data) {
        for (Filter filter : filters) {
            filter.doFilter(data);
        }
    }
}

public class Handler {
    public void handlerRequest(Request request) {
        // 得到请求的数据
        String data = request.getData();
        FilterChain filterChain = new FilterChain();
        // 处理数据
        filterChain.processData(data);
    }
}
复制代码

7 包装

是一种组合模式

  • 第一步:我们有一个Phone接口,该接口定义了Phone的功能

  • 第二步:我们有一个最简单的实现类iPhoneX

  • 第三步:写一个装饰器抽象类PhoneDecorate,以组合(构造函数传递)的方式接收我们最简单的实现类iPhoneX。其实装饰器抽象类的作用就是代理(核心的功能还是由最简单的实现类iPhoneX来做,只不过在扩展的时候可以添加一些没有的功能而已)。

  • 第四步:想要扩展什么功能,就继承PhoneDecorate装饰器抽象类,将想要增强的对象(最简单的实现类iPhoneX或者已经被增强过的对象)传进去,完成我们的扩展!

image-20210618112741955.png

// 装饰器,实现接口
public abstract class PhoneDecorate implements Phone {

    // 以组合的方式来获取默认实现类
    private Phone phone;
    public PhoneDecorate(Phone phone) {
        this.phone = phone;
    }

    @Override
    public void call() {
        phone.call();
    }
}
复制代码

8 策略

内部还是组合模式

image.png

image.png

image.png

image.png

image.png

9 模板方法

重写子类的方法,在模板方法中会去调用该方法

   abstract void initialize();
   abstract void startPlay();
   abstract void endPlay();
 
   //模板
   public final void play(){
 
      //初始化游戏
      initialize();
 
      //开始游戏
      startPlay();
 
      //结束游戏
      endPlay();
   }
}
复制代码
public class Cricket extends Game {
 
   @Override
   void endPlay() {
      System.out.println("Cricket Game Finished!");
   }
 
   @Override
   void initialize() {
      System.out.println("Cricket Game Initialized! Start playing.");
   }
 
   @Override
   void startPlay() {
      System.out.println("Cricket Game Started. Enjoy the game!");
   }
}
复制代码
public class Football extends Game {
 
   @Override
   void endPlay() {
      System.out.println("Football Game Finished!");
   }
 
   @Override
   void initialize() {
      System.out.println("Football Game Initialized! Start playing.");
   }
 
   @Override
   void startPlay() {
      System.out.println("Football Game Started. Enjoy the game!");
   }
}
复制代码
public class TemplatePatternDemo {
   public static void main(String[] args) {
 
      Game game = new Cricket();
      game.play();
      System.out.println();
      game = new Football();
      game.play();      
   }
}
复制代码
  • 把公共的代码抽取出来,如果该功能是不确定的,那我们将其修饰成抽象方法。
  • 将几个固定步骤的功能封装到一个方法中,对外暴露这个方法,就可以非常方便调用了。

AQS中 tryAcquire 重写,会在 acquire 中调用

10 代理

10.1 静态代理

说白了就是组合

10.2 动态代理

package 代理模式.动态代理;

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

public class serviceProxy implements InvocationHandler {

    private Object object;

    public serviceProxy(Object object) {
        this.object = object;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("准备代理");
        Object invoke = method.invoke(object, args);
        System.out.println("完成代理");
        return invoke;
    }
}

复制代码
public class Test {
    public static void main(String[] args) throws Throwable {
        service service = new service();
        serviceProxy serviceProxy = new serviceProxy(service);
        serviceInterface ser = (serviceInterface) Proxy.newProxyInstance(service.getClass().getClassLoader(), service.getClass().getInterfaces(), serviceProxy);
        ser.send("123213");
    }
}

复制代码

Proxy.newProxyInstance()返回一个代理对象,其中包括了代理对象、被代理对象
底层是通过jvm指令去生成代理对象的字节码,而后利用传入的 classloader 进行转载进而生成代理对象的类对象,最后通过发射获取构造器constractor创建代理对象的实例

代理模式和装饰者模式区别

功能上面完全一样,都是为了增强原有的功能,如果是静态代理的话,连代码都一样。

设计模式应该从设计层面进行拆分,增强原有功能无非是 继承组合

代理模式 注重的是隔离,只能通过代理对象去访问被代理对象

装饰者模式注重的是拓展,一个方法下面可以实现更多的功能

(参考知乎的一个说法,不过这样子AOP就好像是装饰者模式)

参考

3y

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享