enhao's Notebook

Later equals never


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 链接

  • 搜索

2.2-创建和销毁对象-当构造参数太多时考虑使用builder模式

发表于 2019-04-24 | 分类于 读书笔记 | | 阅读次数:
字数统计: 679 | 阅读时长 ≈ 3

Item 2:当构造参数太多时考虑使用builder模式

可伸缩构造模式

telescoping constructor pattern

这种模式中,包含了多个构造方法,这些构造方法是可选参数的组合。

可伸缩构造模式缺点

  • 难以调用
  • 难以阅读

JavaBeans 模式

无参构造 + setter方法。

JavaBeans 模式缺点

构造方法在多次调用中被分割,所以在构造过程中JavaBean可能处于不一致的状态。

该类没有通过检查构造参数的有效性来执行一致性的选项。在不一致的状态下尝试使用对象可能会导致与包含bug的代码大相径庭的错误,因此很难调试。

变体

当它的构造完成时,手动“冻结”对象,并且不允许它在解冻之前使用,可以减少这些缺点,但是这种变体在实践中很难使用并且很少使用。 而且,在运行时会导致错误,因为编译器无法确保程序员在使用对象之前调用freeze方法。

Builder 模式

结合了可伸缩构造模式的安全性和javabean模式的可读性。

客户端不直接调用所需的对象,而是调用构造方法(或静态工厂),并使用所有必需的参数,并获得一个builder对象。然后,客户端调用builder对象的setter相似方法来设置每个可选参数。最后,客户端调用一个无参的build方法来生成对象,该对象通常是不可变的。Builder通常是它所构建的类的一个静态成员类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// Builder Pattern

public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;

public static class Builder {
// Required parameters
private final int servingSize;
private final int servings;

// Optional parameters - initialized to default values
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;

public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}

public Builder calories(int val) {
calories = val;
return this;
}

public Builder fat(int val) {
fat = val;
return this;
}

public Builder sodium(int val) {
sodium = val;
return this;
}

public Builder carbohydrate(int val) {
carbohydrate = val;
return this;
}

public NutritionFacts build() {
return new NutritionFacts(this);
}
}

private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}

使用:

1
2
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
.calories(100).sodium(35).carbohydrate(27).build();

缺点

  • 创建builder对象有成本。
  • 相比于可伸缩构造更冗余。

使用场景

在有大量可选参数或相同类型参数时使用。

2.1-创建和销毁对象-考虑使用静态工厂方法替代构造方法

发表于 2019-04-23 | 分类于 读书笔记 | | 阅读次数:
字数统计: 781 | 阅读时长 ≈ 3

Item 1:考虑使用静态工厂方法替代构造方法

注意:这里的静态工厂方法static factory method和设计模式中的工厂方法模式Factory Method pattern不等价。

优点

有方法名,便于用于阅读和理解

更加语义化。

特别是有多个构造函数时,容易错误的调用。静态工厂方法有方法名,很容易辨识。

不必每次调用时都创建一个新对象

  1. 允许不可变的类immutable classes使用预先构建的实例。

  2. 构造时缓存实例,避免创建不必要的重复对象。

    可以提高性能,尤其是那种创建他们非常昂贵的情况(耗性能)下。

与构造方法不同,可以返回原类型的子类对象

这个子类可以是隐藏的,即非公开的内部类或包私有类。

例子:java.util.Collections里有很多内部的集合。

返回对象所属类可以根据输入参数变化而变化

举例:EnumSet类的静态工厂方法noneOf方法。如果大多数枚举类型具有64个或更少的元素,静态工厂将返回一个RegularEnumSet实例, 返回一个long类型;如果枚举类型具有六十五个或更多元素,则工厂将返回一个JumboEnumSet实例,返回一个long类型的数组。

这两个实现类的存在对于客户是不可见的。

返回对象所属类在编写含该静态工厂方法的类时可以不必存在

SPI。静态构造方法返回的是该子类对象,但接受的是接口。

举例:JDBC

缺点

只提供静态工厂方法,则没有public/protected构造方法的类不能被子类化

通过继承创建子类。由于子类的构造中要调用父类的构造。因此父类没有public/protected的构造方法则无法子类化。

可以通过组合实现。

很难让开发者找到

不能像构造方法那么突出。可以通过Javadoc标注。

静态工厂方法常用命名

  • from:类型转换方法。接受单个参数并返回此类型的相应实例。

    1
    Date d = Date.from(instant);
  • of:聚合方法,接受多个参数并返回该类型的实例。

    1
    Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING)
  • valueOf:from和of更为详细的替代方法。

    1
    BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
  • instance或者getInstance:返回一个由其参数(如果有的话)描述的实例,但不能说它具有相同的值。

    1
    StackWalker luke = StackWalker.getInstance(options);
  • create或者newInstance:与instance 或 getInstance类似,除了该方法保证每个调用返回一个新的实例。

    1
    Object newArray = Array.newInstance(classObject, arrayLen);

    区别:create或者newInstance每次调用都返回新的实例。instance或者getInstance不一定。

  • getType:和getInstance类似,在工厂方法处于不同的类中的时候使用。

    1
    FileStore fs = Files.getFileStore(path);
  • newType:和newInstance类似,在工厂方法处于不同的类中的时候使用。

    1
    BufferedReader br = Files.newBufferedReader(path);
  • type:getType 和 newType简洁的替代方式。

    1
    List<Complaint> litany = Collections.list(legacyLitany);

《代码不朽》读书笔记

发表于 2018-11-02 | 分类于 读书笔记 | | 阅读次数:
字数统计: 3,404 | 阅读时长 ≈ 12

