Unix-философия гласит, что всё есть файл. То есть любое устройство, принтер, сканер и папка — это файлы. При этом нужно уметь различать типы этих файлов и проверять, чем они являются. Это нужно, чтобы была возможность работать с ними определенным образом.
Чтобы получить необходимую информацию о файле, Node.js в своем API дает для этого специальный объект — Stats
. В этом уроке мы познакомимся с ним и узнаем, как с ним работать.
Информация о файле без Stats
Рассмотрим, как мы получаем информацию о файле сейчас. Мы извлекаем текущую ноду внутри и проверяем руками type
:
// isFile(path)
const parts = getPathParts(path);
const current = this.tree.getDeepChild(parts);
return current && current.getMeta().type === 'file';
// isDirectory, isSocket, isSymbolicLink
Кроме type
можно проверять что-нибудь другое.
Это нормальный рабочий подход, но тут всплывает сразу несколько неудобностей. Например, мы делаем проверку на основе строк, и если возникнет ошибка, мы не узнаем о ней. Допустим, кто-то ошибся в названии.
Также при более сложных проверках будет появляться дублирующая логика в разных местах. Поэтому здесь нужны функции, которые покажут, чем является файл, а также его дополнительные характеристики. Например, это функции isDirectory
, isSocket
, isSymbolicLink
.
Информация о файле через Stats
Stats
— это объект, в котором много разных параметров:
// util.inspect(stats)
{
dev: 2114,
ino: 48064969,
mode: 33188,
nlink: 1,
uid: 85,
gid: 100,
size: 527,
blocks: 8,
atime: 'Mon, 10 Oct 2011 23:24:11 GMT',
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
}
По ним можно узнать информацию о файле, например: когда и кем он был создан, сколько блоков занимает, кто его владелец.
Нам не понадобится такое количество параметров из примера выше. Достаточно знать, является ли что-то файлом или директорией. Но в дальнейшем это можно расширить до любой задачи, которая необходима при работе со статистикой.
Рассмотрим, как сделать правильный интерфейс. Его можно скопировать с Node.js API:
files.mkdirSync('/etc');
// У нас существует директория, поэтому gj'njve мы делаем на ней `statSync`,
// где указываем такой же параметр
files.statSync('/etc ').isDirectory(); // возвращается в объект, в котором есть много полезных методов
// Сокращенно можно написать:
files.isDirectory('/etc'); // true
Файловая система может иметь проверку isDirectory
, но внутри она уже будет работать по-другому. Она будет вызывать statSync
и проверять строчку files.statSync('/etc').isDirectory();
.
Идентичная работа у Stats
-объекта будет и с файлами:
// Создаем файл
files.touchSync('/file.txt');
// Извлекаем объект и проверяем isFile
files.statSync('/file.txt').isFile(); // true
files.isFile('/file.txt'); // true
Так как это единый интерфейс, он содержит те же функции и для папок, и для файлов, и для всего остального. Но, например, если мы вызовем isDirectory()
, где работаем с файлом, то получим false
, и наоборот. То есть мы зависим от того, с чем работаем.
Полиморфизм
Внутри интерфейса было бы удобнее разделять типы, потому что так проще обеспечить полиморфизм.
Например, в примере ниже видно, что в touchSync()
при вызове addChild()
мы создаем новый объект класса File
:
// touchSync(path)
return parent.addChild(name, new File(name));
// file.getStats().isFile();
А в mkdirSync()
мы создаем объект класса Dir
:
// mkdirSync(path)
return parent.addChild(name, new Dir(name));
// file.getStats().isDirectory();
То есть в первом случае мы могли бы сразу создать Stats
-объект и по-разному его конфигурировать. Но тогда придется делать это везде и постоянно. А в данных примерах мы инкапсулируем эту логику и зашиваем параметры в типы. Так этим управлять проще и удобнее, а код становится чище.
Получается, что file
и dir
– это отдельные объекты, которые придерживаются общего типа и имеют внутри метод getStats()
. То есть когда мы создаем файл, у него есть getStats()
, который возвращает объект статистики. И мы можем вызвать у него уже знакомые нам методы, например, isFile()
или isDirectory()
.
В этом случае наш statSync()
будет выглядеть так:
statSync(path) {
const keys = getPathParts(path);
const current = this.tree.getDeepChild(keys);
return current.getMeta().getStats();
}
Здесь мы извлекаем нашу ноду и достаем из нее мета.
Нужно понимать, что мета – это еще не статистика. Это new File()
или new Dir()
, а у него мы дальше берем getStats()
. Здесь и срабатывает полиморфизм подтипов.
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.