javascript 面向对象概念

fykwrbwg  于 2023-03-21  发布在  Java
关注(0)|答案(3)|浏览(134)

我有一个名为Rule的主类,它有一个param属性和几个子类,例如:

class Rule {
      param = {}
      constructor (param) {
        // here I want to add the logic to fuse this.param from child component (all are different) and param provided to the constructor
      }
    }

    class SubRule1 extends Rule {
      param = {
        subparam: {
          values: ['A', 'B', 'C'],
          value: 'A'
        }
      }
      constructor (param) {
        super(param)
      }
    }
    
    class SubRule2 extends Rule {
      param = {
        subparam: {
          values: [1, 2],
          value: 1
        }
      }
      constructor (param) {
        super(param)
      }
    }

    let myrule = new SubRule1({ subparam: 'B' })
    >> expected Object { subparam: { values: ['A', 'B', 'C'], value: 'B' }}
    let myrule2 = new SubRule2({ subparam: 2 })
    >> expected Object { subparam: { values: [1, 2], value: 2 }}

当示例化一个新对象时,我想将this.paramparam参数融合到所有子类的constructor中。我想在Rule类中添加逻辑,但是,当访问Rule.constructor类中的this.param时,我不访问this.param子属性,而是访问Rule中定义的子属性。在所有子类中正确启动this.param的正确设计是什么?

efzxgjgh

efzxgjgh1#

从我上面的评论…

  • “针对所有param数据结构的最新更改确实打开了一个全新的主题,最好用...**“如何在不通过部分覆盖丢失数据的情况下深度合并两个结构相似的对象?”**与这个无声的主题转移相比,关于最佳class/constructor/super设计的原始问题被边缘化了。
  • "@user1595929... OP可能会研究以下问题的一个答案...“比较2个嵌套数据结构,目标+源,与源对应数据结构相比,缺少目标值的适当合并策略是什么?”...以便获得一些关于深度合并方法的灵感。
  • "@user1595929...此外,就每个预期结果而言,两个新示例都是坏的/错误的...为了满足预期,示例代码需要是... new SubRule1({ subparam: { value: 'B' } })而不是new SubRule1({ subparam: 'B' })new SubRule2({ subparam: { value: 2 } })而不是new SubRule2({ subparam: 2 })“*
    **(1)**首先...关于OP的Rule构造函数的代码注解...
  • // here I want to add the logic to fuse this.param from child component (all are different) and param provided to the constructor*

...父类或超类必须对子类/子类的属性和操作不敏感(因此它不做任何假设)。

**(2)**此外,对于每个Sub/Rules param成员的处理,没有继承,因此甚至不需要Rule-SubRule(父子)关系。这是由于在构造时如何处理每个示例的this.param的初始化。

即使与OP的示例代码一样(子)类型SubRule1SubRule2分别扩展了(super-)类型Rulethis.param初始化完全被每个类型本身覆盖/隐藏。无论this.param是什么值,在super调用结束时(链),最迟使用任何子类型构造函数,该值将完全重新分配/覆盖(如OP的params默认值SubRule1SubRule2所示)。因此,如果没有任何其他尚未显示的原型功能,任何与Rule相关的继承都是完全不必要的。

**(3)**此外,最近编辑的OP为param对象引入了一个比使用非嵌套对象容易实现的更复杂的合并行为,例如...

Object.assign(this.param, param);

而预期的合并...

{
  subparam: {
    values: ['A', 'B', 'C'],
    value: 'A',
  }
}

和...

{
  subparam: {
    value: 'B',
  }
}

是...

{
  subparam: {
    values: ['A', 'B', 'C'],
    value: 'B',
  }
}

...它要求自定义合并实现。

总结OP的主要任务是实现一个合适的自定义合并策略,而不是使用基于子类/类型的继承,因为后者很可能甚至没有有效的理由。

接下来提供的示例代码证明了上面所说的(尽管它保留了继承特性,以便与OP的主代码结构保持一致)。

