ARTS-10(括号生成回溯法,职责链设计模式,职场工作小感悟)

Algorithm

题目描述
leecode 22. 括号生成
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
示例 2:
输入:n = 1
输出:["()"]
提示:
1 <= n <= 8

题目详解
1、概念:
回溯算法是一种优先搜索的方法,因为有些问题是存在很多可能性,如果通过穷举法,虽然最终能解决问题,但是时间复杂度和空间复杂度就会变高,为了减少搜索范围,进行剪枝式搜索,即按照满足要求式的深度优先搜索,满足剪枝条件则停止遍历,如果没有找到满足解就往回退,走新的分支。回溯是DFS中的一种,回溯和DFS的主要区别是,回溯在求解问题过程中不保留完整的树结构,而深度优先搜索则记录完整的搜索树。

2、回溯法思想概念参考

3、解题秘籍:
1.定义解空间
解空间包括解的组织形式和显约束
解的组织形式:解的组织形式都规范为一个n元组{x1,x2,x3,x4…,xn}
显约束:是对解分量的取值范围的限定,通过显约束可以控制解空间的大小
2.确定解空间的组织结构:
解空间的组织结构通常用解空间树形象的表达,根据解空间树的不同,解空间分为子集树
排列树,m叉树。
3.搜索解空间
回溯法是按照深度优先搜索策略,根据隐约束(约束函数和限界函数),在解空间中搜索问题的可行解或最优解
当发现当前结点不满足求解条件时,回溯尝试其他的路径
求可行解,则只需要设定约束函数即可,如果求最优解,则设定约束函数和限界函数。
显约束可以控制解空间的大小,约束函数决定剪枝的效率,限界函数决定是否得到最优解

4、本题实际解题思路

解空间:满足左括号=右括号=n的括号组合字符串,按照回溯的解空间树来说,该树通常有2^n个叶结点,
其结点总数为(2^(n+1))-1。
显约束:对数n,left表示当前左括号的个数,right表示当前右括号的个数,n表示括号对数。
递归出口:当left=right=n时,添加新括号组合。
剪枝函数(隐约束):当left<n时,可添加左括号;当right<left时,可添加右括号。

代码实现

class Solution {
    public List<String> generateParenthesis(int n) {
        List<String> list = new ArrayList<String>();
        if(n==0){
            return list;
        }

        dfs("",0,0,n,list);
        return list;
    }
    private void dfs(String s, int left, int right, int n, List<String> list) {

        if(left==n && right==n){
            list.add(s);
        }
        if(left < n){
            dfs(s+"(",left+1,right,n,list);
        }
        if(right<left){
            dfs(s+")",left,right+1,n,list);
        }

    }
}

Review

设计模式之职责链模式

在 GoF 的《设计模式》中,它是这么定义的:
将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将所有接收处理者通过前一对象记住其下一个对象的引用而连成一条链,将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止,链条上的每个处理器各自承担各自的处理职责。

在现实生活中,采购审批流程、请假流程、公司员工请假流程等都可以使用到职责链来解决。

职责链是一种行为型设计模式,优点是:它可以降低对象之间的耦合度,将请求的发送和接收解耦,这样可以增强系统的扩展性,可以根据需要增加新的请求处理类,满足开闭原则。缺点是:不能保证每一个请求都得到处理,因为如果你的请求没有明确的接收者,可能走到链路末尾也无法得到处理,因此在设计之处要尽可能地考虑请求的多样性处理方案,第二就是请求的处理可能涉及多个处理对象,系统性能将受到一定影响。

1、模式的结构

  • 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
  • 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
  • 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
    2、示例实现

public interface IHandler {
  boolean handle();
}

public class HandlerA implements IHandler {
  @Override
  public boolean handle() {
    boolean handled = false;
    //...
    return handled;
  }
}

public class HandlerB implements IHandler {
  @Override
  public boolean handle() {
    boolean handled = false;
    //...
    return handled;
  }
}

public class HandlerChain {
  private List<IHandler> handlers = new ArrayList<>();

  public void addHandler(IHandler handler) {
    this.handlers.add(handler);
  }

  public void handle() {
    for (IHandler handler : handlers) {
      boolean handled = handler.handle();
      if (handled) {
        break;
      }
    }
  }
}

