1 Star 0 Fork 0

Paul_Zhen / java-review

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
设计模式.md 23.77 KB
一键复制 编辑 原始数据 按行查看 历史
zhenpeng 提交于 2023-12-11 13:34 . 分布式等篇章总结

高内聚,低耦合啥意思,如何去设计

设计模式分三种

创建型模式:对对象创建过程的各种问题和解决方案的总结。如各种工厂模式、单例模式、建造者模式

结构型模式:针对软件设计结构的总结,关注类、对象继承、组合方式的实践经验。如装饰者模式、代理模式

行为型模式:是从类或对象之间交互、职责划分等角度总结的模式。如策略模式、模版方法模式、责任链模式

怎么理解依赖倒置/控制反转/IOC?

实际上就是一个好莱坞原则的体现,我使用框架的时候,不需要调用某个方法,只需要实现对应的接口,让框架来调用我即可。

如spring框架里写set方法的依赖注入(DI);junit里不需要调用方法,只需要写个testcase,junit就可以调用我;还有servlet,我继承一个servlet 接口实现service方法即可,它自然会调用我。

spring的设计模式?

spring aop的实现,通过底层的aop-proxy实现动态代理。

beanFactory工厂模式

bean的默认作用域是单例模式

jdbcTemplate resttemplate 模版方法

jdk的设计模式?

IO里的inputstream的包装,synchronizeList的装饰者模式

swing编程里的组合模式

java.lang.reflect.Proxy的代理模式

java.util.EventListener 观察者模式

java.lang.Runtime#getRuntime() 单例模式

mybatis 设计模式 工厂模式 建造者模式 jdk动态代理 (MapperProxy执行sql语句的前面增加自己的逻辑)

设计模式的应用?

用构造者模式做参数构建生成接口的对象。

策略模式实现不同erp类型的接口调用。+工厂模式。

模板方法模式+钩子函数回调实现固定步骤。


创建型模式:

单例模式

优点:

1.提供了对唯一实例的受控访问。

2.因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。

饿汉

public class Singleton {
        private static Singleton instance = new Singleton();
        private Singleton() {
        }
        public static Singleton getInstance() {
        	return instance;
        }
    }

延迟加载版本 懒汉

public class Singleton {
        private static Singleton instance;
        private Singleton() {
        }
        public static Singleton getInstance() {
            if (instance == null) {
            instance = new Singleton();
            }
        return instance;
        }
    }

线程安全的延迟加载版本,双检锁:

public class Singleton {
	//https://cloud.tencent.com/developer/article/1161095,避免重排序,使得另一个线程获得了一个不可用的对象引用(分配了内存地址,但是还未初始化),然后执行后续的逻辑。
	//也就是说使用了volatile关键字,保证了singleton对象的写操作(此处的new操作happens-before于读操作(获取引用)。
	//new不是原子性的,分以下三步;返回地址给引用可能会发生重排序
	(1) 给DoubleCheckSingleton类的实例instance【分配内存】
	(2) 调用实例instance的【构造函数来初始化成员变量】
	(3) 将instance指向【分配的内存地址】
	//happens-before原则之一:对于 volatile 变量,对它的写操作,保证 happen-before 在随后对该变量的读取操作。
	
  private static volatile Singleton singleton = null;
  private Singleton() {
  }

  public static Singleton getSingleton() {
  		
      if (singleton == null) { // 第一层检查,当初始化好了就没必要再进入同步块,主要是为了减少开销。
      //当线程b执行到此处,准备进入同步块。
          synchronized (Singleton.class) { //同步.class,意味着对同步类方法调用,确保所有Singleton对象共用同一把锁
              if (singleton == null) {//第二层检查的意义是,当线程a正在执行同步块过程中,线程b也通过了第一层检查。当后续a释放锁后,b再进入就需要第二层检查,防止b再次new一个新对象,而违反了单例。
                  singleton = new Singleton();
              }
          }
      }
      return singleton;
  }
}

https://xie.infoq.cn/article/88c926822394aa1c80847dd2a

工厂模式(BeanFactory和ApplicationContext的getBean方法就是工厂方法)

建造者模式

https://time.geekbang.org/column/article/199674?code=niATWQ6EU8pXKP9GvljUrr0mcnaAoiB7MRSYW4FybGw&utm_term=zeusFGBF3&utm_source=zhihu&utm_medium=zhihu&utm_campaign=126-end&utm_content=textlink0417

