美文网首页
个人精进系列-好的代码-审美/函数

个人精进系列-好的代码-审美/函数

作者: JayWu的架构笔记 | 来源:发表于2020-05-13 13:06 被阅读0次

    个人精进系列-好的代码-审美/函数

    审美

    好的代码应当“看上去养眼”

    一致性

    public class PerformanceTester {
    
        public TcpConnectionSimulator wifi = new TcpConnectionSimulator(
                                                500,
                                                80);
                                                
        public TcpConnectionSimulator t3LongNameFiber = 
                    new TcpConnectionSimulator(
                          45000,
                          10);
    
        public TcpConnectionSimulator cell = new TcpConnectionSimulator(    
                                                100,
                                                400);
    }
    

    调整

    重新排版来保持一致和紧凑

    public class PerformanceTester {    
     public TcpConnectionSimulator wifi = 
                    new TcpConnectionSimulator(
                          500,
                          80);
     public TcpConnectionSimulator t3LongNameFiber = 
                    new TcpConnectionSimulator(
                          45000,
                          10);
     public TcpConnectionSimulator cell = 
                    new TcpConnectionSimulator(
                          100,
                          400);
    }
    

    换行

    定义:

    public ResourceHandlerMapping(ResourceLoader staticResourceLoader, String baseDir, String mappingPath)
    

    使用:

    new ResourceHandlerMapping(staticResourceLoader,"/Volumes/Mac/Users/jaywu/Documents/webapp/",DEFAULT_MAPPING_PATH);
    

    调整

    重新排版来保持一致和紧凑

    public ResourceHandlerMapping(ResourceLoader staticResourceLoader,
                                  String baseDir,
                                  String mappingPath) 
    

    使用:

    new ResourceHandlerMapping(
            staticResourceLoader,
            "/Volumes/Mac/Users/jaywu/Documents/webapp/",
            DEFAULT_MAPPING_PATH
    );
    

    分组

    例1

    public class Demo {
        public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
        
        private MultipartResolver multipartResolver;
        
        private FlashMapManager flashMapManager;
        
        private LocaleResolver localeResolver;
        
        public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver";
    
        private MultipartResolver multipartResolver;
    
        public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver";
    }
    
    调整

    把声明按块组织起来

    public class Demo {
        public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
        public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver";
        public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver";
    
        private MultipartResolver multipartResolver;
        private LocaleResolver localeResolver;
        private MultipartResolver multipartResolver;
        
        private FlashMapManager flashMapManager;
    }
    

    例2

    processedRequest = checkMultipart(request);
    multipartRequestParsed = (processedRequest != request);
    mappedHandler = getHandler(processedRequest);
    if (mappedHandler == null || mappedHandler.getHandler() == null) {
        noHandlerFound(processedRequest, response);
        return;
    }
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
        return;
    }
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
    调整

    把代码分成“段落”

    processedRequest = checkMultipart(request);
    multipartRequestParsed = (processedRequest != request);
    
    // Determine handler for the current request.
    mappedHandler = getHandler(processedRequest);
    if (mappedHandler == null || mappedHandler.getHandler() == null) {
        noHandlerFound(processedRequest, response);
        return;
    }
    
    // Determine handler adapter for the current request.
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
        return;
    }
    
    // Actually invoke the handler.
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    

    原则

    按代码逻辑的关联、紧密程度分组

    IDEA

    • 修复IDEA的黄色警告
      • 越新的版本,可以给出越多改进的地方
    • 代码分割线时刻提醒你的代码长度

    函数

    • 在面向对象语言中,函数称之为方法。
    • 编写好函数是编写好代码的基础。
    • 一个系统容易腐化的部分正是函数,不解决函数的复杂性,就很难解决系统的复杂性。

    意图提取

    将逻辑中的重要意图单独提取函数,使整体逻辑清晰,避免陷入细节。

    if(a > 0 && b < 1) {
        c = 1;
        d = 2;
    }
    

    调整

    if(isItemEnabled()) {
        changeOrderStatus();
    }
    

    函数参数

    • 参数越少,越容易理解,越容易使用和测试
    • 参数较多时,考虑是否可以封装为类

    Line makeLine(String startX, 
                  String startY,
                  String endX,
                  String endY);
    

    调整

    public class Point{
        double x;
        double y;
    }
    
    Line makeLine(Point start, Point end);
    

    短小的函数

    • 不超过25行
    • 严格执行标准,代码质量可以得到快速提升

    要写出精简的函数,可以牢记以下原则:

    • 精简辅助代码
    • 组合函数模式

    精简辅助代码

    定义

    必不可少的非核心代码即为辅助代码

    • 打印日志
    • 鉴权
    • 降级
    • 缓存操作
    • 非空判断
    • ……
    重要性

    减少对业务代码的干扰,防止淹没在辅助代码,直观地体现业务逻辑。

    优化判空

    判空代码非常干扰阅读流畅性

    public void handle(A a) {
        String value = null;
    
        if (a != null) {
            B b = a.b();
            if (b != null) {
                C c = b.c();
                if (c != null) {
                    D d = c.d();
                    if (d != null) {
                        value = d.value();
                    }
                }
            }
        }
        
        // 重要的业务代码
        ...
    }
    

    调整

    意图提取,防止淹没业务代码

    public void handle(A a) {
        String value = getValue(a)
        // 重要的业务代码
    }
    
    private String getValue(A a) {
        if (a != null) {
            B b = a.b();
            if (b != null) {
                C c = b.c();
                if (c != null) {
                    D d = c.d();
                    if (d != null) {
                       return d.value();
                    }
                }
            }
        }
        
        return null;
    }
    

    尽早退出异常流程

    private String getValue(A a) {
        if(a == null) {
            return null;
        }
        
        B b = a.b();
        if(b == null) {
            return null;
        }
        
        C c = b.c();
        if(c == null) {
            return null;
        }
        
        D d = c.d();
        if(d == null) {
            return null;
        }
        
        return d.value();
    }
    
    隐藏技术细节
    • 抽象通用框架
      • 自动化日志打印
      • 自动化鉴权处理
      • 全局异常处理
    • 隔离第三方依赖(DDD)
      • 中间件依赖
      • 第三方接口

    组合函数模式

    定义

    将大函数拆成多个子函数的组合

    public Source handle(Context context) {
        List<Source> sources = handlers.stream()
            .map(it -> it.handle(context))
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
    
        if (sources.isEmpty()) {
            return null;
        }
    
        SelectorBinding selectorBinding = new ListBinding(
            sources.stream()
            .map(it -> it.getSourceType().value())
            .collect(Collectors.toList())
        );
        
        String selectedValue = selector.select(selectorConfigId(), selectorBinding);
    
        return sources.stream()
            .filter(it -> StrUtil.equals(it.getSourceType().value(), selectedValue))
            .findFirst()
            .orElse(null);
    }
    
    • 17行代码,属于短小的函数
    • 无法快速理解整体业务逻辑

    调整

    • 提炼执行步骤概要,细节分散在私有函数
    • 一个私有函数尽可能只做一件事情
    public Source handle(Context context) {
        List<Source> sources = filterEffectiveSources(context);
    
        if (sources.isEmpty()) {
            return null;
        }
    
        String selectedValue = selectSourceValue(sources);
    
        return sourceOf(sources, selectedValue);
    }
    

    调整后

    • 像在看一本书,入口函数是目录,指向各自的私有函数
    • 每个私有函数职责单一,代码精炼、易于复用

    抽象层次一致性

    • 函数体内容在同一抽象层次
    • 高层抽象和实现细节不能混杂

    public Source handle(Context context) {
        List<Source> sources = filterEffectiveSources(context);
    
        if (sources.isEmpty()) {
            return null;
        }
    
        SelectorBinding selectorBinding = new ListBinding(
            sources.stream()
            .map(it -> it.getSourceType().value())
            .collect(Collectors.toList())
        );
        
        String selectedValue = selector.select(selectorConfigId(), selectorBinding);
    
        return sourceOf(sources, selectedValue);
    }
    

    调整

    • selectedValue的处理过程与其他函数不在一个层次
    public Source handle(Context context) {
        List<Source> sources = filterEffectiveSources(context);
    
        if (sources.isEmpty()) {
            return null;
        }
    
        String selectedValue = selectSourceValue(sources);
    
        return sourceOf(sources, selectedValue);
    }
    

    抽象层次一致性是指导函数拆分的重要原则。

    函数式编程

    • 把函数作为参数传递给另一个函数

      • public count(Supplier<String> supplier) {
            
        }
        
    • 减少冗余代码,让代码更简洁、可读性更好。

      • 代码冗余程度(大到小)
        • 经典类
        • 匿名类
        • 匿名类
        • Lamda(匿名函数)
        • 函数引用
    • 函数无副作用

      • 没有对共享的可变数据操作
      • 利用多核并行处理,无需关心线程安全问题

    Java的函数式编程

    Stream
    long count = 0;
    List<Integer> list = Arrays.asList(1, 2, 3);
    for (Integer it : list) {
        if (it > 2) {
            count++;
        }
    }
    

    调整

    Stream.of(1, 2, 3)
        .filter(it -> it > 2)
        .count();
    
    Optional
    String value = null;
    
    if (a != null) {
        B b = a.b();
        if (b != null) {
            C c = b.c();
            if (c != null) {
                D d = c.d();
                if (d != null) {
                    value = d.value();
                }
            }
        }
    }
    

    调整

    String value = Optional.ofNullable(a)
        .map(it->it.b())
        .map(it->it.c())
        .map(it->it.d())
        .map(it->it.value())
        .orElse(null)
    
    总结
    • 函数组合容易构建复杂逻辑
    • 自明的代码
    • 便于复用
    • 易于使用
    • 更多请参考

    保持精进

    • 写出好的代码无需高深的思想,需要的只是工匠精神

      Kent Beck说的:“我不是一个伟大的程序员,只是习惯比较好而已。”

    • 只有养成精益求精、追求卓越的习惯,才能保持精进,写出好的代码

    相关文章

      网友评论

          本文标题:个人精进系列-好的代码-审美/函数

          本文链接:https://www.haomeiwen.com/subject/lcrnnhtx.html