- Refactoring TypeScript
- James Hickey
- 521字
- 2021-06-11 13:05:51
Null Object Pattern
Empty Collections
Imagine you work for a company that writes software for dealing with legal cases.
As you are working on a feature, you discover some code:
const legalCases: LegalCase[] = await fetchCasesFromAPI();
for (const legalCase of legalCases) {
if(legalCase.documents != null) {
uploadDocuments(legalCase.documents);
}
}
Remember that we should be wary of null checks? What if some other part of the code forgot to check for a null array?
The Null Object pattern can help: you create an object that represents an empty or null object.
Fixing It Up
Let's look at the fetchCasesFromAPI() method. We'll apply a version of this pattern that's a very common practice in JavaScript and TypeScript when dealing with arrays:
const fetchCasesFromAPI = async function() {
const legalCases: LegalCase[] = await $http.get('legal-cases/');
for (const legalCase of legalCases) {
// Null Object Pattern
legalCase.documents = legalCase.documents || [];
}
return legalCases;
}
Instead of leaving empty arrays/collections as null, we are assigning them an actual empty array.
Now, no one else will need to make a null check!
But what about the entire legal case collection itself? What if the API returns null?
const fetchCasesFromAPI = async function() {
const legalCasesFromAPI: LegalCase[] = await $http.get('legal-cases/');
// Null Object Pattern
const legalCases = legalCasesFromAPI || [];
for (const legalCase of legalCases) {
// Null Object Pattern
legalCase.documents = legalCase.documents || [];
}
return legalCases;
}
Cool!
Now, we've made sure that everyone who uses this method doesn't need to be worried about checking for nulls.
Take 2
Other languages, such as C#, Java, and so on, won't allow you to assign a mere empty array to a collection due to rules about strong typing.
In those cases, you can use something like this version of the Null Object pattern:
class EmptyArray<T> {
static create<T>() {
return new Array<T>()
}
}
// Use it like this:
const myEmptyArray: string[] = EmptyArray.create<string>();
What About Objects?
Imagine that you are working on a video game. In it, some levels might have a boss.
When checking whether the current level has a boss, you might see something like this:
if(currentLevel.boss != null) {
currentLevel.boss.fight(player);
}
We might find other places that do this null check:
if(currentLevel.boss != null) {
currentLevel.completed = currentLevel.boss.isDead();
}
If we introduce a null object, then we can simply remove all these null checks.
First, we need an interface to represent our Boss:
interface IBoss {
fight(player: Player);
isDead();
}
Then, we can create our concrete boss class:
class Boss implements IBoss {
fight(player: Player) {
// Do some logic and return a bool.
}
isDead() {
// Return whether boss is dead depending on how the fight went.
}
}
Next, we'll create an implementation of the IBoss interface that represents a null Boss:
class NullBoss implements IBoss {
fight(player: Player) {
// Player always wins.
}
isDead() {
return true;
}
}
NullBoss will automatically allow the player to win, and we can remove all our null checks!
In the following code example, if the boss is an instance of NullBoss or Boss, no extra checks need to be made:
currentLevel.boss.fight(player);
currentLevel.boss.isDead();
- The Complete Rust Programming Reference Guide
- 編程的修煉
- 摩登創(chuàng)客:與智能手機(jī)和平板電腦共舞
- PHP+MySQL網(wǎng)站開發(fā)技術(shù)項(xiàng)目式教程(第2版)
- Scratch真好玩:教小孩學(xué)編程
- Mastering LibGDX Game Development
- 游戲程序設(shè)計(jì)教程
- 編譯系統(tǒng)透視:圖解編譯原理
- MATLAB定量決策五大類問題
- 深入淺出PostgreSQL
- C程序設(shè)計(jì)實(shí)踐教程
- NoSQL數(shù)據(jù)庫(kù)原理
- Python程序設(shè)計(jì)與算法基礎(chǔ)教程(第2版)(微課版)
- 計(jì)算機(jī)程序的構(gòu)造和解釋(JavaScript版)
- 軟技能2:軟件開發(fā)者職業(yè)生涯指南