class Rule {
  param = {};

  constructor (options = {}) {
    // - a simple `Object.assign` is suitable
    //   due to `param` being an empty object.
    Object.assign(this.param, options);
  }
}

class SubRule1 extends Rule {
  param = {
    subparam: {
      values: ['A', 'B', 'C'],
      value: 'A',
    },
  };
  constructor (options = {}) {
    super(/*no `options` passing needed due to own `param`*/);
    mergeDataStructures(this.param, options);
  }
}
class SubRule2 extends Rule {
  param = {
    subparam: {
      values: [1, 2],
      value: 1,
    },
  };
  constructor (options = {}) {
    super(/*no `options` passing needed due to own `param`*/);
    mergeDataStructures(this.param, options);
  }
}
const myRule1 = new SubRule1({ subparam: { value: 'B' } });
const myRule2 = new SubRule2({ subparam: { value: 2 } });

console.log({
  myRule1, // { subparam: { values: ['A', 'B', 'C'], value: 'B' }}
  myRule2, // { subparam: { values: [1, 2], value: 2 }}
});
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
// - a pushing/patching approach of assigning/overwriting
//   target-values of deeply nested data-structures.
function mergeDataStructures(target, source) {

  const targetIsArray = Array.isArray(target);
  const sourceIsArray = Array.isArray(source);

  const targetIsNonArrayObject =
    !!target && 'object' === typeof target && !targetIsArray;
  const sourceIsNonArrayObject =
    !!source && 'object' === typeof source && !sourceIsArray;

  if (targetIsArray && sourceIsArray) {
    source
      .forEach((_/*sourceItem*/, idx) =>
        assignValue(target, source, idx)
      );
  } else if (targetIsNonArrayObject && sourceIsNonArrayObject) {
    Object
      .keys(source)
      .forEach(key =>
        assignValue(target, source, key)
      );
  } else {
    target = cloneDataStructure(source);
  }
  return target;

  function assignValue(target, source, key) {
    const sourceValue = source[key];

    if (!target.hasOwnProperty(key)) {

      target[key] = cloneDataStructure(sourceValue);
    } else {
      const targetValue = target[key];

      const targetValueIsObject =
        (!!targetValue && 'object' === typeof targetValue);
      const sourceValueIsObject =
        (!!sourceValue && 'object' === typeof sourceValue);
      const targetValueIsArray =
        targetValueIsObject && Array.isArray(targetValue);
      const sourceValueIsArray =
        sourceValueIsObject && Array.isArray(sourceValue);

      if (
        (targetValueIsArray && sourceValueIsArray) ||
        (targetValueIsObject && sourceValueIsObject)
      ) {
        mergeDataStructures(targetValue, sourceValue);
      } else {
        target[key] = cloneDataStructure(sourceValue);
      }
    }
  }
}
const cloneDataStructure =
  ('function' === typeof structuredClone)
  && structuredClone
  || (value => JSON.parse(JSON.stringify(value)));
</script>

编辑

如果OP希望通过super调用进行参数传递/转发的完全继承,则需要将方法从将每个类型的默认param定义为自己的属性进行更改。
这是由于this因此,每种类型的默认param配置必须存储为例如每种类型的(或所有类型的)模块作用域。我不会将此类配置声明为静态类属性,因为它们会暴露出来,而模块作用域确保保护每个默认的param配置。
前面提供的实际上不需要任何继承的示例代码将更改为真实的的继承方法,其实现可能类似于下面提供的代码...

const subRule2Param = {
  subparam: {
    values: [1, 2],
    value: 1,
  },
};
const subRule1Param = {
  subparam: {
    values: ['A', 'B', 'C'],
    value: 'A',
  },
};
const baseRuleParam = {};

class Rule {
  param = {};