场景:替代new和set的方式,在创造实例时候对构造函数的参数增加一些约束,比如某个参数是必须填写的,其他的可以不填写,比如其中参数之间有依赖约束,做一些参数校验等等,这是构造函数不具备的能力。


public class ResourcePoolConfig {
  private String name;
  private int maxTotal;
  private int maxIdle;
  private int minIdle;

  private ResourcePoolConfig(Builder builder) {
    this.name = builder.name;
    this.maxTotal = builder.maxTotal;
    this.maxIdle = builder.maxIdle;
    this.minIdle = builder.minIdle;
  }
  //...省略getter方法...

  //我们将Builder类设计成了ResourcePoolConfig的内部类。
  //我们也可以将Builder类设计成独立的非内部类ResourcePoolConfigBuilder。
  public static class Builder {
    private static final int DEFAULT_MAX_TOTAL = 8;
    private static final int DEFAULT_MAX_IDLE = 8;
    private static final int DEFAULT_MIN_IDLE = 0;

    private String name;
    private int maxTotal = DEFAULT_MAX_TOTAL;
    private int maxIdle = DEFAULT_MAX_IDLE;
    private int minIdle = DEFAULT_MIN_IDLE;

    public ResourcePoolConfig build() {
      // 校验逻辑放到这里来做,包括必填项校验、依赖关系校验、约束条件校验等
      if (StringUtils.isBlank(name)) {
        throw new IllegalArgumentException("...");
      }
      if (maxIdle > maxTotal) {
        throw new IllegalArgumentException("...");
      }
      if (minIdle > maxTotal || minIdle > maxIdle) {
        throw new IllegalArgumentException("...");
      }

      return new ResourcePoolConfig(this);
    }

    public Builder setName(String name) {
      if (StringUtils.isBlank(name)) {
        throw new IllegalArgumentException("...");
      }
      this.name = name;
      return this;
    }

    public Builder setMaxTotal(int maxTotal) {
      if (maxTotal <= 0) {
        throw new IllegalArgumentException("...");
      }
      this.maxTotal = maxTotal;
      return this;
    }

    public Builder setMaxIdle(int maxIdle) {
      if (maxIdle < 0) {
        throw new IllegalArgumentException("...");
      }
      this.maxIdle = maxIdle;
      return this;
    }

    public Builder setMinIdle(int minIdle) {
      if (minIdle < 0) {
        throw new IllegalArgumentException("...");
      }
      this.minIdle = minIdle;
      return this;
    }
  }
}
// 这段代码会抛出IllegalArgumentException,因为minIdle>maxIdle
ResourcePoolConfig config = new ResourcePoolConfig.Builder()
        .setName("dbconnectionpool")
        .setMaxTotal(16)
        .setMaxIdle(10)
        .setMinIdle(12)
        .build();

与工厂模式有何区别?

实际上,工厂模式是用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象。建造者模式是用来创建一种类型的复杂对象,通过设置不同的可选参数,“定制化”地创建不同的对象

网上有一个经典的例子很好地解释了两者的区别。顾客走进一家餐馆点餐,我们利用工厂模式,根据用户不同的选择,来制作不同的食物,比如披萨、汉堡、沙拉。对于披萨来说,用户又有各种配料可以定制,比如奶酪、西红柿、起司,我们通过建造者模式根据用户选择的不同配料来制作披萨。

结构型模式:

装饰器模式

因为装饰器模式本质上是包装同类型实例,我们对目标对象的调用,往往会通过包装类覆盖过的方法,迂回调用被包装的实例,这就可以很自然地实现增加额外逻辑的目的,也就是所谓的“装饰”。

1.inputStream是一个抽象类,标准类库中提供了FileInputStream、ByteArrayInputStream等各种不同的子类,分别从不同角度对InputStream进行了功能拓展,如BufferedInputStream对传入的InputStream进行增强,此处的close方法。

InputStream in = new FileInputStream("//");
BufferedInputStream bis = new BufferedInputStream(in);
    public void close() throws IOException {
        byte[] buffer;
        while ( (buffer = buf) != null) {
            if (bufUpdater.compareAndSet(this, buffer, null)) {
                InputStream input = in;
                in = null;
                if (input != null)
                    input.close();
                return;
            }
            // Else retry in case a new buf was CASed in fill()
        }
    }

2.Collections 工具类提供的包装方法,来获取一个同步的包装容器(如 Collections.synchronizedList),每个方法进行加锁增强。

public int hashCode() {
    synchronized (mutex) {return list.hashCode();}
}

public E get(int index) {
    synchronized (mutex) {return list.get(index);}
}
public E set(int index, E element) {
    synchronized (mutex) {return list.set(index, element);}
}
public void add(int index, E element) {
    synchronized (mutex) {list.add(index, element);}
}
public E remove(int index) {
    synchronized (mutex) {return list.remove(index);}
}

public int indexOf(Object o) {
    synchronized (mutex) {return list.indexOf(o);}
}
public int lastIndexOf(Object o) {
    synchronized (mutex) {return list.lastIndexOf(o);}
}

代理模式

有接口则走组合实现,没接口则通过继承来实现。

https://time.geekbang.org/column/article/201823?code=niATWQ6EU8pXKP9GvljUrr0mcnaAoiB7MRSYW4FybGw&utm_term=zeusFGBF3&utm_source=zhihu&utm_medium=zhihu&utm_campaign=126-end&utm_content=textlink0417

动态代理,如jdk的实现方式如下

定义接口

package designPattern.composite.proxy;

public interface IPlayer {
    void login();
    void play();
}

实现类

package designPattern.composite.proxy;

/**
 * @author: zhenpeng
 */
public class Player implements IPlayer{
    String name;

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

    public void login(){
        System.out.println(name+"登陆成功!");
    }
    public void play(){
        System.out.println(name+"开始打游戏!");
    }
}

代理类,注意此处对每个被代理类的方法进行了同样的控制操作(装饰器不同,装饰器进行了不同的增强)。

package designPattern.composite.proxy;

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

/**
 * @author: zhenpeng
 */
public class PlayerLogProxy implements InvocationHandler {
    //被代理者
    Class cls = null;
    //被代理的实例
    Object obj = null;
    //我要代理谁
    public PlayerLogProxy(Object _obj){
        this.obj = _obj;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		//对每个方法进行日志操作
        beforeInvokeLog(method);
        Object result = method.invoke(this.obj,args);
        //对每个方法进行日志操作
        afterInvokeLog(method);
        return result;
    }

    private void beforeInvokeLog(Method method){
        System.out.println("--log---"+method.getName()+"方法开始被调用了----");
    }

    private void afterInvokeLog(Method method){
        System.out.println("--log---"+method.getName()+"方法结束调用-----");
    }
}

main

package designPattern.composite.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * @author: zhenpeng
 */
public class Test {
    public static void main(String[] args) {
        IPlayer player = new Player("zhangsan");
        InvocationHandler handler = new PlayerLogProxy(player);
        ClassLoader cl = player.getClass().getClassLoader();
        IPlayer proxy = (IPlayer) Proxy.newProxyInstance(cl,new Class[]{IPlayer.class},handler);
        proxy.login();
        proxy.play();
    }
}

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

装饰器模式强调的是增强自身,在被装饰之后你能够在被增强的类上使用增强后的功能。增强后你还是你,只不过能力更强了而已(close等方法被不同的逻辑来增强)。而代理模式是为了实现对象的控制,强调要让别人帮你做非业务的职责(记录日志、设置缓存)。 另外,装饰器模式,被装饰的类(FileInputStream),和(BufferedInputStream)都是一个东西(inputStream),但是代理模式里面,代理的对象(AopProxy)和被代理的对象(Object)之间没有关系。

行为型模式:

https://cloud.tencent.com/developer/article/1385920

springboot使用策略模式中一个接口配置多个策略实现类_一个接口多个实现类 设计模式_晨曦遇晓的博客-CSDN博客

策略模式

该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

譬如商场购物场景中,有些商品按原价卖,商场可能为了促销而推出优惠活动,有些商品打九折,有些打八折,有些则是返现10元等。

策略模式的结构图

举例:jdk comparator中的new comparator()、ThreadPoolExecutor的异常处理策略传递参数 new AbortPoliy()等等;

图片

图片

模板方法模式

Template Method模式的应用场景是:

  1. 你想将相同的算法放在一个类中,将算法变化的部分放在子类中实现

  2. 子类公共的算法应该放在一个公共的类中,避免代码重复

    举例:spring的refresh方法,tomcat源码,抽象类HttpServlet里service()就是模版方法,其他HttpServlet的子类可以实现不同的doGet、doPost等方法,但是service是公共的。

