- 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();
- Python快樂編程:人工智能深度學(xué)習(xí)基礎(chǔ)
- 嵌入式軟件系統(tǒng)測試:基于形式化方法的自動化測試解決方案
- Java Web及其框架技術(shù)
- Julia機器學(xué)習(xí)核心編程:人人可用的高性能科學(xué)計算
- Unity 2D Game Development Cookbook
- 運用后端技術(shù)處理業(yè)務(wù)邏輯(藍橋杯軟件大賽培訓(xùn)教材-Java方向)
- Unity 3D腳本編程:使用C#語言開發(fā)跨平臺游戲
- Practical Predictive Analytics
- 計算機組裝與維護(第二版)
- Moodle 3.x Developer's Guide
- Linux Networking Cookbook
- C++ Data Structures and Algorithm Design Principles
- Swift iOS Programming for Kids
- JavaScript程序設(shè)計基礎(chǔ)教程(慕課版)
- Python AI游戲編程入門:基于Pygame和PyTorch