Traceback.java
3.8 KB
#Java 咳咳,既然频道只有认识我的几位 dalao,我就发下链接了。其实这个是我重写 drakeets 上面的。
#Java 下面我会对 Promise 进行 Jawa 建模,过后有神秘分享哦(非技术向(跑,其实不值得期待
什么是 Promise 呢?英文中 Promise 的意思是保证。这里的 Promise 的意思则是 ECMAScript 6 新推出异步编程的一项特性。
异步编程是什么? Android 开发者会举出一种简单例子:
别的例子,AsyncTask 封装(还有一个)
不要忘了,最终执行你程序的还是机器处理器,所以所谓『异步』还是机器提供的特性。
『异步』,有时候是从其他『线程』在做的处理,但多数情况下都和处理器中断有关。
为了演示方便和篇幅考虑,我选择用汇编给 x86 DOS 写类似上面的那个例子... 按下按键的时候就(越过 DOS 操作系统地)输出字符
https://wiki.osdev.org/Interrupts
http://spike.scu.edu.au/~barry/interrupts.html#ah02
总之,我们可以认为『异步』就是不在我们的『串行』程序逻辑执行过程中被处理的东西,它有一个额外的处理进程。
而异步和我们有个毛线的关系?关系只在不同的『处理进程』发生联系的时候出现,例如需要有某种『信号』在线程间传递,打断正常的串行执行(或许这种打断是被预期的,也可能并没用打断,而是主动执行分派),让异步信号得到处理。
这是『异步信号』(类似 Qt 的信号/插槽)模型,比较通用。因为其他进程的执行状态,抽象成信号也是很直白的。不过注意这个『信号』是抽象的,不代表信号就应该怎么样地(例如在『线程』 A)被处理。
当然,也有『回调』,实际上就是把信号处理固定在任务执行的线程进行了(
以上是信号的抽象,可以认为是一种
所谓 Promise,它的意思是这项特性『保证』了异步任务的状态转移存在某种规律:
states
pending (reject)~> rejected (catch)
初始状态是 pending,代表任务正在执行
除了这样以外没用其他可能,不能从 resolved 变回 pending 或者转移到 rejected,这就是 Promise 的含义。
那么终于进入正题了:Java 里该如何抽象?
然后有个
还应该实现 Promise.resolve, Promise.all, Promise.race, Promise#done, Promise#catch 方法 🤔
什么是 Promise 呢?英文中 Promise 的意思是保证。这里的 Promise 的意思则是 ECMAScript 6 新推出异步编程的一项特性。
异步编程是什么? Android 开发者会举出一种简单例子:
Button.OnClickListener exitApplication = (_) -> { finish(); };
assert exitApplication == new Button.OnClickListener() {
@Override void onClick(View _) { this.finish(); }
};
btnExit.setOnClickListener(exitApplication);
finish() 是 Activity 的方法,它标记当前活动状态为已结束。别的例子,AsyncTask 封装(还有一个)
class AsyncTask<R>(work: () -> R): AsyncTask<Void, Nothing, R> {
private var result: R
var onResult: Function<R, *>?
override fun doInBackground(vararg parms: Void) { result = work() }
override fun onPostExecute(result: R) = onResult?.invoke(result)
}
val dl = AsyncTask { downloadUrl("https://baidu.com/favicon.ico") }
dl.onResult = { println(it.size) }
dl.execute()
Web stackholder 则会举出更简单的例子(因为浏览器环境毕竟和 app 不同的原因,权限控制有区别,请大家忽视处理上的一些区别...):btnExit.onclick = () => window.close();
Qt 和 GObject 的开发者则会使用 Signal & slotsconnect(btnExit, SIGNAL(onClick()), this, SLOT(closeAllWindows()));C# 的开发者会使用 C# 的 Event
+=
btnExit.click += () => Environment.Exit(0);最直白的例子还是 C/C++ 使用 Signal 的时候,此时没有那么多额外的东西干扰视线(比如对象对数据的封装,有时候可以把对象理解为操作被抽提的模块)
#include <csignal>当然最最直白的特性还是为特定机器编程时设置的 interrupt 处理器(跑
#include <iostream>
using namespace std;
void doTrapSigInt() {
signal(SIGINT, [](int s) { cout << "SIGINT ("<<s<<") Received" << endl; });
}
[[noreturn]] int main() {
doTrapSigInt();
for(;;){}
}
不要忘了,最终执行你程序的还是机器处理器,所以所谓『异步』还是机器提供的特性。
『异步』,有时候是从其他『线程』在做的处理,但多数情况下都和处理器中断有关。
为了演示方便和篇幅考虑,我选择用汇编给 x86 DOS 写类似上面的那个例子... 按下按键的时候就(越过 DOS 操作系统地)输出字符
https://wiki.osdev.org/Interrupts
http://spike.scu.edu.au/~barry/interrupts.html#ah02
bits 16啊又跑偏了... 上面的程序在 DOSBox 里测试是失败的 不知道为什么...(求不喷没用 marco)
kbdval db 0x60
pic db 0x20
sys db 0x21
DOS_displaychar db 0x02
ivt_kbd db 9*4
_entry:
cli
mov word [ivt_kbd], kbdInt
mov word [ivt_kbd+2], 0
sti
mov al, 'D'; call putc_al
mov al, 'o'; call putc_al
mov al, 'n'; call putc_al
mov al, 'e'; call putc_al
mainloop:
hlt ; wait next interrupt
jmp mainloop
kbdInt:
push eax
in al, kbdval
call putc_al
mov al, pic
out pic, al
pop eax
iret
putc_al:
mov ah, DOS_displaychar
mov dl, al
call sys
ret
总之,我们可以认为『异步』就是不在我们的『串行』程序逻辑执行过程中被处理的东西,它有一个额外的处理进程。
而异步和我们有个毛线的关系?关系只在不同的『处理进程』发生联系的时候出现,例如需要有某种『信号』在线程间传递,打断正常的串行执行(或许这种打断是被预期的,也可能并没用打断,而是主动执行分派),让异步信号得到处理。
这是『异步信号』(类似 Qt 的信号/插槽)模型,比较通用。因为其他进程的执行状态,抽象成信号也是很直白的。不过注意这个『信号』是抽象的,不代表信号就应该怎么样地(例如在『线程』 A)被处理。
当然,也有『回调』,实际上就是把信号处理固定在任务执行的线程进行了(
以上是信号的抽象,可以认为是一种
Thenable, 只有一个方法 then(Listener),反正信号除了可以『监听』也没啥用了。所谓 Promise,它的意思是这项特性『保证』了异步任务的状态转移存在某种规律:
states
pending (0), resolved, rejected
pending (resolve)~> resolved (then)pending (reject)~> rejected (catch)
初始状态是 pending,代表任务正在执行
new Promise((resolve, reject) => { resolve(1) }).then((it) => console.log(it))
pending 时通过 resolve 箭头,我们状态转移到了 resolved 状态,并且 then 将会接收到此信号pending 时通过 reject 箭头,我们状态转移到了 rejected 状态,并且 catch 将会接收到此信号除了这样以外没用其他可能,不能从 resolved 变回 pending 或者转移到 rejected,这就是 Promise 的含义。
那么终于进入正题了:Java 里该如何抽象?
interface Thenable<R> { void then(Consumer<? super R> next); }
interface Failable<R> extends Thenable<R> { void then(Consumer<? super Result<R>> failable_next); }
这是我们对最基础『异步任务』的抽象:可以失败的东西然后有个
abstract class Promise 来扩展它还应该实现 Promise.resolve, Promise.all, Promise.race, Promise#done, Promise#catch 方法 🤔
Gist
Essay: Android cancellable asynchronous task API abstractoin in Kotlin
Essay: Android cancellable asynchronous task API abstractoin in Kotlin - Essay-Android-AsynchronousTaskAbstractoin.kt
🤔 #Java #task 打算写一个上下文依赖注入(CDI)库,不过不是最近的事情
就起名叫做 Pot,钦定以下使用:
也打算支持使用 Singleton 模式
Pot 应该支持依赖链条(比如 Lock 有无参数架构器,实例还有别的依赖)
此外,循环依赖也应该被良好的处理(创建先后有日志输出)
Pot 可以支持两种创建注入对象的方法:
1.
2.
可以选择
Pot 支持按照反射参数化类型查找实例
不过其实组件化到这个地步也是足够的...
就起名叫做 Pot,钦定以下使用:
@pot abstract class Door {
@implicit public Lock locker;
public void open(String password) {
locker.checkPass(password);
openDoor();
}
private abstract void openDoor();
}
这是最基本的 Bean 用法,除了使用 public @implicit, 还可以用private @implicit Lock locker;
public @give(Lock.class) setLocker(Lock lck) { locker = lck; }
Door(@give Lock lck) { lock = lck; }
此外,还可以private Dependency<Lock> locker;
然后在 constructor 里locker.ensure();
当然 pot 的 annotation processor 可以自动生成这种 constructorDoor() { this.injectDependencies(); }
Pot 当然是全局的,要不然也无法提供注入了也打算支持使用 Singleton 模式
Pot 应该支持依赖链条(比如 Lock 有无参数架构器,实例还有别的依赖)
此外,循环依赖也应该被良好的处理(创建先后有日志输出)
class Van {
@ImplicitContext
public @interface DeepDarkHome {}
@ImplicitContext
public @interface Jam {}
}
@DeepDarkHome
@pot abstract class VanHomeDoor {
@implicit public Lock locker; // with context DeepDarkHome
public void open(String password) {
locker.checkPass(password);
openDoor();
locker = Pot.refresh(locker, Jam.class); // dropped, re-created with context Jam
locker = Pot.rebuild(lock, VanHomeDoor.class) // re-created
}
private abstract void openDoor();
}
🤔 这样?我不是很熟悉 CDIPot 可以支持两种创建注入对象的方法:
1.
@provides(...) 标记的方法2.
@provides(...) 标记的架构器可以选择
Pot 支持按照反射参数化类型查找实例
@implicit List<User> storageUserList;
...@provides List<User> loadUserListFromFile() {...}
...@provides(cacheRefresh = 0) List<User> refreshUserListFromFile() {...}
需要创建组件(pot) 的实例时:door_Jam = Pot.make(DarkDoor.class, "Jam");
door_Home = Pot.make(DarkDoor.class, "F**king comming");
当然关于那个 CDI 的 C 我不是很确定,以后可能再了解不过其实组件化到这个地步也是足够的...
/tmp/duangsuse.sock
吃上了 Eclipse Java(
🙈 其实这里第一个例子 #Java #Android 的
具体情况是下面语言实现的层面考虑的
assert 是错的... 大家看着理解就好了,我的意思主要关于『等价』具体情况是下面语言实现的层面考虑的
Button.OnClickListener exitApplication = ($) -> { ctx.finish(); };
assert exitApplication == new Button.OnClickListener() {
@Override void onClick(View _) { ctx.finish(); }
};
另外原来的 this 有个遮盖的问题,Kotlin 语法可以所以能用 this@scope 解决,Java 就没有办法惹https://coolshell.cn/articles/3005.html #Java
对 JavaEE 的复杂化讽刺的很好.... 不得不说,这个例子里对设计模式的使用的确非常无聊,什么 DirectoryFactory, Directory, Descriptor, Service, Model Factory ServiceLocator, Model Factory, Model, Bean getter, Context... 我去... 起一大堆有时候还真是不必要的名字,到处『松耦合』... 我都快记不住这些名词了,什么 Adapter、什么 Strategy、Facade、Command... 尤其是 Adapter 什么的,起一个莫名其妙的名字,真的有必要? loadFromFile / writeTo(OutputImpl) 有这么困难?为什么非得取个带设计模式的名字? Kotlin 的 『Singleton 模式』就是一个
对 JavaEE 的复杂化讽刺的很好.... 不得不说,这个例子里对设计模式的使用的确非常无聊,什么 DirectoryFactory, Directory, Descriptor, Service, Model Factory ServiceLocator, Model Factory, Model, Bean getter, Context... 我去... 起一大堆有时候还真是不必要的名字,到处『松耦合』... 我都快记不住这些名词了,什么 Adapter、什么 Strategy、Facade、Command... 尤其是 Adapter 什么的,起一个莫名其妙的名字,真的有必要? loadFromFile / writeTo(OutputImpl) 有这么困难?为什么非得取个带设计模式的名字? Kotlin 的 『Singleton 模式』就是一个
object Simpleton {}, 模板双检锁 Singleton 应该被视为耻辱,而不是了不得的见闻酷 壳 - CoolShell
代码重构的一个示例 | | 酷 壳 - CoolShell
还记得以前和大家提到过的《各种流行的编程风格》吗?有一些人问我那些编程风格具体是什么样子的。下面是一个代码重构的实例,让我们看看那个流行的编程风格是实践是什么样的。下面的这个实践不是虚构,如有雷同,请对号入座。 首先,我们有一个表达式如下所示: 很明显,这个表达式的变量名太没意义了,很不利于程序的可读性,所以,我们需要取一个有意义的变量名: 很好,不过,那个常量7是hard-code或是一个Mag
https://github.com/tzh476/zvm/issues/2#issuecomment-575976613
居然有人叫我大神真的好兴奋呢。 #GitHub #Java #Kotlin
其实
定义式 declarative 的 Kotlin。
居然有人叫我大神真的好兴奋呢。 #GitHub #Java #Kotlin
其实
org.duangsuse.bin 现在本来是有很大的问题,应该重构,但实际上已经比那些传统的手段高到不知哪里去了,定义式不是 #Scala 的专利。定义式 declarative 的 Kotlin。
GitHub
关于类解析的实现,可以参考的另一种风格 · Issue #2 · tzh476/zvm
https://github.com/tzh476/zvm/blob/master/src/main/java/com/zvm/classfile/ClassFile.java#L129 suse 写过一个类似的实现,能够解析常量池。 https://github.com/duangsuse-valid-projects/SomeAXML/blob/master/src/commonTest...
/tmp/duangsuse.sock
https://github.com/rubenlagus/TelegramBotsExample/blob/master/src/main/java/org/telegram/updateshandlers/DirectionsHandlers.java#L60 咱吐了。咱从很早以前都不写这种程序了,如今见教、见教。
我真是不想对这种代码说什么了。 #Java #statement
从 Eclipse 上都可以看得出,『Java 软件工程』简直是个笑话,通通的过度设计。
从设计模式上来讲,Eclipse IDE 可以说是很遵循教科书了,可惜还是 IDEA 技高一筹,不仅提供花样繁多的 refactor,而且带各种 inspection、quick fix、智能感知,界面还简洁、不是像宇宙一样过度混杂,这点 Eclipse 可以说是数十年没有长进。
我不是很了解,当前也没啥资料可以翻阅,不过 IDEA 既然做得好,当然是有做得好的缘由的。
坦白吧。之前我在给某个 Qt 5.14 的 C++ 程序加功能的时候,也是这个熊样,我当时感觉自己复制了许多相当冗长用于设置 UI 的代码,然后补充了少数为了给程序引入一个参数的代码,最后完成了,但最核心的改动不过是加了一个映射到 QSlider 的变量。
Java 的 Checked Exceptions,我也坦白了,当年啥都不懂的我第一反应是记下这个名词,因为它好大佬啊,那是我对 Java 语言一些『深层次』的设计细节第一次的了解,要是我能稍微把它和 Haskell 的 Either、Rust 和 Kotlin 的 Result,能联系起来也好啊。
当年我不是写不出小应用,尽管的确是连 Android 的 ArrayAdapter 都不会用,但还是写得出来点 EditText 文本处理什么的,尽管是抄基础模式,不自己理解。
以前我是觉得越多越好,现在我是觉得越少越好。少能分出主次,与人方便自己方便,我再也不做机器该做的工作。
就是这样,毫不在意别人的可读性和项目的可维护性,现在是如此,以后假如要换一个持久化存储的数据和操作模型,或者换成 Future 的 request 接口,那些代码,通通不能用、是废物。通通得重写、再改。
这个过程往往会引入更多新 bug,我都不敢想修完一个,再自动甚至手动测试后发现不能用,而且是因为另一个地方同类的问题忘改了,再返工,那是一种什么样的工作。
能把代码写成这样,只有一种原因,就是你完全是『见机行事』,先觉得需要是那样做,然后丝毫不多想想有哪些通用模式,就去霸王硬上弓,
或者干脆就是仿冒别人的代码喽,抄过来的你当然不知道怎么裁剪以使之适体,当然更没有那个功夫了。
能把代码写成这样,说明你从来没有重写,以及在很大的层面上重构你的代码。何谓「很大的层面」?比如给一个广泛使用的 class 添加方法,比如把所有阻塞操作改成异步的,比如让你的代码可以在很多个底层抽象平台上工作。
promise & future, observer, task 什么的我不说,就说说 await/async 吧,让看起来顺序执行的代码可以被打断,可以异步,你不需要改原来代码加 then 指定回调,简单吧?
而「可打断的程序」是 Melvin Conway 于 1958 年引入的『协程 coroutine』概念,Kotlin 消灭了 Java 和 Python 的 try with resource 语法的
最关键的问题永远不在你写了多少行,而在你把看起来简单至极的东西表达出了多重的分量。
我想说对于这样生硬的代码来说,基本上就没有复用、移植的可能性,一旦情况稍微有变,或者想要改变一点点数据的获取方式,都“牵一发而动全身”,每次重构都疲于奔波,除了符号重命名基本就没啥了。
可维护性?安全性?鬼知道呢,反正有新功能加新代码就好了,你管它是一个新功能 10 行还是 4 行。
倒不如说是这样的程序员自己的迷信,他们会觉得写的越多、看着越复杂越密集越好,所以喜欢把一些 IDE 都检测不出来的 dead code,比如把默认是那样的属性再显式设一遍,或者严格按照「工程的代码风格」来,大量赋值也不懂得为了可读性加几个分号,换换布局套路,跟个机器人一样,还和电脑抢饭吃。
谁开始的时候不是那样呢,当好一个码农,当然是手工管的地越大越好,王道征途。
谁稀罕函数式的一亩三分地,切。
它根本不知道编程该是什么样,就是往上瞎加代码。
又怎么样。
从 Eclipse 上都可以看得出,『Java 软件工程』简直是个笑话,通通的过度设计。
从设计模式上来讲,Eclipse IDE 可以说是很遵循教科书了,可惜还是 IDEA 技高一筹,不仅提供花样繁多的 refactor,而且带各种 inspection、quick fix、智能感知,界面还简洁、不是像宇宙一样过度混杂,这点 Eclipse 可以说是数十年没有长进。
我不是很了解,当前也没啥资料可以翻阅,不过 IDEA 既然做得好,当然是有做得好的缘由的。
坦白吧。之前我在给某个 Qt 5.14 的 C++ 程序加功能的时候,也是这个熊样,我当时感觉自己复制了许多相当冗长用于设置 UI 的代码,然后补充了少数为了给程序引入一个参数的代码,最后完成了,但最核心的改动不过是加了一个映射到 QSlider 的变量。
Java 的 Checked Exceptions,我也坦白了,当年啥都不懂的我第一反应是记下这个名词,因为它好大佬啊,那是我对 Java 语言一些『深层次』的设计细节第一次的了解,要是我能稍微把它和 Haskell 的 Either、Rust 和 Kotlin 的 Result,能联系起来也好啊。
当年我不是写不出小应用,尽管的确是连 Android 的 ArrayAdapter 都不会用,但还是写得出来点 EditText 文本处理什么的,尽管是抄基础模式,不自己理解。
以前我是觉得越多越好,现在我是觉得越少越好。少能分出主次,与人方便自己方便,我再也不做机器该做的工作。
就是这样,毫不在意别人的可读性和项目的可维护性,现在是如此,以后假如要换一个持久化存储的数据和操作模型,或者换成 Future 的 request 接口,那些代码,通通不能用、是废物。通通得重写、再改。
这个过程往往会引入更多新 bug,我都不敢想修完一个,再自动甚至手动测试后发现不能用,而且是因为另一个地方同类的问题忘改了,再返工,那是一种什么样的工作。
能把代码写成这样,只有一种原因,就是你完全是『见机行事』,先觉得需要是那样做,然后丝毫不多想想有哪些通用模式,就去霸王硬上弓,
或者干脆就是仿冒别人的代码喽,抄过来的你当然不知道怎么裁剪以使之适体,当然更没有那个功夫了。
能把代码写成这样,说明你从来没有重写,以及在很大的层面上重构你的代码。何谓「很大的层面」?比如给一个广泛使用的 class 添加方法,比如把所有阻塞操作改成异步的,比如让你的代码可以在很多个底层抽象平台上工作。
promise & future, observer, task 什么的我不说,就说说 await/async 吧,让看起来顺序执行的代码可以被打断,可以异步,你不需要改原来代码加 then 指定回调,简单吧?
而「可打断的程序」是 Melvin Conway 于 1958 年引入的『协程 coroutine』概念,Kotlin 消灭了 Java 和 Python 的 try with resource 语法的
File("wtf").use { } 用的是 Scheme 编程语言 1975 就实现的『闭包 closure』。最关键的问题永远不在你写了多少行,而在你把看起来简单至极的东西表达出了多重的分量。
我想说对于这样生硬的代码来说,基本上就没有复用、移植的可能性,一旦情况稍微有变,或者想要改变一点点数据的获取方式,都“牵一发而动全身”,每次重构都疲于奔波,除了符号重命名基本就没啥了。
可维护性?安全性?鬼知道呢,反正有新功能加新代码就好了,你管它是一个新功能 10 行还是 4 行。
倒不如说是这样的程序员自己的迷信,他们会觉得写的越多、看着越复杂越密集越好,所以喜欢把一些 IDE 都检测不出来的 dead code,比如把默认是那样的属性再显式设一遍,或者严格按照「工程的代码风格」来,大量赋值也不懂得为了可读性加几个分号,换换布局套路,跟个机器人一样,还和电脑抢饭吃。
谁开始的时候不是那样呢,当好一个码农,当然是手工管的地越大越好,王道征途。
谁稀罕函数式的一亩三分地,切。
它根本不知道编程该是什么样,就是往上瞎加代码。
又怎么样。