JAVA代码规范与编写高质量代码的建议(2)
目录6. 行为参数化传递代码7. 接口验证优化与自定义注解8. 原型模式提高创建对象的性能9. CompleteFuture异步编程提高RPC效率10. AOP实现日志记录6. 行为参数化传递代码
软件工程中一个众所周知的问题就是,不管你做什么,用户的需求肯定会变。比方说,有个应用程序是帮助农民了解自己的库存。这位农民可能想要一个查找库存中所有绿色苹果的功能。但到了第二天,他可能会告诉你:"其实我还想找出所有重量超过150克的苹果。"过了两天,农民又跑回来补充道:"要是我可以找出所有既是绿色,重量也超过150克的苹果,那就太棒了。"你要如何应对这样不断变化的需求?理想的状态下,应该把你的工作量降到最少。此外,类似的新功能实现起来还应该很简单,而且易于长期维护。
行为参数化就是可以帮助你处理频繁变更的需求的一种软件开发模式
就农场库存程序而言,你必须实现一个从列表中筛选绿苹果的功能6.1 初试牛刀:筛选绿苹果/** * @author Marion * @date 2022/5/13 18:08 */ public class Farm001 { /** * v1. 筛选绿色苹果 */ public static List filterGreenApples(List list) { List ret = new ArrayList<>(); for (Apple apple : list) { if (apple.getColor().equals(ColorEnums.GREEN.name())) { ret.add(apple); } } return ret; } }6.2 再展身手:把颜色作为参数 /** * v2. 颜色作为参数 */ public static List filterRedApples(List list, ColorEnums color) { List ret = new ArrayList<>(); for (Apple apple : list) { if (apple.getColor().equals(color.name())) { ret.add(apple); } } return ret; }
太简单了,对吧?让我们把例子再弄得复杂一点儿。这位农民又跑回来和你说:"要是能区分轻的苹果和重的苹果就太好了。重的苹果一般是重量大于150克。"/** * v2: 筛选重量苹果 * @param flag true-比较颜色 false-比较重量 */ public static List filterGreenApples(List list, ColorEnums color, int wight, boolean flag) { List ret = new ArrayList<>(); for (Apple apple : list) { if (flag) { if (apple.getColor().equals(color.name())) { ret.add(apple); } } else { if (apple.getWeight() > wight) { ret.add(apple); } } } return ret; }6.3 行为参数化
你在上一节中已经看到了,你需要一种比添加很多参数更好的方法来应对变化的需求。让我们后退一步来看看更高层次的抽象。一种可能的解决方案是对你的选择标准建模:你考虑的是苹果,需要根据Apple的某些属性(比如它是绿色的吗?重量超过150克吗?)来返回一个boolean值。我们把它称为谓词(即一个返回boolean值的函数)。让我们定义一个接口来对选择标准建模:@FunctionalInterface public interface ApplePredicate { boolean filter(Apple apple); }
你可以把这些标准看作filter方法的不同行为。你刚做的这些和"策略设计模式"相关,它让你定义一族算法,把它们封装起来(称为"策略"),然后在运行时选择一个算法6.4 第五次尝试:使用匿名类/** * v3: 筛选绿色 */ Farm003.filterApples(apples, new ApplePredicate() { @Override public boolean filter(Apple apple) { return apple.getColor().equals(ColorEnums.GREEN.name()); } }); Farm003.filterApples(apples, new ApplePredicate() { @Override public boolean filter(Apple apple) { return apple.getColor().equals(ColorEnums.GREEN.name()) && apple.getWeight() > 100; } }); /** * v3: 筛选重量苹果 */ public static List filterApples(List list, ApplePredicate predicate) { List ret = new ArrayList<>(); for (Apple apple : list) { if (predicate.filter(apple)) { ret.add(apple); } } return ret; }
第一,它往往很笨重,因为它占用了很多空间
第二,很多程序员觉得它用起来很让人费解6.5 第六次尝试:使用Lambda表达式Farm003.filterApples(apples, apple -> apple.getColor().equals(ColorEnums.GREEN.name())); Farm003.filterApples(apples, apple -> apple.getColor().equals(ColorEnums.GREEN.name()) && apple.getWeight() > 100);6.6 第七次尝试:将List类型抽象化@FunctionalInterface public interface ApplePredicate { boolean filter(T apple); }6.7 第八次尝试:使用Stream流过滤 /** * v4: 筛选重量苹果 */ public static List filterApples(List list, ColorEnums color) { return list.stream() .filter(v -> v.getColor().equals(color.name())) .collect(Collectors.toList()); }7. 接口验证优化与自定义注解
validation主要是校验用户提交的数据的合法性,比如是否为空,密码是否符合规则,邮箱格式是否正确等等,校验框架比较多,用的比较多的是hibernate-validator, 也支持国际化,也可以自定义校验类型的注解,这里只是简单地演示校验框架在SpringBoot中的简单集成,要想了解更多可以参考 hibernate-validator。/** * @author Marion * @date 2022/5/13 14:38 */ @Data @NoArgsConstructor @AllArgsConstructor @Builder public class UserQuery { private Long id; @NotBlank @Length(min = 4, max = 10) private String name; @NotBlank @Email private String email; /** * ^((13[0-9])|(15[^4,D])|(18[0,3-9]))d{8}$ */ @NotBlank //@Pattern(regexp = "^((13[0-9])|(15[^4,D])|(18[0,3-9]))d{8}#34;, message = "手机号格式不正确") @Phone private String phone; @Min(value = 18) @Max(value = 200) private int age; @NotBlank @Length(min = 4, max = 12, message = "昵称4-12位") private String nickname; }/** * 全局异常处理器增加对APIException的拦截,并修改异常时返回的数据格式 * @author Marion * @date 2022/5/13 14:47 */ @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) public AppResponse validationException(MethodArgumentNotValidException e) { System.out.println(e.getMessage()); FieldError fieldError = e.getBindingResult().getFieldError(); return AppResponse.builder() .code(400) .message(fieldError.getDefaultMessage()) .build(); } }
自定义验证注解/** * @author Marion * @date 2022/5/13 15:10 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) @Constraint(validatedBy = PhoneValidator.class) public @interface Phone { String message() default "手机号格式不正确"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }/** * @author Marion * @date 2022/5/13 15:43 */ public class PhoneValidator implements ConstraintValidator { @Override public void initialize(Phone constraintAnnotation) { } @Override public boolean isValid(String value, ConstraintValidatorContext context) { if (!StringUtils.isEmpty(value)) { return isPhone(value); } return false; } private boolean isPhone(String phone) { return phone.matches("^((13[0-9])|(15[^4,D])|(18[0,3-9]))d{8}#34;); } }8. 原型模式提高创建对象的性能对于构造参数耗时多的使用原型模式更快轻量级对象new效率高于原型模式/** * @author Marion * @date 2022/5/13 16:16 */ @Data @NoArgsConstructor public class UserDTO implements Cloneable { public UserDTO(int id, String name) { if (name.length() != 0) { name = name.substring(0, 1); } this.id = id; this.name = name; } private int id; private String name; @Override public Object clone() { UserDTO userDTO = null; try { userDTO = (UserDTO) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return userDTO; } }
benchmark进行性能测试/** * 原型模式创建对象 * 1. 轻量级对象new效率高于原型模式 * @author Marion * @date 2022/5/13 16:16 */ public class Demo008 { @Benchmark @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 1, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 1, time = 5, timeUnit = TimeUnit.SECONDS) @OutputTimeUnit(TimeUnit.SECONDS) @Fork(1) @Threads(1) public static void newUserDTO() { long start = System.currentTimeMillis(); for (int i = 0; i < 10000000; i++) { UserDTO userDTO = new UserDTO(1, "张三"); userDTO.setName("张三"); } long end = System.currentTimeMillis(); System.out.println("newUserDTO=" + (end -start) + "ms"); } @Benchmark @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 1, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 1, time = 5, timeUnit = TimeUnit.SECONDS) @OutputTimeUnit(TimeUnit.SECONDS) @Fork(1) @Threads(1) public static void cloneUserDTO() { long start = System.currentTimeMillis(); UserDTO userDTO = new UserDTO(1, "张三"); for (int i = 0; i < 10000000; i++) { UserDTO clone = (UserDTO) userDTO.clone(); clone.setName("李四"); } long end = System.currentTimeMillis(); System.out.println("cloneUserDTO=" + (end -start) + "ms"); } public static void main(String[] args) throws RunnerException { OptionsBuilder builder = new OptionsBuilder(); Options build = builder.include(Demo008.class.getSimpleName()) .forks(1) .build(); new Runner(build).run(); } } 9. CompleteFuture异步编程提高RPC效率
很多语言(如JavaScript)提供了异步回调,一些Java中间件(如Netty、Guava)也提供了异步回调API,为开发者带来了更好的异步编程工具。Java 8提供了一个新的、具备异步回调能力的工具类——CompletableFuture,该类实现了Future接口,还具备函数式编程的能力。
使用CompletableFuture进行多个RPC调用/** * @author Marion * @date 2022/5/13 17:41 */ @Service public class ShopService { /** * v1: 串行执行 */ public void goods() { StopWatch stopWatch = new StopWatch(); stopWatch.start(); System.out.println(userInfo()); System.out.println(goodsInfo()); System.out.println(commentInfo()); stopWatch.stop(); System.out.println("[goods] execute time=" + stopWatch.getTotalTimeMillis() + "ms"); } public void syncGoods() throws ExecutionException, InterruptedException { StopWatch stopWatch = new StopWatch(); stopWatch.start(); CompletableFuture s1 = CompletableFuture.supplyAsync(this::userInfo); CompletableFuture s2 = CompletableFuture.supplyAsync(this::goodsInfo); CompletableFuture s3 = CompletableFuture.supplyAsync(this::commentInfo); CompletableFuture
让直播变得更简单,加来众科CM8触摸屏数字直播声卡为直播而生,是加来众科产品系列的核心理念,加来众科经过多次改良,精心打造了CM触摸屏数字直播声卡,这款触摸屏直播声卡,是专门为K歌直播设计得全新K歌直播神器。目前市面上的传统手机直
从iPhone转到一加7Pro什么感受?这也意味着,随着国产手机的不断进步与发展,越来越多的iPhone用户开始转向了安卓手机。那么这些iPhone用户流向了哪些手机品牌呢?最近这段时间,就有不少用户在微博上分享了自己的
iPhone12连转接头都不送了,想听无损音频有好办法吗?等了一年又一年,支持5G的iPhone12终于上市了,这次iPhone12连手机连转接头都不送了,想听有线音频,有没有更好的办法?以前,我们只需要在手机上播放歌曲,插上耳机就能享受
加来众科M50专业监听耳机,让听音乐变得有仪式感直播行业的火热程度,大家是有目共睹的,这一行业的迅速发展,也带动了许多相关产业的发展,特别是直播设备也越来越多了,而耳机作为直播中必不可少的设备,不但能用于直播监听,在日常娱乐生活
音乐手机为啥越来越少了?音乐手机,曾经无限风光的产品,现在为啥越来越少了?就连主打音乐手机的ViVO最近也很少更新其音乐手机产品线,是什么原因?我们有做一期视频,在知乎和B站都有,有兴趣的可以去我们视频主
THX到底是个啥你可能看到过这个标志(THX),但你知道它表示什么吗?THX不是Thanks的缩写,而是视听感受的忠实守护者。星球大战帝国反击战上映后,导演乔治卢卡斯发现虽然声音录制和影片拍摄技术
hifi把功率做大是否是一个骗局?此文我们有做一个视频,觉得文字不方便看的话可以看我们的视频。经常看到有些人说,拿普通手机推耳机就够用了,耳机就是听个响,几mW的功率完全满足使用,HiFi就是智商税。hifi把功率
华为新一代麒麟芯片将于9月6日发布,或集成5G基带8月19日,华为终端官方微博发布了IFA2019预告视频,宣告华为将参加9月6日柏林IFA2019大展!预告视频发出之后,网友们纷纷猜测在IFA2019上华为会有哪些大动作,是发布
不惧地理限制华为智慧屏让亲情汇聚一屏之间9月19日,华为在德国慕尼黑一下带来了两款重磅智能终端产品华为Mate30系列和华为智慧屏。其中,华为智慧屏由于是智慧终端的新物种,受到了格外的关注。华为智慧屏很有可能掀起一场客厅
会买的小伙伴双11开始准备新年礼物啦!华为平板M5青春版了解下?双11购物狂欢的号角已然吹响,不少小伙伴又开始摩拳擦掌准备买买买了!而且随着双11抢购的经验不断丰富,很多会买的小伙伴还会趁着优惠力度最大的时候把一年里需要的物品都买好。特别是双1
华为nova6系列配备麒麟9904200mAh大电池让你爽玩一整天今年的5G手机竞争尤为激烈,但于近日发布的华为nova65G手机一经推出便成为了同档位最受欢迎的手机之一,受到nova星人的追捧。该机除了先进的105前置超广角双摄系统外,搭载的麒