因为性格缘由,笔者很难沉下心来进行严肃的系统学习,老是喜欢折腾一些奇淫技巧,很是喜欢代码设计,扣代码的细节,因此本次分享一下我所知道的如何写最少的代码
的小技巧,若是你有更好的方案,欢迎在评论区留言,方案很棒的话,加我微信,为你送上冬天的一杯奶茶~
java
秀一下Go
的多返回值:算法
package main import "fmt" // 返回 X + Y 和 X * Y func Computer(X, Y int) (int, int) { return X + Y, X * Y }
众所周知,Java仅支持单一返回值,通常状况下若是须要返回多个对象,咱们会根据代码语义选择容器或者新建一个新的类,把咱们须要的数据包起来。编程
这样作有没有问题?
固然没有问题,可是瑕疵就在于:可能会产生没啥语义但又不得不存在的中间类
,我我的很是讨论该类代码,那么该如何解决这种问题呢?设计模式
首先须要认识到,解决方案必须知足几个要求:安全
既然如此,咱们能够采用泛型
来知足复用、语义清晰的要求,用中间类
来知足代码安全性的要求,代码以下:微信
public class MultipleTwoReturn<A, B> { /** 第一个返回值 **/ private final A first; /** 第二个返回值 **/ private final B second; public MultipleTwoReturn(A first, B second) { this.first = first; this.second = second; } // 省略Get方法 }
同时,咱们能够依赖于继承
,让该工具类拓展更多的参数:数据结构
public class MultipleThreeReturn<A, B, C> extends MultipleTwoReturn<A, B> { /** 第三个返回值 **/ private final C third; public MultipleThreeReturn(A first, B second, C third) { super(first, second); this.third = third; } }
测试类:ide
public class MultipleApp { public static void main(String[] args) { MultipleTwoReturn<Integer, String> returnTest = MultipleApp.getReturnTest(); System.out.println(returnTest.getFirst()); System.out.println(returnTest.getSecond()); } private static MultipleTwoReturn<Integer, String> getReturnTest() { MultipleTwoReturn<Integer, String> demo = new MultipleTwoReturn<>(0, "Kerwin Demo."); return demo; } }
本质仍是普通对象
,可是加上泛型
后威力剧增!因为在方法定义时就强制了泛型约束
,语义很是清晰,同时能够彻底杜绝上述的无语义中间类,固然一些必要的,有业务含义的组装类,不建议使用这种方式。函数式编程
你们在学Java泛型之初有没有这种想法?我想利用
new
一个
T
,可是Java它
new
不出来啊 😂
好久以前我在写一个通用的Java爬虫接口
,里面有一个功能就是传入目标网页的便可获取到针对不一样网页设计的Bean
,大概以下所示:
public interface SpiderBeansHandle<T> { /** 获取Url **/ String getUrl(); /** 获取Cookie **/ String getCookie(); /** 获取CSS selector **/ String getSelector(); // .... }
中间关键的一点即如何获取到这个Bean,那个时候我只有一个想法:new 一个 T
事实证实,我过于天真了 🙄
可是换种思路,既然new
不出来,那我就返回一下吧,因而代码出炉了~
public interface SpiderBeansHandle<T> { /** * 获取Url */ String getUrl(); /** * 获取Cookie */ String getCookie(); /*** * 获取CSS selector */ String getSelector(); /*** * 解析Element * @param element element */ T parseElement(Element element); /*** * Get Beans * @param handle Bean对象 | handle对象 * @param <T> Bean类型 * @return List<Beans> */ static <T> List<T> getBeans(SpiderBeansHandle<T> handle) { List<T> list = new ArrayList<>(); List<Element> elements = SpiderUtils.getElementWithCookie(handle.getUrl(), handle.getSelector(), handle.getCookie()); for (Element element : elements) { T bean = handle.parseElement(element); if (bean != null) { list.add(bean); } } return list; } }
关键一步就在于:
/*** * 解析Element * @param element element */ T parseElement(Element element);
那么这个小技巧有什么用呢?仔细看会不会以为它像一种设计模式的变形体?没错!真相只有一个:模板方法模式
我刚提到了我须要一个处理爬虫的通用接口
,由于简单爬虫无非就是拿到url而后请求,解析细节封装到自身的Bean里,而后获取一个列表,那么在开发业务代码的时候相似,确定有某些场景和需求具备高度的一致性,那么使用这种设计方案便可大大的减小重复代码~
我们在写代码的时候有没有遇到过这种问题?写了一个工具类方法,可是功能又过于单一,虽然说单一原则好吧,可是一个小逻辑写一堆方法,总感受不得劲,如何解决咧?
Java8提供的函数式编程便可帮咱们必定程度上解决这种问题,如:
// 写一个获取文件列表,且判断是否为txt结尾的工具类方法,新手会这么写 public static File getFileWithTxt(String path) throws IOException { File file = new File(path); if (!file.exists()) { throw new IOException("File is not exist."); } if (file.getName().endsWith(".txt")) { return file; } return null; }
老手通常会把 .txt
做为参数传入,可是某一天我须要判断文件大小,文件长度,甚至是文件内容的时候,我该咋办?再写N个?
最好的方案即传入 Predicate
谓词,让调用者自定义处理逻辑
,而后再把最经常使用的逻辑基于该方法复写一下,拓展性Max!代码以下:
/*** * 文件夹谓词匹配 * @param file 文件 * @param predicate 谓词匹配 * @return List<File> * @throws IOException IOException */ public static List<File> listFilesInDirWithFilter(File file, Predicate<String> predicate) throws IOException { if (!file.exists()) { throw new IOException("File is not exist."); } List<File> fileList = new ArrayList<>(); if (file.isDirectory()) { File[] files = file.listFiles(); for (File f : Objects.requireNonNull(files)) { fileList.addAll(listFilesInDirWithFilter(f, predicate)); } } else { if (predicate.test(file.getName())) { fileList.add(file); } } return fileList; }
相似的还好比说处理 IO
,直接上代码:
public static void readLine(BufferedReader br, Consumer<String> handle, boolean close) { String s; try { while (((s = br.readLine()) != null)) { handle.accept(s); } } catch (IOException e) { e.printStackTrace(); } finally { if (close && br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } }
方法
说你到底想干吗?!算了,你想干吗就干吗吧,请随意😎~
写的更多也是为了写的更少
,这句话乍一听感受很是矛盾,可是编程经验比较丰富的小伙伴应该能体会到方法重载
的威力,尤为是在写工具类或者底层接口的时候,建议你们先写一个大而全的内部方法
,而后一点点去根据须要重载它,会有意想不到的好处。
最简单的例子,以下:
// Root 方法 private static void readLine(BufferedReader br, Consumer<String> handle, boolean close) { String s; try { while (((s = br.readLine()) != null)) { handle.accept(s); } } catch (IOException e) { e.printStackTrace(); } finally { if (close && br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } } // 重载方法一 public static void readLine(String path, Consumer<String> handle, boolean close) { try { BufferedReader br = new BufferedReader(new FileReader(path)); readLine(br, handle, close); } catch (FileNotFoundException e) { e.printStackTrace(); } } // 重载方法二 public static void readLine(String path, Consumer<String> handle) { readLine(path, handle, true); }
重载
可让咱们的方法调用方式变得丰富多彩,在语义明确的状况下,写代码有如神助,配合函数式编程,可让工具类或者底层接口的能力大大加强。
同时,当咱们须要调整某一个方法逻辑时,也可使用继续重载
的方式,将影响面降到最小,尽可能不动其余模块的代码。
与其说是如何写最少的代码
,不如说是:如何只写真正有价值的代码
。
面对这种问题的时候,咱们第一反应确定就是设计模式了,例如上文的泛型章节提到的模板方法模式
,小小的推荐一下我以前的文章:
经过良好的设计模式或者其变形体,咱们能够获得高内聚低耦合的代码,这是一个很是好的思路。
另外一个思路,全部人都认同一点:程序 = 算法 + 数据结构
,选择好正确的数据结构能够事倍功半,好比说咱们作相似文件夹需求
的时候,会想到使用链表
或者树
结构,在作如:如何高效的给用户发送生日短信
时会想到用堆
结构(用当前时间对比堆中的最大值,知足则继续迭代,减小遍历)等等。
这其实都是抽象,或深或浅而已,我最开始学习Java的时候,老师会说一句话:万物皆为对象,咱们来看看上面的技巧各自对应着什么?
因此如何只写真正有价值的代码?官方一点的话就是:把变化的抽象出来
,那么到底该怎么抽?
这就须要咱们一点点的去探索了,毕竟奇淫技巧
只是小道尔,不过我会一直探索下去。
若是你以为这篇内容对你有帮助的话: