个人介绍

刘光聪,程序员,敏捷教练,开源软件爱好者,具有多年大型遗留系统的重构经验,对OOFPDSL等领域具有浓厚的兴趣。

  • 还是要坚持体育锻炼

  • 思维套图用的啥工具呀?颜值挺高的(*_*)

  • @hkliya should方法是ScalaTest通过「隐式转换」规则,给Any的类型增加的方法,这是Scala常用的DSL设计的方法。可以举另外一个例子,问题与本例等价:给Any增加操作符+,以便追加「字符串」。

    val s = 1 + "2"  // s: String = 12
    

    之所以1能够支持+操作,取决于Predef有如下定义的「隐式转换」规则,能将任意类型Any,增加+方法。ScalaTestshould方法,也应该用类似的机制实现;但绝不是像RSpec实现should的动态方法。所以,Scala虽然是一门静态语言,但实现DSL可以与Ruby相媲美了。

    object Predef {
      implicit final class ToStringAdder[A](private val self: A) extends AnyVal {
        def +(other: String): String = String.valueOf(self) + other
      }
      ...
    }
  • @asj 因为需求中只是为了看某个单词是否在这个集合中,为了举例,数据少了点,其实是关键字集合。使用HashSet可以将查找的时间复杂度从O(n)优化至O(logn)

  • @hkliya 跑了一下代码,有一个结果不得不说,结果如下:

    super-->static
    child-->static
    Father Non-Static
    super's x=100
    age=0    // 特殊的输出结果                       
    Child Non-Static
    The other constructor
    child constructor body: 30
    x=200

    解释

    多态什么时候生效?

    类初始化后,类与类的继承关系,方法列表等信息JVM已经获取了,多态调用机制(犹如C++的虚表)已经具备工作的条件了。为此,在Father的构造函数调用了可被覆写的函数age(不好的编码实践),按照预期,age的调用结果与多态机制一致。

    零值初始化

    但因为在初始化Father对象时,Child对象还未真正地初始化,Java默认地将Child对象的空间进行了零值初始化处理,为此,此刻Child对象的n0

    结论:

    不要企图在构造函数中调用可以被覆写的函数,以避免运行时的不确定性;一般地,构造函数一般只调用private(隐含了final的语义)的方法。

  • @hkliya 我的答案和你一致。Java初始化的顺序:

    1. 类始化化(类加载时)
    2. 对象初始化
    3. 先父后子
    4. 从上而下
  • #1楼 @sunysen 已经分享过了,当时用的是微信语言,没有积累沉淀。

  • #1楼 @chenge 动态语言都很难重构,IDE相对于静态语言也相对不太成熟。但重构是一种习惯,没有重构工具,也是能做重构,只是速度慢些,工作效率不高,也容易出错。

    我在重构Ruby, Erlang时,也会出现一些低级错误,但总结还是我自己没有遵循良好的重构原则,例如步子太大扯了蛋,同时干两件事情等等不良习惯,不经意间引入了低级错误。更重要的是,由于是动态语言,缺乏测试覆盖的情况,我在运行时不经意地埋了好几个雷。所以,对于动态语言,我的经验是:TDD和重构“双剑合璧”的软件设计。静态语言,重构相对会好一些,至少不会犯低级的错误。

    语言的选择,我也很谨慎。因为要精通一门语言,至少要修炼3年时间,这是一个很大的机会成本。我的原则:精通主流语言,尝试不同思维。

    为此,我选择C++, Java作为我最精通的语言,胜任大部分的工作;脚本语言我要精通:Ruby, Shell,提高工作效率;函数式我选择了Scala,目前也在为此付出较大的精力。其他语言,我只是尝试,多听多练多学多做足以,帮助我在精通的这几门语言上更多的思考,帮助提高设计的水准,除非工作将我逼上了绝路(_)

  • #4楼 @chenge 我也得向楼主学习Clojure,我还是门外汉呢。不变性是「函数式」的思维,我也很推崇这样的「函数式」思维。「不变形」的确给设计带来诸多的益处,即使是「指令式」语言也会提倡「不变性」的良好「设计原则」。所以我很赞同楼主的观点。

    OO makes code understandable by encapsulating moving parting, but FP makes code understandable by minimizing moving parts. -Michael Feathers

    OO通过封装,抽象,依赖管理将可变性控制恰当,为此良好的OO设计必然是SOLID的,可组合的;FP的思路与众不同,与其控制其变化,不如让其不变。但是,这两种方法论殊途同归,都是为了做到更简单的、正交的、高内聚低耦合的设计。

    对于ClojureScala,我选择了Scala,这完全是因为项目的原因。我相信「函数式」的高阶抽象,可以做到更好的「组合式设计」。但函数式也不是软件工程的唯一银弹,我更偏爱多范型的语言,例如C++,Scala,而不是所有问题只有一种思维,一种解法。至于选择函数式,面向对象,还是泛型,还得具体问题具体分析,关键看设计能否反映问题的本质。

  • 对于类型系统的推演能力,拿几个例子对比一下,就知道C++到底难不难了。

    Java的类型系统

    几乎没有类型推演机制,这个工作完全推给了程序员,所以你不得不写很多「样板代码」。

    Map<String, Map<Integer, String>> map = new HashMap<String, Map<Integer, String>>();

    这种情况,直至Java7/8引入了部分的自动推演能力才得到了「些许」改观,但程序员依然承受沉重的心智负担。

    尤其,Java1.5引入的泛型设计,完全可以秒杀普通的Java程序员。以java.util.Comparator为例。

    public interface Comparator<T> {
        public static <T, U> Comparator<T> comparing(
                Function<? super T, ? extends U> keyExtractor,
                Comparator<? super U> keyComparator)
        {
            return (Comparator<T> & Serializable)
                (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
                                                  keyExtractor.apply(c2));
        }
    
       ......
    }

    再来看C++的模板,功能相对于Java的兼容、适中方案,其功能强大,又能做到如此简单,当属不易了!

    Scala的类型系统

    Scala的「类型系统」与「隐式转换」几乎让90%的程序员面对Scala时望而止步了,复杂度不可言喻。

    动态语言

    没有静态的类型系统,虽然代码可以很简洁,但完全缺乏「类型安全」的保证,将所有错误都推迟到「运行时」。动态语言就是写着「爽歪歪」,运行时「哭歪歪」。