构造函数 构造代码块

构造函数中应完成多少工作? 在构造函数内部进行一些计算然后封装结果似乎是合理的。 这样,当对象方法需要结果时,我们将准备好它们。 听起来是个好方法? 不,这不对。 这是一个坏主意,原因有一个:它阻止了对象的组合并使它们不可扩展。

杀死比尔 2(2004)由昆汀·塔伦蒂诺(Quentin Tarantino)

假设我们正在制作一个代表一个人的名字的接口:

interface Name {String first();
}

很简单,对不对? 现在,让我们尝试实现它:

public final class EnglishName implements Name {private final String name;public EnglishName(final CharSequence text) {this.parts = text.toString().split(" ", 2)[0];}@Overridepublic String first() {return this.name;}
}

这怎么了 更快吧? 它仅将名称分成几部分,然后将其封装。 然后,无论我们调用first()方法有多少次,它都将返回相同的值,并且无需再次进行拆分。 但是,这是有缺陷的想法! 让我向您展示正确的方法并说明:

public final class EnglishName implements Name {private final CharSequence text;public EnglishName(final CharSequence txt) {this.text = txt;}@Overridepublic String first() {return this.text.toString().split("", 2)[0];}
}

这是正确的设计。 我可以看到你在微笑,所以让我证明我的观点。

不过,在开始证明之前,请允许我阅读这篇文章: 可组合装饰器与命令式实用方法 。 它解释了静态方法和可组合装饰器之间的区别。 上面的第一个代码段看起来非常像一个对象,它非常接近命令式实用程序方法。 第二个例子是一个真实的对象。

在第一个示例中,我们正在滥用new运算符,并将其转换为静态方法,该方法现在和现在都为我们进行所有计算。 这就是命令式编程的目的。 在命令式编程中,我们立即执行所有计算并返回完全准备好的结果。 相反,在声明式编程中,我们尝试尽可能长时间地延迟计算。

让我们尝试使用我们的EnglishName类:

final Name name = new EnglishName(new NameInPostgreSQL(/*...*/)
);
if (/* something goes wrong */) {throw new IllegalStateException(String.format("Hi, %s, we can't proceed with your application",name.first()));
}

在此代码段的第一行中,我们只是创建一个对象的实例并将其标记为name 。 我们还不想进入数据库并从那里获取全名,将其拆分为多个部分,然后将其封装在name 。 我们只想创建一个对象的实例。 这种解析行为对我们来说将是一个副作用,在这种情况下,将减慢应用程序的速度。 如您所见,如果出现问题,我们可能只需要name.first() ,而我们需要构造一个异常对象。

我的观点是,在构造函数内部进行任何计算都是一种不好的做法,必须避免这样做,因为它们是副作用,对象所有者不要求这样做。

您可能会问,重用name期间的性能如何? 如果我们创建了EnglishName的实例,然后调用了name.first()五次,则最终将对String.split()方法进行五次调用。

为了解决这个问题,我们创建了另一个类,一个可组合的decorator ,它将帮助我们解决这个“重用”问题:

public final class CachedName implements Name {private final Name origin;public CachedName(final Name name) {this.origin = name;}@Override@Cacheable(forever = true)public String first() {return this.origin.first();}
}

我正在使用jcabi-aspects的Cacheable批注,但是您可以使用Java(或其他语言)可用的任何其他缓存工具,例如Guava Cache :

public final class CachedName implements Name {private final Cache<Long, String> cache =CacheBuilder.newBuilder().build();private final Name origin;public CachedName(final Name name) {this.origin = name;}@Overridepublic String first() {return this.cache.get(1L,new Callable<String>() {@Overridepublic String call() {return CachedName.this.origin.first();}});}
}

但是请不要使CachedName可变并且延迟加载-这是一种反模式,我之前在“ 对象应该是不可变的”中已经讨论过。

这是我们的代码现在的样子:

final Name name = new CachedName(new EnglishName(new NameInPostgreSQL(/*...*/))
);

这是一个非常原始的示例,但我希望您能理解。

在此设计中,我们基本上将对象分为两部分。 第一个知道如何从英文名称中获取名字。 第二个知道如何将计算结果缓存到内存中。 现在,作为这些类的用户,我将决定如何正确使用它们。 我将决定是否需要缓存。 这就是对象构成的全部内容。

让我重申一下,构造函数中唯一允许的语句是赋值。 如果您需要在此处放置其他内容,请开始考虑进行重构-您的课程肯定需要重新设计。

翻译自: https://www.javacodegeeks.com/2015/05/constructors-must-be-code-free.html

构造函数 构造代码块

