前言
因为性情起因,笔者很难沉下心来进行庄重的零碎学习,总是喜爱折腾一些奇淫技巧,十分喜爱代码设计,扣代码的细节,所以本次分享一下我所晓得的如何写起码的代码的小技巧,如果你有更好的计划,欢送在评论区留言,计划很棒的话,加我微信,为你送上冬天的一杯奶茶~
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;
}
}
复制代码
测试类:
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;
}
}
复制代码
实质还是一般对象,然而加上泛型后威力剧增!因为在办法定义时就强制了泛型束缚,语义十分清晰,同时能够齐全杜绝上述的无语义两头类,当然一些必要的,有业务含意的组装类,不倡议应用这种形式。
泛型:我想 new 一个对象
大家在学 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);
}
复制代码
重载能够让咱们的办法调用形式变得丰富多彩,在语义明确的状况下,写代码有如神助,配合函数式编程,能够让工具类或者底层接口的能力大大加强。
同时,当咱们须要调整某一个办法逻辑时,也能够应用持续重载的形式,将影响面降到最小,尽量不动其余模块的代码。
终极:从设计模式到形象
与其说是如何写起码的代码,不如说是:如何只写真正有价值的代码。
面对这种问题的时候,咱们第一反馈必定就是设计模式了,例如上文的泛型章节提到的模板办法模式,小小的举荐一下我之前的文章:
【一起学系列】之模板办法:写 SSO 我只有 5 分钟
设计模式总篇:从为什么须要准则到理论落地
通过良好的设计模式或者其变形体,咱们能够失去高内聚低耦合的代码,这是一个十分好的思路。
另一个思路,所有人都认同一点:程序 = 算法 + 数据结构,抉择好正确的数据结构能够事倍功半,比如说咱们做相似文件夹需要的时候,会想到应用链表或者树结构,在做如:如何高效的给用户发送生日短信时会想到用堆构造(用以后工夫比照堆中的最大值,满足则持续迭代,缩小遍历)等等。
这其实都是形象,或深或浅而已,我最开始学习 Java 的时候,老师会说一句话:万物皆为对象,咱们来看看下面的技巧各自对应着什么?
多返回值:封装对象 + 泛型束缚
泛型:封装对象的公共接口,高度形象
函数式办法:把办法当作一个对象
重载:对象办法(行为)的一直演变
所以如何只写真正有价值的代码?官网一点的话就是:把变动的形象进去,那么到底该怎么抽?
这就须要咱们一点点的去摸索了,毕竟奇淫技巧只是小道尔,不过我会始终摸索上来。
https://juejin.cn/post/690589…
欢送关注公众号【码农开花】一起学习成长
我会始终分享 Java 干货,也会分享收费的学习材料课程和面试宝典
回复:【计算机】【设计模式】【面试】有惊喜哦