// 使用举例
public class Application {
  public static void main(String[] args) {
    HandlerChain chain = new HandlerChain();
    chain.addHandler(new HandlerA());
    chain.addHandler(new HandlerB());
    chain.handle();
  }
}

3、实际工作运用

场景一、登录鉴权拦截伪职责链实现

1)需求背景:系统设置了登录验证及权限拦截来决定接口或者用户的系统访问,其中角色权限设计主要就是基于RBAC(Role-Base Access Control,基于角色的访问控制),就是用户通过角色和权限进行关联。简单的说,一个用户拥有多个角色,一个角色拥有多个权限。基于“用户-角色-权限”的授权模型,进行该登录鉴权的功能。
2)具体设计

  • 通过SpringBoot的HandlerInterceptorAdapter这个适配器来实现系统的登录拦截器,获取登录用户的信息
  • 再次通过HandlerInterceptorAdapter来实现权限控制拦截器,判断当前登录用户具有哪些权限
  • 通过WebMvcConfigurationSupport 配置类,将上述两个拦截器进行注册构建登录、鉴权的链路

3)针对拦截器和配置类作用介绍及具体实现
a、拦截器
在SpringBoot中我们可以使用HandlerInterceptorAdapter这个适配器来实现自己的拦截器。这样就可以拦截所有的请求并做相应的处理。自定义拦截器需要实现该适配器,如下:

  • preHandle:在业务方法被调用前执行。在该方法中可以做类似校验的功能。如果返回true,则继续调用下一个拦截器。如果返回false,则中断执行。
  • postHandle:业务方法(Controller)执行完后执行
  • afterCompletion:在整个请求处理完毕后进行回调,也就是说视图数据渲染完毕或者调用方已经拿到响应。

多个拦截器的执行顺序:(正常执行的顺序,异常则从异常拦截器处倒序执行)
拦截器1的preHandle(执行完返回true)->拦截器2的preHandle(执行完返回true)->拦截器2的postHandle->拦截器1的postHandle->拦截器2的afterCompletion->拦截器1的afterCompletion

@Configuration
public class MyInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
            Object handler) throws Exception {
            //登录相关逻辑
    }
 
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
    }
 
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
            Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion.........");
    }
}

b、配置类WebMvcConfigurationSupport
WebMvcConfigurationSupport是webmvc的配置类,如果在springboot项目中,有配置类继承了WebMvcConfigurationSupport,那么webmvc的自动配置类WebMvcAutoConfiguration就会失效。
在这里插入图片描述
可以看到上面贴图中的WebMvcAutoConfiguration的注解@ConditionalOnMissingBean(WebMvcConfigurationSupport.class),该注解的意思是,当spring的容器中没有装载继承了WebMvcConfigurationSupport类型的bean时,自动配置类才会生效。
重点注意:Spring Boot中只能有一个WebMvcConfigurationSupport配置类

使得a中拦截器生效的方法就是将拦截器通过配置类进行注册,其中WebMvcConfigurationSupport配置webmvc的一些方法有:

@Configuration
public class Configurer extends WebMvcConfigurationSupport{
    
    //自定义的一个拦截器
    @Autowired
    MyInterceptor MyInterceptor;
    