《代码不朽:编写可维护软件的10大要则》读书笔记。

1 三个基本理论

本书提供了10条可以实现高可维护性的原则。这些原则的理论如下:

理论一:坚持简单的原则最有助于提高可维护性。

理论二:可维护性不是开发完才考虑的事情,而应该是在项目开发的一开始就加以考虑,每个人的贡献都应当计算在内。

理论三:对各个原则的违背会带来不同的影响,有些严重程度甚于其他。一个软件系统越遵循原则,可维护性越高。

阅读全文 »

10 Spring Boot 之消息队列

发表于 2018-09-12 | 分类于 Spring Boot | | 阅读次数:
字数统计: 2,824 | 阅读时长 ≈ 11

1 消息服务规范:JMS&AMQP

1.1 消息服务的作用

消息中间见可以用来提升异步通信,扩展解耦能力。

【异步通信】例如,用户注册时,需要将注册信息写入数据库(50ms),并发送注册邮件(50ms)和注册短信(50ms)。如果采是同步调用,需要150ms相应给用户;如果采用多线程的方式,并发执行发送注册邮件和注册短信,则耗费的时间100ms;如果采用消息队列,当把注册信息写入数据库以后,把接下来的操作写入消息队列(5ms),写入消息队列的时间是很短的,这样总耗费时间55ms。

【应用解耦】例如,有订单模块和库存模块,用户下单时,需要调用订单系统下单,然后调用库存系统减去库存。使用消息队列,将订单模块和库存模块抽取成两个独立系统,将下单信息写入消息队列中,那么库存系统通过订阅消息队列里的订单的内容,库存系统再来计算库存。

阅读全文 »

09 Spring Boot 之缓存

发表于 2018-09-09 | 分类于 Spring Boot | | 阅读次数:
字数统计: 3,861 | 阅读时长 ≈ 16

1 Java缓存规范 JSR107

Java Caching 定义了5个核心接口,分别是CachingProvider、CacheManager、Cache、Entry和Expiry。

  • CachingProvider:缓存提供者。定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期访问多个CachingProvider。
  • CacheManager:缓存管理器。定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有。
  • Cache:缓存。是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有。
  • Entry:是一个存储在Cache中的key-value对。
  • Expiry:有效期。每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个有效期,条目为过期的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。

image-20180902163332355

阅读全文 »

08 Spring Boot 之数据访问

发表于 2018-09-05 | 分类于 Spring Boot | | 阅读次数:
字数统计: 1,147 | 阅读时长 ≈ 5

1 整合 JDBC

使用JDBC需要导入:spring-boot-starter-jdbc和数据库驱动依赖。

一、【数据库连接配置】:

访问数据库,只需要在application.properties/application.yml中配置:

1
2
3
4
5
6
spring:
datasource:
username: root
password: root
url: jdbc:mysql://127.0.0.1:3306/jdbc
driver‐class‐name: com.mysql.jdbc.Driver

数据源相关配置参考:DataSourceProperties。

阅读全文 »

07 Spring Boot 之Docker

发表于 2018-09-04 | 分类于 Spring Boot | | 阅读次数:
字数统计: 515 | 阅读时长 ≈ 2

1 核心概念

docker主机(host):安装了Docker程序的机器,Docker是直接安装在操作系统之上的。

docker客户端(Client):连接Docker主机进行操作。

docker仓库(Registry):用来保存各种打包好的软件镜像。

docker镜像(images):软件打包好的镜像。

docker容器(Container):镜像启动后的实例,称为一个容器。

使用Docker步骤:

  1. 安装Docker。
  2. 去Docker仓库找到需要的镜像。
  3. 使用Docker运行这个镜像,这个镜像就会生成一个容器。
  4. 对容器的操作就是对软件的操作。
阅读全文 »

06 Spring Boot 之文件上传

发表于 2018-09-03 | 分类于 Spring Boot | | 阅读次数:
字数统计: 609 | 阅读时长 ≈ 3

Spring Boot的文件上传,常使用MultipartFile类,该类源自SpringMVC。

一、【文件上传三要素】:

  1. 表单提交方式:method="post"。
  2. 表单类型:enctype="multipart/form-data"。
  3. 表单项中有一个或者多个的file类型。
阅读全文 »

05 Spring Boot 之web开发

发表于 2018-09-02 | 分类于 Spring Boot | | 阅读次数:
字数统计: 4,307 | 阅读时长 ≈ 19

1 Spring Boot 对静态资源的映射规则

Spring MVC 的自动配置在WebMvcAutoConfiguration类中。

资源映射配置的方法:addResourceHandlers()。

欢迎页的配置:welcomePageHandlerMapping()。

1、所有的/webjars/**路径访问,都去classpath:/META-INF/resources/webjars/下找资源。

阅读全文 »

04 Spring Boot 之日志

发表于 2018-09-01 | 分类于 Spring Boot | | 阅读次数:
字数统计: 1,146 | 阅读时长 ≈ 5

1 日志框架

日志门面 (日志抽象层) 日志实现
JCL(Jakarta Commons Logging)、SLF4j(Simple Logging Facade for Java)、jboss-logging Log4j、JUL(java.util.logging)、Log4j2、 Logback

左边选一个抽象层,右边选一个实现:

最常用的日志门面:SLF4J。

日志实现:Log4J和Logback是一个写的框架。Log4J2是Apache出的。

Spring Boot 选用的是SLF4J和Logback。

阅读全文 »
123

梁恩浩

A Java Developer

30 日志
5 分类
11 标签
GitHub Weibo
© 2020 梁恩浩
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4
访问人数 人 总访问量 次
0%