ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【TypeScript 学习】TypeScript 枚举范例发散:基于位运算的权限管理 CRUD [打印本页]

作者: 莱莱    时间: 2024-7-14 11:42
标题: 【TypeScript 学习】TypeScript 枚举范例发散:基于位运算的权限管理 CRUD
TypeScript 枚举范例发散:基于位运算的权限管理 CRUD 操作

1 问题由来

TypeScript 枚举范例常用于束缚一组固定的选项(如星期、月份)、表示几种可选的状态(如用户角色、订单状态)、或者替换一些难写的取值(如常用颜色的十六进制值等)。在这些应用场景中,有一类场景在开辟后台管理系统时尤为常见——用户权限管理。假设有三种权限:可读(Readable)、可写(Writable)、可执行(Executable),则可以用 TypeScript 枚举范例表示为:
  1. enum Permission = {
  2.     Readable   = 0b001,   // i.e. 2^0 = 1
  3.     Writable   = 0b010,   // i.e. 2^1 = 2
  4.     Executable = 0b100,   // i.e. 2^2 = 4
  5. }
复制代码
于是可以类比 Lunix 中的权限管理,团结位运算实现权限的基本操作(增、删、改、查,即 CRUD)。
2 具体实现

准备工作就是上面定义好的 TS 枚举范例 Permission
2.1 新增权限

新增可通过 位或(|) 运算实现:
  1. /**
  2. * Add a permission to the permission set
  3. *
  4. * @param perms original permission set
  5. * @param permToAdd permission to add
  6. * @returns the new permission set
  7. */
  8. function addPermission(perms: Permission, permToAdd: Permission): Permission {
  9.   return perms | permToAdd;
  10. }
复制代码
相当于求并集。
2.2 删除权限

既然有新增,自然就有删除。通常有两种写法:

我觉得第二个更简洁,于是实现为:
  1. /**
  2. * Delete a permission from the permission set
  3. *
  4. * @param perms original permission set
  5. * @param permToDel permission to delete
  6. * @returns the new permission set
  7. */
  8. function delPermission(perms: Permission, permToDel: Permission): Permission {
  9.   return perms ^ permToDel;
  10. }
复制代码
2.3 查询权限(即判断存在与否)

所谓查询,就是判断当前权限集合 perms 是否包含某个权限(或权限组合)permTarget。这可以通过 位与 运算实现:
  1. /**
  2. * Check if the permission set has the target permission
  3. *
  4. * @param perms original permission set
  5. * @param permTarget target permission to check
  6. * @returns true if the permission set has the target permission; otherwise false
  7. */
  8. function hasPermission(perms: Permission, permTarget: Permission): boolean {
  9.   return (perms & permTarget) === permTarget;
  10. }
复制代码
2.4 修改权限

实际工作中,权限的修改无非是将原来的权限组合 重新替换成 前端传来的新组合。这里为了练习位运算,稍微做了一下调解:令目标函数接收三个参数:当前权限、待删权限、待增权限。也就是说,把权限的 修改 看成是 删除添加 的组合操作,先删后加。
v1.0 版实现如下:
  1. function updatePermission(perms: Permission, permToDel: Permission, permToAdd: Permission): Permission {
  2.   return (perms ^ permToDel) | permToAdd;
  3. }
复制代码
实践 DRY 原则(Don’t Repeat Yourself),复用前面的两个方法 delPermission 和 addPermission,酿成 v2.0 版实现:
  1. function updatePermission(perms: Permission, permToDel: Permission, permToAdd: Permission): Permission {
  2.   return addPermission(delPermission(perms, permToDel), permToAdd)
  3. }
复制代码
结果一测就出问题了:
  1. let perms = addPermission(Permission.Readable, Permission.Executable);  // 101
  2. const pDel = addPermission(Permission.Readable, Permission.Writable);  // 011
  3. const pAdd = Permission.Executable;  // 100
  4. perms = updatePermission(perms, pDel, pAdd);
  5. console.assert(perms === 0b100, `updatePermission failed: \n  Expected '0b100', but got '0b${perms.toString(2)}' instead.`);
  6. /* =>
  7. Assertion failed: updatePermission failed:
  8.   Expected '0b100', but got '0b110' instead.
  9. */
