找出在 Typescript 中更新 OOP 中对象状态的最佳方法

问题描述 投票:0回答:1

我在我的应用程序中使用面向对象编程,我试图找出改变对象状态同时保持其一致性的最佳方法。我已经编写了一个代码来实现这一点,但是有更好的方法吗?有人在 OOP 中编写时已经发现了任何模式吗?

这是我写的代码:

export class AccessProfile {

    id!: number;
    active!: boolean;
    name!: string;
    createdAt!: Date;
    createdBy!: string | null;
    createdFrom!: SystemModule;
    updatedAt!: Date;
    updatedBy!: string | null;
    updatedFrom!: SystemModule;
    environment!: Environment;
    hierarchy!: number | null;
    permissions!: Set<string>;


    public update(
        {
            updatedFrom,
            updatedBy = null,
            name,
            active,
            hierarchy,
            permissions
        }: {
            updatedFrom: SystemModule,
            updatedBy?: string | null,
            name?: string,
            active?: boolean,
            hierarchy?: number | null,
            permissions?: Set<string>,
        }
    ) {

        const backup = {
            name: this.name,
            active: this.active,
            hierarchy: this.hierarchy,
            permissions: this.permissions
        };

        try {

            if (name !== undefined) {
                name = AccessProfile.sanitizeName(name);

                if (this.name !== name) {
                    AccessProfile.validateName(name);
                    this.name = name;
                }
            }

            if (active !== undefined && this.active !== active) {
                this.active = active;
            }

            if (hierarchy !== undefined) {
                hierarchy = AccessProfile.sanitizeHierarchy(hierarchy);

                if (this.hierarchy !== hierarchy) {
                    AccessProfile.validateHierarchy(hierarchy);
                    this.hierarchy = hierarchy;
                }
            }

            if (
                permissions !== undefined
                && !permissionsAreEqual(permissions, this.permissions)
            ) {
                this.permissions = permissions;
            }

            if (this.hierarchy !== backup.hierarchy && this.permissions !== backup.permissions) {
                AccessProfile.validateHierarchyAndPermissions({
                    hierarchy: this.hierarchy,
                    permissions: this.permissions
                });
            } else if (this.hierarchy !== backup.hierarchy) {
                AccessProfile.validateHierarchyAndPermissions({
                    hierarchy: this.hierarchy,
                    permissions: backup.permissions
                });
            } else if (this.permissions !== backup.permissions) {
                AccessProfile.validateHierarchyAndPermissions({
                    hierarchy: backup.hierarchy,
                    permissions: this.permissions
                });
            }

            this.updatedFrom = updatedFrom;
            this.updatedBy = updatedBy;

            this.updatedAt = new Date();
            return this;
        } catch (err) {

            this.name = backup.name;
            this.active = backup.active;
            this.hierarchy = backup.hierarchy;
            this.permissions = backup.permissions;

            throw err;
        }
    }


}
typescript oop backend
1个回答
0
投票

我正在尝试找出改变对象状态同时保持其一致性的最佳方法。

我想说,规范且确实最干净的方法是分两个阶段构建

update
方法:1)验证和调整参数(并抛出无效数据); 2) 将(现在有效)数据分配给实例属性。下面是一个最小的示例,代码中还有一些建议。

顺便说一句,使用单个参数/变量与对象及其属性几乎完全相同,指导原则应该是可读性:也就是说,

Object.assign
确实可以节省几行代码,但另一方面它不允许优化仅分配给更改的属性。

export interface ProfileData {
  hierarchy: number | null;
  permissions: Set<string>;
}

export class Profile implements ProfileData {

  hierarchy: ProfileData["hierarchy"];
  permissions: ProfileData["permissions"];

  /**
   * @throws {Error} Invalid argument
   * @throws {Error} Invalid arguments
   */
  constructor({
    hierarchy,
    permissions,
  }: ProfileData) {
    // We SHOULD validate data here as well, which
    // hints at extracting a data validation function
    // to be used here as well as in `update`...

    this.hierarchy = hierarchy;
    this.permissions = permissions;
  }

  /**
   * @throws {Error} Invalid argument
   * @throws {Error} Invalid arguments
   */
  public update({
    hierarchy,
    permissions,
  }: Partial<ProfileData>) {
    // (1) Validate and adjust data:

    // (1.1) Validate and adjust data values:

    if (typeof hierarchy === "undefined") {
      hierarchy = this.hierarchy;
    }
    else {
      hierarchy = Profile.sanitizeHierarchy(hierarchy);

      Profile.validateHierarchy(hierarchy);
    }

    if (typeof permissions === "undefined") {
      permissions = this.permissions;
    }

    // (1.2) Cross-validate data:

    Profile.validateHierarchyAndPermissions(hierarchy, permissions);

    // (2) Assign (now valid) data:

    // These optimizations, skipping assignments,
    // usually are overkill, anyway here we go:

    if (hierarchy !== this.hierarchy) {
      this.hierarchy = hierarchy;
    }

    if (!Profile.permissionsAreEqual(permissions, this.permissions)) {
      this.permissions = permissions;
    }

    return this;
  }

  static permissionsAreEqual(
    permissions1: ProfileData["permissions"],
    permissions2: ProfileData["permissions"],
  ): boolean {
    throw "NOT YET IMPLEMENTED";
  }

  // There is a *smell* associated with a `validate` that
  // takes in input the same type as in output: if there is
  // a `validate`, then the type of input supposedly is not
  // the same as the type of valid data... and, along those
  // lines, the validation functions below should rather
  // return an `asserts` type.  But I didn't have enough info
  // to fully close the circle here, also considering that you
  // have made no such distinction in your `update` function...

  static sanitizeHierarchy(
    hierarchy: ProfileData["hierarchy"],
  ): ProfileData["hierarchy"] {
    throw "NOT YET IMPLEMENTED";
  }

  /**
   * @throws {Error} Invalid argument
   */
  static validateHierarchy(
    hierarchy: ProfileData["hierarchy"],
  ): void | never {
    throw "NOT YET IMPLEMENTED";
  }

  /**
   * @throws {Error} Invalid arguments
   */
  static validateHierarchyAndPermissions(
    hierarchy: ProfileData["hierarchy"],
    permissions: ProfileData["permissions"],
  ): void | never {
    throw "NOT YET IMPLEMENTED";
  }
}

游乐场链接

© www.soinside.com 2019 - 2024. All rights reserved.