在JS中重新映射对象属性的成本高吗?有基准测试吗?或者如何进行基准测试



一段时间以来,我一直在单片项目中紧密耦合我的对象属性。几乎没有DTO。来自客户端->一直到数据库,它几乎没有注意到SOLID原则。依赖性正在向内发展,留下了一个更改和维护成本高昂的体系结构。然而,由于我已经阅读并研究了很多关于它的内容——坚固而干净的架构。我注意到,为了保持分离,有很多对象的重新映射。

示例1:DAL:属性约定的重命名first_name=>firstName。

class UserRepository {
#pool: Pool;
#tableName = 'platform_users';
constructor(pool: Pool) {
this.#pool = pool;
}
async findById(id: string): Promise<UserEntity | null> {
try {
const QueryString = `SELECT * FROM ${this.#tableName} WHERE id = $1`;
const QueryArguments = [id];
const rawData = await this.#pool.query(QueryString, QueryArguments);
const data = this.#mapPostResults(rawData);
return data.length > 0 ? data[0] : null;
} catch (e) {
throw new Error('Failed to process query');
}
}
async insertNewUser({
id,
username,
firstName,
lastName,
password,
email,
verified,
registeredOn,
lastLogin,
description
}: UserEntity): Promise<{ rowCount: number; id: string }> {
try {
const QueryString = `INSERT INTO ${
this.#tableName
}(id, first_name, last_name, email, verified, password, registered_on, last_login, description, username) VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETUIRNING id`;
const QueryArguments = [
id,
username,
firstName,
lastName,
email,
password,
verified,
registeredOn,
lastLogin,
description
];
const rawData = await this.#pool.query(QueryString, QueryArguments);
return { rowCount: rawData.rowCount, id: rawData.rows[0].id };
} catch (e) {
console.log(e);
throw new Error('Failed to process query');
}
}
#mapPostResults(res: QueryResult): UserEntity[] {
return res.rows.map((r) => {
return {
id: r.id,
username: r.username,
firstName: r.first_name,
lastName: r.last_name,
email: r.email,
password: r.password,
verified: r.verified,
lastLogin: r.last_login,
description: r.description,
registeredOn: r.registered_on
};
});
}
}

示例2:express的req和res对控制器的依赖性反转。为了建立一个独立于框架(express(的接口——下面我们看到httpRequest将从express的"请求结构"接收一个接近1:1的映射。

export default function makeExpressCallback(controller) {
return (req: Request, res: Response) => {
const httpRequest = {
body: req.body,
query: req.query,
params: req.params,
ip: req.ip,
method: req.method,
path: req.path,
session: req.session || {},
headers: {
'Content-Type': req.get('Content-Type'),
Referer: req.get('referer'),
'User-Agent': req.get('User-Agent')
}
};
controller(httpRequest)
.then((httpResponse) => {
if (httpResponse.headers) {
res.set(httpResponse.headers);
}
res.type('json');
res.status(httpResponse.statusCode).send(httpResponse.body);
})
.catch((e) => res.status(500).send({ error: 'An unkown error occurred.' }));
};
}
// index.ts
router.post('/user', bodyParser.json(), makeExpressCallback(createUserAccount));

这些示例继续到用例层,用于映射它们的响应和请求。。。

"请求和响应模型用例期望输入数据,并生成输出数据。然而,一个格式良好的用例对象应该不知道数据传递给用户或任何其他组件的方式。我们当然不希望用例类中的代码知道HTML或SQL!

用例类接受简单的请求数据结构作为输入,并返回简单的响应数据结构作为输出。这些数据结构不依赖于任何东西。它们不是从标准框架接口派生的,例如HttpRequest和HttpResponse。他们对网络一无所知,也不分享任何可能存在的用户界面的装饰。

这种依赖性的缺乏是至关重要的。如果请求和响应模型不是独立的,那么依赖于它们的用例将间接绑定到模型附带的任何依赖关系。

您可能会想让这些数据结构包含对实体对象的引用。您可能认为这是有道理的,因为实体和请求/响应模型共享了大量数据。避免这种诱惑!这两个物体的目的是非常[…]">

摘录自清洁建筑:软件结构和设计的工匠指南(Robert C.Martin系列(Robert C.Martin本材料可能受版权保护。

因此,与我以前的编码方式相比,我不必进行这些映射,因为它们在每一层(或缺少(都耦合得很紧密,这当然很糟糕。但我确实想知道,这些类型的映射是否昂贵,我们是否仍应尽最大努力将最佳映射最小化,即使权衡远远大于不这样做?

这些映射的成本都在开发人员的时间内,而不是在运行时。您所说的是在内存中的几个字节之间来回移动,最多需要几微秒。如果您曾经担心过,当然您应该对它进行基准测试,只需确保基准测试代码本身的性能影响是一致的。

RAM访问速度非常快,并且在Node VM中进行了高度优化。通常,在这种情况下,您传递对字符串对象的引用,而不是实际创建新的内存位置。当你第一次从数据库读取额外的几个字节或往返于浏览器时(10毫秒而不是几微秒(,所有这些都会被包含在内。

除非您正在深度复制大型阵列,否则DTO通常会很便宜。

最新更新