构造函数 构造代码块_构造函数必须没有代码相关推荐

  1. Java提高篇——静态代码块、构造代码块、构造函数以及Java类初始化顺序

    构造函数 public HelloA(){//构造函数} 关于构造函数,以下几点要注意: 1.对象一建立,就会调用与之相应的构造函数,也就是说,不建立对象,构造函数时不会运行的. 2.构造函数的作用是 ...

  2. 静态代码块、构造代码块、构造函数、普通代码块的区别

    本文转自:IT可乐的博客 在Java中,静态代码块.构造代码块.构造函数.普通代码块的执行顺序是一个笔试的考点,通过这篇文章希望大家能彻底了解它们之间的执行顺序. 1.静态代码块 ①.格式 在java ...

  3. 【Kotlin】Kotlin 构造函数 ( 主构造函数 | 主构造函数声明属性 | init 初始化代码块 | 次构造函数 | 构造函数委托 | 调用构造函数创建实例对象 )

    文章目录 I . 主构造函数 II . 主构造函数声明属性 III . init 初始化代码块 IV . 主构造函数参数 和 成员变量访问方式 V . 主构造函数 可见性 设置 VI . 次构造函数 ...

  4. 静态代码块、非静态代码块、构造函数执行顺序

    静态代码块.非静态代码块.构造函数执行顺序 /*** 类的实例化顺序,静态代码块.非静态代码块.构造函数.* @author MING*/ class Person {static String na ...

  5. 静态代码块、非静态代码块、构造函数三者执行顺序

    主要探讨一下关于静态代码块,非静态代码块,构造函数的执行顺序. 如有错误,欢迎指出. 首先: 静态成员变量和静态代码块的优先级是一样的,先定义的先执行. 在创建一个对象的时候会执行非静态代码块和构造函 ...

  6. java 代码块_详解java中的四种代码块

    在java中用{}括起来的称为代码块,代码块可分为以下四种: 一.简介 1.普通代码块: 类中方法的方法体 2.构造代码块: 构造块会在创建对象时被调用,每次创建时都会被调用,优先于类构造函数执行. ...

  7. java 静态代码块_关于Java你不知道的那些事之代码块

    前言 普通代码块:在方法或语句中出现的{},就被称为代码块 静态代码块:静态代码块有且仅加载一次,也就是在这个类被加载至内存的时候 普通代码块和一般语句执行顺序由他们在代码中出现的次序决定,先出现先执 ...

  8. 父类静态代码块、非静态代码块、构造方法、子类静态代码块、子类非静态代码块、子类构造方法执行顺序

    父类 public class Father {static{System.out.println("父类静态代码块");}{System.out.println("父类 ...

  9. java gui构造工具_Java Web框架 静态代码块、构造代码块、构造函数、普通代码块 执行顺序 Decompiler JD-GUI 反编译工具...

    1.下载jd-gui-windows-1.4.0. http://jd.benow.ca/ 2.通过jd-gui.exe查看.class文件,用于分析类编译过程. 3.源文件. // 加载相应的 He ...

最新文章

  1. tomcat更换服务器后无法显示验证码
  2. 运营人必备的7大技能:数据分析能力是未来运营的分水岭
  3. Spring Cloud构建微服务架构:分布式服务跟踪(抽样收集)【Dalston版】
  4. Css2.0+Css3.0+jQuery手册 chm
  5. c语言键盘回调函数键盘的码,深入浅出剖析C语言函数指针与回调函数(三)
  6. C++ Primer 5th笔记(10)chapter10 泛型算法 :bind
  7. G6 图可视化引擎——入门教程——插件与工具
  8. 批量修改数据_#泰Q头条#065期 四步搞定Excel表中的批量数据修改
  9. ue4导入倾斜摄影_倾斜摄影建模干货|还怕搞不定CC空三?这里只要5分钟……
  10. Kafka生成消息时的3种分区策略
  11. 为什么浏览器的用户代理字符串以 Mozilla 开头?
  12. ubuntu下caffe的FCN8模型训练
  13. 【色彩管理】色彩管理之灰平衡
  14. 【Books系列】席慕蓉《回眸》欣赏
  15. UNITY个人版设置深色主题
  16. 友谊:铭记恩惠忘记伤害
  17. 细品这杯香浓的咖啡——阿里中间件高级专家沈询的Java之旅
  18. 【赛百味加入区块链试验项目以提高食品供应链透明度】GBCAX
  19. react中ref使用方法解析
  20. Python Project

热门文章

  1. jzoj5702-[gdoi2018day2]滑稽子图【树形dp,二项式定理】
  2. jzoj4815-ksum【堆】
  3. P2814-家谱【图论,并查集,std map库】
  4. HDU 5008 Boring String Problem ( 后缀数组求本质不同第k大子串)
  5. 【数论】疯狂 LCM(P1891)
  6. IQ测试(jzoj 5048)
  7. 【贪心】Sunscreen(poj 3614/luogu 2887)
  8. 是男人就过 8 题--Pony.AI 题 - A String Game
  9. Java面试,如何在短时间内做突击
  10. TCP为什么是三次握手和四次挥手