    //定义时间格式转换器
    @Bean
    public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"));
        converter.setObjectMapper(mapper);
        return converter;
    }

    //添加转换器
    //配置springmvc返回数据时 输出数据的格式,这里只配置了时间的输出格式
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        //将我们定义的时间格式转换器添加到转换器列表中,
        //这样jackson格式化时候但凡遇到Date类型就会转换成我们定义的格式
        converters.add(jackson2HttpMessageConverter());
    }

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        // TODO Auto-generated method stub
        //将我们自定义的登录拦截器注册到配置中 ,该拦截器会对/api/**请求路径进行拦截。
        registry.addInterceptor(MyInterceptor).addPathPatterns("/api/**");
        super.addInterceptors(registry);
    }   
	

	/**
     * 防止@EnableMvc把默认的静态资源路径覆盖了,手动设置的方式
     * 配置了静态资源访问
     * 还能配置视图解析 访问服务器上的资源
     * @param registry
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 解决静态资源无法访问
        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
        //配置视图解析,把url中后面带/image/***的路径映射到c盘photo文件中的资源
       	registry.addResourceHandler("/image/**").addResourceLocations("file:C://photo/");
        super.addResourceHandlers(registry);
    }

    //配置服务器跨域请求被允许
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH")
                .allowCredentials(true).maxAge(3600);
    }
}

场景二、异常处理通知模块职责链实现

1)需求背景
通过异常处理(重试和推荐通知)来减少作业执行的失败率并且能及时通知到用户,保障用户的作业正常执行,并减少总行运维人员的运维工作。

  • 通过配置自动重试的异常配置来解决一些因连接问题或者异常失败原因的作业进行自动重试,减少人工运维成本,同时可以提高作业执行的成功率
  • 通过配置处理推荐方法,当作业失败之后,可以将匹配日志关键词或者返回码来检测作业的错误类型是否有配置处理推荐的,如果配置了将显示在日志下面,并可以通过招乎的方式推送给用户。

2)具体设计
构建异常重试处理器、异常推荐处理器、异常通知处理器的链路,根据用户对异常的配置来决定异常步骤到每一个处理器上是否进行相应的处理。
实际运用和上面讲到的基本职责链实现有一些改造,基本职责链是指一个请求到达一个处理器上如果它能处理该请求,就不继续往下传递;如果不能处理,则交由后面的处理器来处理;而本实际需求则可能存在多个处理器都需要进行处理,因此不设置处理完就返回,而是每一条请求都完整走完整条链路。
处理器A:初始化处理器,用于将请求步骤信息持久化到数据库中
处理器B:异常重试处理器,根据用户配置的步骤重试关键字进行匹配,匹配成功处理步骤重试
处理器C:异常推荐处理器,根据用户配置的步骤推荐关键字进行匹配,匹配成功处理步骤推荐信息
处理器D:异常通知处理器,根据用户配置的作业是否通知信息,来决定是否将异常推荐处理器C的推荐信息通过短信方式推荐给用户
处理器E:将步骤信息在此链路上的处理信息持久化到数据库中

Tips

将类转换成属性与属性值的map方法推荐,主要是通过java反射的方式获取类的字段及字段值。

简单介绍下java反射:java反射功能非常强大,可以通过反射创建对象,获取类属性、方法、构造器等。

public HashMap<String, Object> beanToMap(Object obj) throws IllegalAccessException {
        HashMap<String, Object> map = new HashMap<>();
        Class<?> clazz=obj.getClass();
        for (Field field:clazz.getDeclaredFields()){
            field.setAccessible(true);
            String fieldName=field.getName();
            Object fieldObj = field.get(obj);
            if (fieldObj!=null)
                map.put(fieldName,fieldObj.toString());
        }
        return map;
    }

Share

本周进行了绩效访谈,通过谈话,分享几点工作的感受:
1、工作过程中,积极主动的帮助客户解决问题,在解决问题的过程中,记录下用户的需求并且思考用户的问题是否是产品本身存在不足,如果要改进应该如何改进

2、在实际开发过程中,不能仅仅站在开发者的角度也要多站在用户的角度思考功能的实现,比如功能的基本实现、如果多用户需求不一样但使用的是统一功能如何满足、考虑并发等等,不仅要夯实自己的开发实力也要站在产品或者用户的角度,把功能设计的更加完善

3、事事有回应 件件有着落 凡事有交代,这句话说的是工作上的“闭环思维”,如果你接下了一件事情,无论最后办到什么程度,都要有个交代。办成了,有个交代,没办成,也要有个交代。听起来似乎不是很难,可其实绝大多数人都做不到。有的人,也不愿意去做到。

4、一个人的做事能力可以从每一件小事上看出,只有你把每一件事都做到上述第三点,领导才会把更重要的事情交给你

5、无论是在讨论需求会议上还是与同事领导沟通,都要多思考并表达自己的想法,不管想法是否合理,表达了才说明你有思考才有idea碰撞的可能,这就是之前看到的强力瞬间中讲到制造体验。

6、要多表达自己做的事情,让领导看到,不要闷声做事情,不当透明人,也不当边缘人,多参与到事情中,贡献自己的一份力量。

7、到底是想做产品还是技术??这个问题真的值得思考(找同学或者其他人聊聊这个问题)

8、目前还是要先多提高自己的技术能力,模块化定制学习,最近学到一个方法,就是把未来一到两个月的学习计划罗列,比如写博客,先把博客文章名字和内容大致规划好,按计划每周实施下去

9、实际的交流沟通才是灵感迸发的源泉,自己学习固然重要,但是实际体验更重要

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值