public abstract class HttpServlet extends GenericServlet {
  protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      String method = req.getMethod();
      long lastModified;
      if (method.equals("GET")) {
          lastModified = this.getLastModified(req);
          if (lastModified == -1L) {
              this.doGet(req, resp);////////////////////////
          } else {
              long ifModifiedSince = req.getDateHeader("If-Modified-Since");
              if (ifModifiedSince < lastModified) {
                  this.maybeSetLastModified(resp, lastModified);
                  this.doGet(req, resp);
              } else {
                  resp.setStatus(304);
              }
          }
      } else if (method.equals("HEAD")) {
          lastModified = this.getLastModified(req);
          this.maybeSetLastModified(resp, lastModified);
          this.doHead(req, resp);/////////////////////
      } else if (method.equals("POST")) {
          this.doPost(req, resp);/////////////////
      } else if (method.equals("PUT")) {
          this.doPut(req, resp);////////////////
      } else if (method.equals("DELETE")) {
          this.doDelete(req, resp);
      } else if (method.equals("OPTIONS")) {
          this.doOptions(req, resp);
      } else if (method.equals("TRACE")) {
          this.doTrace(req, resp);
      } else {
          String errMsg = lStrings.getString("http.method_not_implemented");
          Object[] errArgs = new Object[]{method};
          errMsg = MessageFormat.format(errMsg, errArgs);
          resp.sendError(501, errMsg);
      }
  }
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {....}
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {.....}
  ....
}

模版方法模式和策略模式的区别?

Strategy模式允许外界使用其接口方法,因而可以将这个接口方法认为是"一整个算法";而Template Method模式可以限制所留下的虚方法(doGet、doPost)只对其继承类可见,外部使用者不一定能够直接使用这些虚方法,因而可以将这些虚方法认为是"一个算法的一部分",外部调用的是它的公共方法.

如果我们要封装的算法适合于提供给用户任意使用,是"一整个算法",那么用Strategy模式较好;如果要封装的变化是一个算法中的部分(换言之,大算法的步骤是固定的),而且我们不希望用户直接使用这些方法,那么应该使用Template Method模式.就此,问题的"痛处"算是抓住了.

责任链模式:

首先 servlet将xml里面定义的filter按照顺序串联成一个filterchain,然后所有的filter 实现同一个接口,在接口里面doFilter方法先执行自己的逻辑,再通过filterchain执行下一个filter。

责任链模式: 经常用来开发过滤器 拦截器,过滤各种文本限制等等。

解决多个步骤,这里通过List进行存储了链式结构,通过extends类,设置里面的局部变量,再执行进行逐个调用。

   //设置责任链,返回第一个要执行的步骤
   private BaseApproveFlowStep initApplyResourceFlow() {
        List<BaseApproveFlowStep> stepList = new ArrayList<BaseApproveFlowStep>();
        stepList.add(new CheckApplyFormStep());//检查申请单
        stepList.add(new ItemStoreStep());//存储审批项
        stepList.add(new CheckApplyResourceStep());//检查审批项
        stepList.add(new ResourceAllocationStep());//分配计算资源
        stepList.add(new DecuctQueueFeeStep());//承包队列扣费
        stepList.add(new StoragePriceConfigStep());//设置存储费用
        stepList.add(new ServicePriceConfigStep());//设置服务费用
//        stepList.add(new ConfigShareStorageStep());//配置共享存储配额(在集群上)
        stepList.add(new ConfigUserQueueAclStep());//配置用户队列权限
        return getFirstStep(stepList);
    }
	
    private BaseApproveFlowStep getFirstStep(List<BaseApproveFlowStep> stepList) {
        //将步骤串联起来
        for (int i = 0; i < stepList.size(); i++) {
            if (i != stepList.size() - 1) {
                stepList.get(i).setNextStep(stepList.get(i + 1));
            }
        }
        
        return stepList.get(0);
    }
    
    class CheckApplyFormStep extends BaseApproveFlowStep {
    	BaseApproveFlowStep nextStep;
    	@Override
    	public execute(){
    			//处理自己的业务逻辑
    			...
    			//处理下一个设置好的步骤
    			if(nextStep!=null){
    				nextStep.execute();
    			}
    	}
    }
    
    class ItemStoreStep extends BaseApproveFlowStep {
    	BaseApproveFlowStep nextStep;
    	@Override
    	public execute(){
    			//处理自己的业务逻辑
    			...
    			//处理下一个设置好的步骤
    			if(nextStep!=null){
    				nextStep.execute();
    			}
    	}
    }
    	....