  constructor (options = {}) {
    // - a simple `Object.assign` is suitable
    //   due to `param` being an empty object.
    Object
      .assign(
        this.param,
        mergeDataStructures(
          cloneDataStructure(baseRuleParam),
          options
        )
      );
  }
}

class SubRule1 extends Rule {
  constructor (options = {}) {
    super(
      mergeDataStructures(
        cloneDataStructure(subRule1Param),
        options
      )
    );
  }
}
class SubRule2 extends Rule {
  constructor (options = {}) {
    super(
      mergeDataStructures(
        cloneDataStructure(subRule2Param),
        options
      )
    );
  }
}
const myRule1 = new SubRule1({ subparam: { value: 'B' } });
const myRule2 = new SubRule2({ subparam: { value: 2 } });

console.log({
  myRule1, // { subparam: { values: ['A', 'B', 'C'], value: 'B' }}
  myRule2, // { subparam: { values: [1, 2], value: 2 }}
});
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
// - a pushing/patching approach of assigning/overwriting
//   target-values of deeply nested data-structures.
function mergeDataStructures(target, source) {

  const targetIsArray = Array.isArray(target);
  const sourceIsArray = Array.isArray(source);

  const targetIsNonArrayObject =
    !!target && 'object' === typeof target && !targetIsArray;
  const sourceIsNonArrayObject =
    !!source && 'object' === typeof source && !sourceIsArray;

  if (targetIsArray && sourceIsArray) {
    source
      .forEach((_/*sourceItem*/, idx) =>
        assignValue(target, source, idx)
      );
  } else if (targetIsNonArrayObject && sourceIsNonArrayObject) {
    Object
      .keys(source)
      .forEach(key =>
        assignValue(target, source, key)
      );
  } else {
    target = cloneDataStructure(source);
  }
  return target;

  function assignValue(target, source, key) {
    const sourceValue = source[key];

    if (!target.hasOwnProperty(key)) {

      target[key] = cloneDataStructure(sourceValue);
    } else {
      const targetValue = target[key];

      const targetValueIsObject =
        (!!targetValue && 'object' === typeof targetValue);
      const sourceValueIsObject =
        (!!sourceValue && 'object' === typeof sourceValue);
      const targetValueIsArray =
        targetValueIsObject && Array.isArray(targetValue);
      const sourceValueIsArray =
        sourceValueIsObject && Array.isArray(sourceValue);

      if (
        (targetValueIsArray && sourceValueIsArray) ||
        (targetValueIsObject && sourceValueIsObject)
      ) {
        mergeDataStructures(targetValue, sourceValue);
      } else {
        target[key] = cloneDataStructure(sourceValue);
      }
    }
  }
}
const cloneDataStructure =
  ('function' === typeof structuredClone)
  && structuredClone
  || (value => JSON.parse(JSON.stringify(value)));
</script>
b5lpy0ml

b5lpy0ml2#

你要找的模式是

actual_params = Object.assign({}, defaults, user_options)

要使其与继承一起工作,defaults必须是静态的:

class Whatever {
    constructor(options = {}) {
        this.config = Object.assign({}, this.constructor.defaults, options)
    }
}

class Deriv extends Whatever {
    static defaults = {'foo': 'bar', 'spam': 'pam'}
}

let a = new Deriv()
console.log(a.config)

let b = new Deriv({'spam': 'hello'})
console.log(b.config)

如果你想把它们合并得更深,这是另一个更复杂的故事。

wh6knrhe

wh6knrhe3#

要从父类构造函数访问param参数,不应该使用this.param,而应该使用console.log(param),它作为参数传递给父类的构造函数。this.param只是指向该类的变量的指针。

class Rule {
      param = {}
      constructor (param) {
        console.log(param)
      }
    }

    class SubRule extends Rule {
      param = {
        subparam1: 'test'
      }
      constructor (param) {
        super(param)
      }
    }
    
    let myrule = new SubRule()

    // Object {  } instead of Object { subparam1: 'test' }

这样的东西可能对你有用。

相关问题