共计 5257 个字符,预计需要花费 14 分钟才能阅读完成。
前言
因为性情起因,笔者很难沉下心来进行庄重的零碎学习,总是喜爱折腾一些奇淫技巧,十分喜爱代码设计,扣代码的细节,所以本次分享一下我所晓得的 如何写起码的代码
的小技巧,如果你有更好的计划,欢送在评论区留言,计划很棒的话,加我微信,为你送上冬天的一杯奶茶~
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 泛型之初有没有这种想法?我想利用 <T> 作为泛型束缚,却须要 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 的时候,老师会说一句话:万物皆为对象,咱们来看看下面的技巧各自对应着什么?
- 多返回值:封装对象 + 泛型束缚
- 泛型:封装对象的公共接口,高度形象
- 函数式办法:把办法当作一个对象
- 重载:对象办法(行为)的一直演变
所以如何只写真正有价值的代码?官网一点的话就是:把变动的形象进去
,那么到底该怎么抽?
这就须要咱们一点点的去摸索了,毕竟 奇淫技巧
只是小道尔,不过我会始终摸索上来。
最初
如果你感觉这篇内容对你有帮忙的话:
- 当然要点赞反对一下啦~
- 另外,搜寻并关注公众号「是 Kerwin 啊」,一起在技术的路上走上来吧~ ????