模板方法模式

当场景下,是固定的几个步骤,用模板方法模式。实现一个抽象类,定义模板方法,还有钩子函数。

策略模式:

重构多个if else

public class SingleItemShare {
  *// 单商品*
  public void algorithm(String param) {
    System.out.println("当前分享图片是" + param);
  }
}
public class MultiItemShare {
  *// 多商品*
  public void algorithm(String param) {
    System.out.println("当前分享图片是" + param);
  }
}
public class OrderItemShare {
  *// 下单*
  public void algorithm(String param) {
    System.out.println("当前分享图片是" + param);
  }
}
public class ShareFactory {

  public static void main(String[] args) throws Exception {
    Integer shareType = 1;
    *// 测试业务逻辑*
    if (shareType.equals(ShareType.SINGLE.getCode())) {
      SingleItemShare singleItemShare = new SingleItemShare();
      singleItemShare.algorithm("单商品");
    } else if (shareType.equals(ShareType.MULTI.getCode())) {
      MultiItemShare multiItemShare = new MultiItemShare();
      multiItemShare.algorithm("多商品");
    } else if (shareType.equals(ShareType.ORDER.getCode())) {
      OrderItemShare orderItemShare = new OrderItemShare();
      orderItemShare.algorithm("下单");
    } else {
      throw new Exception("未知分享类型");
    }
    *// .....省略更多分享场景*
  }

  enum ShareType {
    SINGLE(1, "单商品"),
    MULTI(2, "多商品"),
    ORDER(3, "下单");
    */**
     \* 场景对应的编码
     \*/*
    private Integer code;
    */**
     \* 业务场景描述
     \*/*
    private String desc;
    ShareType(Integer code, String desc) {
      this.code = code;
      this.desc = desc;
    }
    public Integer getCode() {
      return code;
    }
    *// 省略 get set 方法*
  }
}

策略模式重构:优点是拓展性强,缺点是需要维护一个枚举。

public interface ShareStrategy {
  *// 定义分享策略执行方法*
  void shareAlgorithm(String param);
}

public class OrderItemShare implements ShareStrategy {
  @Override
  public void shareAlgorithm(String param) {
    System.out.println("当前分享图片是" + param);
  }
}

*// 省略 MultiItemShare以及SingleItemShare策略*

*// 分享工厂*
public class ShareFactory {
 *// 定义策略枚举*
  enum ShareType {
    SINGLE("single", "单商品"),
    MULTI("multi", "多商品"),
    ORDER("order", "下单");
    *// 场景对应的编码*
    private String code;
    
    *// 业务场景描述*
    private String desc;
    ShareType(String code, String desc) {
      this.code = code;
      this.desc = desc;
    }
    public String getCode() {
      return code;
    }
    *// 省略 get set 方法*
  }
 *// 定义策略map缓存*
  private static final Map<String, ShareStrategy> shareStrategies = new    HashMap<>();
  static {
    shareStrategies.put("order", new OrderItemShare());
    shareStrategies.put("single", new SingleItemShare());
    shareStrategies.put("multi", new MultiItemShare());
  }
  *// 获取指定策略*
  public static ShareStrategy getShareStrategy(String type) {
    if (type == null || type.isEmpty()) {
      throw new IllegalArgumentException("type should not be empty.");
    }
    return shareStrategies.get(type);
  }

  public static void main(String[] args) {
    *// 测试demo*
    String shareType = "order";
    ShareStrategy shareStrategy = ShareFactory.getShareStrategy(shareType);
    shareStrategy.shareAlgorithm("order");
    *// 输出结果:当前分享图片是order*
  }
}

62 | 职责链模式(上):如何实现可灵活扩展算法的敏感信息过滤框架? (geekbang.org)

63 | 职责链模式(下):框架中常用的过滤器、拦截器是如何实现的? (geekbang.org)

60 | 策略模式(上):如何避免冗长的if-else/switch分支判断代码? (geekbang.org)

1
https://gitee.com/Paul_Zhen/java-review.git
git@gitee.com:Paul_Zhen/java-review.git
Paul_Zhen
java-review
java-review
master

搜索帮助