Ethan's Blog


  • Home

  • Archives

  • Tags

  • Search

Prototype Design Pattern

Posted on 2020-11-29

原型模式的原理与应用

如果对象的创建成本比较大,而同一个类的不同对象之间差别不大(大部分字段都相同),在这种情况下,我们可以利用对已有对象(原型)进行复制(或者叫拷贝)的方式来创建新对象,以达到节省创建时间的目的。这种基于原型来创建对象的方式就叫作原型设计模式(Prototype Design Pattern),简称原型模式。

实际上,创建对象包含的申请内存、给成员变量赋值这一过程,本身并不会花费太多时间,或者说对于大部分业务系统来说,这点时间完全是可以忽略的。应用一个复杂的模式,只得到一点点的性能提升,这就是所谓的过度设计,得不偿失。但是,如果对象中的数据需要经过复杂的计算才能得到(比如排序、计算哈希值),或者需要从 RPC、网络、数据库、文件系统等非常慢速的 IO 中读取,这种情况下,我们就可以利用原型模式,从其他已有对象中直接拷贝得到,而不用每次在创建新对象的时候,都重复执行这些耗时的操作。

假设数据库中存储了大约 10 万条“搜索关键词”信息,每条信息包含关键词、关键词被搜索的次数、信息最近被更新的时间等。系统 A 在启动的时候会加载这份数据到内存中,用于处理某些其他的业务需求。为了方便快速地查找某个关键词对应的信息,我们给关键词建立一个散列表索引。如果你熟悉的是 Java 语言,可以直接使用语言中提供的 HashMap 容器来实现。其中,HashMap 的 key 为搜索关键词,value 为关键词详细信息(比如搜索次数)。我们只需要将数据从数据库中读取出来,放入 HashMap 就可以了。

不过,我们还有另外一个系统 B,专门用来分析搜索日志,定期(比如间隔 10 分钟)批量地更新数据库中的数据,并且标记为新的数据版本。比如,在下面的示例图中,我们对 v2 版本的数据进行更新,得到 v3 版本的数据。这里我们假设只有更新和新添关键词,没有删除关键词的行为:

Read more »

Builder Design Pattern

Posted on 2020-11-29

为什么需要建造者模式?

假设有这样一道设计面试题:我们需要定义一个资源池配置类 ResourcePoolConfig。这里的资源池,你可以简单理解为线程池、连接池、对象池等。在这个资源池配置类中,有以下几个成员变量,也就是可配置项。现在,请你编写代码实现这个 ResourcePoolConfig 类:

只要你稍微有点开发经验,那实现这样一个类对你来说并不是件难事。因为 maxTotal、maxIdle、minIdle 不是必填变量,所以在创建 ResourcePoolConfig 对象的时候,我们通过往构造函数中,给这几个参数传递 null 值,来表示使用默认值:

Read more »

Factory Design Pattern

Posted on 2020-11-22

简单工厂(Simple Factory)

在下面这段代码中,我们根据配置文件的后缀(json、xml…),选择不同的解析器(JsonRuleConfigParser、XmlRuleConfigParser…),将存储在文件中的配置解析成内存对象 RuleConfig:

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
public class RuleConfigSource 
{
public RuleConfig load(String ruleConfigFilePath)
{
String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
IRuleConfigParser parser = null;
if ("json".equalsIgnoreCase(ruleConfigFileExtension))
{
parser = new JsonRuleConfigParser();
}
else if ("xml".equalsIgnoreCase(ruleConfigFileExtension))
{
parser = new XmlRuleConfigParser();
}
else if ("yaml".equalsIgnoreCase(ruleConfigFileExtension))
{
parser = new YamlRuleConfigParser();
}
else if ("properties".equalsIgnoreCase(ruleConfigFileExtension))
{
parser = new PropertiesRuleConfigParser();
}
else
{
throw new InvalidRuleConfigException("Rule config file format is not supported: " + ruleConfigFilePath);
}

String configText = "";
// 从 ruleConfigFilePath 文件中读取配置文本到 configText 中
RuleConfig ruleConfig = parser.parse(configText);
return ruleConfig;
}

private String getFileExtension(String filePath)
{
//...解析文件名获取扩展名,比如 rule.json,返回 json
return "json";
}
}
Read more »

Singleton Design Pattern

Posted on 2020-11-21

为什么要使用单例?

单例设计模式(Singleton Design Pattern)理解起来非常简单。一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。

实战案例一:处理资源访问冲突

在这个例子中,我们自定义实现了一个往文件中打印日志的 Logger 类。具体的代码实现如下所示:

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
public class Logger 
{
private FileWriter writer;

public Logger()
{
File file = new File("/Users/wangzheng/log.txt");
writer = new FileWriter(file, true); // true 表示追加写入
}

public void log(String message) {
writer.write(message);
}
}

// Logger 类的应用示例:
public class UserController
{
private Logger logger = new Logger();

public void login(String username, String password)
{
//...省略业务逻辑代码...
logger.log(username + " logged-in!");
}
}

public class OrderController
{
private Logger logger = new Logger();

public void create(OrderVo order)
{
//...省略业务逻辑代码...
logger.log("Created an order: " + order.toString());
}
}
Read more »

编程技巧

Posted on 2020-11-17

把代码分割成更小的单元块

大部分人阅读代码的习惯都是,先看整体再看细节。所以,我们要有模块化和抽象思维,善于将大块的复杂逻辑提炼成类或者函数,屏蔽掉细节,让阅读代码的人不至于迷失在细节中,这样能极大地提高代码的可读性。不过,只有代码逻辑比较复杂的时候,我们其实才建议提炼类或者函数。毕竟如果提炼出的函数只包含两三行代码,在阅读代码的时候,还得跳过去看一下,这样反倒增加了阅读成本。

这里我举一个例子来进一步解释一下。重构前,在 invest() 函数中,最开始的那段关于时间处理的代码,是不是很难看懂?重构之后,我们将这部分逻辑抽象成一个函数,并且命名为 isLastDayOfMonth,从名字就能清晰地了解它的功能,判断今天是不是当月的最后一天。这里,我们就是通过将复杂的逻辑代码提炼成函数,大大提高了代码的可读性。代码具体如下所示:

Read more »
1…353637…55
necusjz

necusjz

271 posts
16 tags
© 2016 - 2026 necusjz
Powered by Hexo
Theme - NexT.Mist