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

杀死比尔 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. 静态代码块、构造代码块、构造函数、普通代码块的区别

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

  2. 婴儿的出生为你解惑构造函数和构造代码块

    序言: 今天回想这几天走过的Java路程,发现漏了一个点,就是构造代码块,我绞尽脑汁,也没想起来它的作用,我骗不了我自己,就搜相关资料,重新学习,在学习的过程中发现构造代码块和构造函数有一定的联系,为 ...

  3. 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 ...

  4. 构造函数 构造代码块_构造函数必须没有代码

    构造函数 构造代码块 构造函数中应完成多少工作? 在构造函数内部进行一些计算然后封装结果似乎是合理的. 这样,当对象方法需要结果时,我们将准备好它们. 听起来是个好方法? 不,这不对. 这是一个坏主意 ...

  5. Java中this关键词与构造函数,构造代码块的理解

    目录 一.构造函数的理解 1.一个类的创建 二.代码块的理解 三.this关键词的理解 一.构造函数的理解 1.一个类的创建 public class Person {String name; // ...

  6. 1、构造函数、静态代码块、构造代码块执行顺序

    1.结论: 静态代码块>构造代码块>构造函数的方法体 静态代码块只能访问静态方法,变量 2.细化构造函数体执行前会先执行: 2.1 执行父类的super() 2.2 初始化非静态变量(基本 ...

  7. 构造函数和构造代码块

    1/*构造代码块虽然实际开发很少见,但是在面试的笔试中可能会遇到,所以记录一下*/ class PersonDemo2 2 { 3 public static void main(String[] a ...

  8. 有参构造函数调用无参构造函数,构造代码块会执行两次么?

    测试代码demo: import java.util.concurrent.atomic.AtomicInteger;/*** 有参调用无参方法,构造代码块会执行两次么?*/ public class ...

  9. C++ 笔记(17)— 类和对象(构造函数、析构函数、拷贝构造函数)

    1. 构造函数 构造函数是一种特殊的函数(方法),在根据类创建对象时被调用.构造函数是一种随着对象创建而自动被调用的函数,它的主要用途是为对象作初始化. 构造函数的名称与类的名称是完全相同的,并且不会 ...

最新文章

  1. Centos7上安装docker 详细教程
  2. AI 版 Nature Index 排名,两种结果折射中国 AI 实力软肋
  3. 【编程语言】Java基础进阶——面向对象部分
  4. Java标签Label,如何不使用continue和break跳出循环
  5. Python Django 装饰器模式之三阶装饰器
  6. oracle pl/sql 函数
  7. ios15之把自己编写的框架上传到CocoaPods里面
  8. 单元测试 问题描述_单元测试技巧:创建描述性测试
  9. ai人工智能在手机的应用_强化学习在人工智能中的应用
  10. python opencv2_Python + OpenCV2 系列:2 - 图片操作
  11. 吴恩达机器学习笔记一
  12. Android 系统(126)---Android的死机、重启问题分析方法
  13. 关于JAVA是值传递还是引用传递的问题
  14. matlab现值与终值函数_个人家庭投资理财基础(二 单利、复利、终值、现值、内部收益率)...
  15. rds基于什么开发_玩物得志: 基于DataWorks+MaxCompute+MC-Hologres 构建大数据平台
  16. Java 删除session实现退出登录
  17. 桌面计算机找不到硬盘,bios找不到硬盘完美解决方法 选择STATConfigur
  18. 手写原笔迹输入_手写原笔迹输入SurfacePro使用更轻松
  19. python模拟实现QQ邮箱登录
  20. iOS 贝塞尔曲线初探

热门文章

  1. A configuration error occurred during startup.Please verify the preference field with the prompt: To
  2. Eclipse把默认为Gbk的编码变为UTF-8
  3. Mysql8.0可以使用解压版 这个比较快 好像现在都是解压版了
  4. FE助手 json格式化 reslet client
  5. python爬新闻并保存csv_用python爬取内容怎么存入 csv 文件中
  6. review_core_basic_java(1)java程序设计概述
  7. Dijkstra 算法——计算有权最短路径(边有权值)
  8. 量角器中Selenium定位器的完整指南(示例)
  9. linux使jdk开机可用_JDK 11的一般可用性
  10. apache ignite_Apache Ignite变得简单:第一个Java应用程序