复制代码
缘故原由说来也简单:删除的时候需要先判断一下,否则本来没有的权限,也可能由于位异或操作而被意外引入。于是有了第 3 版实现:
  1. function updatePermission(perms: Permission, permToDel: Permission, permToAdd: Permission): Permission {
  2.   // return addPermission(delPermission(perms, permToDel), permToAdd);
  3.   const permDelReal = perms & permToDel;
  4.   if(hasPermission(perms, permDelReal)) {
  5.     const permPreAdd = delPermission(perms, permDelReal);
  6.     return addPermission(permPreAdd, permToAdd);
  7.   } else {
  8.     return addPermission(perms, permToAdd);
  9.   }
  10. }
复制代码
注意到第 3 行,单独的 位与 运算着实是类似求交集的结果(同真为真,别的为假),而且与查询逻辑的部分代码重复了。这说明 位与 运算也是一个原子操作,应该单独提出来:
  1. /**
  2. * Get the shared permission between two permission sets
  3. *
  4. * @param perm1 the 1st permission set
  5. * @param perm2 the 2nd permission set
  6. * @returns the shared permission
  7. */
  8. function sharedPermission(perm1: Permission, perm2: Permission): Permission {
  9.   return perm1 & perm2;
  10. }
复制代码
于是再次重构 updatePermission 函数,有了第 4 版实现:
  1. /**
  2. * Update the permission set by adding and deleting permissions
  3. *
  4. * @param perms original permission set
  5. * @param permToDel permission to delete
  6. * @param permToAdd permission to add
  7. * @returns the new permission set
  8. */
  9. function updatePermission(perms: Permission, permToDel: Permission, permToAdd: Permission): number {
  10.   const permDelReal = sharedPermission(perms, permToDel);
  11.   if(hasPermission(perms, permDelReal)) {
  12.     const permPreAdd = delPermission(perms, permDelReal);
  13.     return addPermission(permPreAdd, permToAdd);
  14.   } else {
  15.     return addPermission(perms, permToAdd);
  16.   }
  17. }
复制代码
大功告成。
2.5 完整测试

搞定了 CRUD 操作,最后再完整测试一遍:
  1. // Test cases
  2. let perms = addPermission(Permission.Readable, Permission.Writable);
  3. console.log('perms:', perms.toString(2));  // 011
  4. // Test permission deletion
  5. perms = delPermission(perms, Permission.Writable);
  6. console.log('permsAfterDel:', perms.toString(2));  // 001
  7. // Test permission addition
  8. perms = addPermission(perms, Permission.Executable);
  9. console.log('permsAfterAdd:', perms.toString(2));  // 101
  10. // Test shared permission
  11. console.log('has readable:', hasPermission(perms, Permission.Readable));  // true
  12. console.log('has writable:', hasPermission(perms, Permission.Writable));  // false
  13. console.log('has executable:', hasPermission(perms, Permission.Executable));  // true
  14. // Test permission update
  15. console.log('Before update, perms =', perms.toString(2));               // 101 (i.e. 5)
  16. const pDel = addPermission(Permission.Readable, Permission.Writable);  // 011
  17. const pAdd = addPermission(Permission.Executable, Permission.Executable);  // 100
  18. console.log('perms to delete =', pDel.toString(2));  // 011 (i.e. 3)
  19. console.log('perms to add =', pAdd.toString(2));  // 110 (i.e. 6)
  20. perms = updatePermission(perms, pDel, pAdd);
  21. console.assert(perms === 0b100, `updatePermission failed: \nExpected '0b100', but got '0b${perms.toString(2)}'`);
  22. console.log('After update perms =', perms.toString(2));  // 110
复制代码
实测结果如下:

3 小结



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4