创建不同种类或类型的形状对象(如矩形或三角形)的灵活方法是什么?

What are flexible approaches for creating different kinds or types of shape objects like rect- or triangles?

我有这个创建盒子数组的代码片段,我想让它通用,这样它也可以,例如,存储一个三角形。我不太确定我需要使用什么参数或者我需要如何修改它以允许三角形。如果我想要三角形和长方体,创建一个三角形数组然后将它们定位成一个长方体似乎会更好,但这样我就失去了创建简单矩形的灵活性。上下文:这是实现 z 缓冲区的程序片段。

class Box {
  /** @member {Object} position of the box storing x,y,z coordinates */
  position;
  /** @member {Object} size of the box storing width and height */
  size;
  /** @member {Object} color of the box given in RGB */
  color;

  constructor (props) {
    this.position = props.position;
    this.size = props.size;
    this.color = props.color;
  }

  /**
   * Check if given point is in box
   * @param {Number} px coordinate of the point
   * @param {Number} py coordinate of the point
   * @return {Boolean} point in box
   */
  pointInBox (px,py) {
    return this.position.x < px && this.position.x + this.size.width > px
        && this.position.y < py && this.position.y + this.size.height > py;
  }
}

const boxes = [
  new Box({
    position: { x: 50, y: 50, z: 10 },
    size: { width: 150, height: 50 },
    color: { r: 255, g: 0, b:0 }
  }),
  new Box({
    position: { x: 80, y: 30, z: 5 },
    size: { width: 10, height: 150 },
    color: { r: 0, g: 255, b:0 }
  }),
  new Box({
    position: { x: 70, y: 70, z: 8 },
    size: { width: 50, height: 40 },
    color: { r: 0, g: 0, b: 255 }
  })       
];

console.log({ boxes });
.as-console-wrapper { min-height: 100%!important; top: 0; }

对于 vanilla JS,您将不得不使用继承,如下所示。我建议使用 Typescript,它是 Javascript 的超集,它使类型的使用变得更加容易。

class Shape {
  constructor({ color, position}){
    this.color = color;
    this.positon = position;
  }
}

class Cube extends Shape {
  constructor({color, position, height, width, length}){
    super({ color, position });
    this.height = height;
    this.width = width;
    this.length = length;
  }
}

const myCube = new Cube({
  color: "#555555",
  position: {x: 12, y: 5, z: 9},
  height: 12,
  width: 12,
  length: 12
});

console.log(myCube)

在 Typescript 中,它看起来像这样:

interface Coordinate {
    x: number;
    y: number;
    z: number;
}

interface Shape {
    color: string;
    position: Coordinate;
}

interface Box extends Shape {
    height: number;
    width: number; 
    length: number;
}

现在,如果我想要一个适用于盒子和形状的函数,您可以这样做:

function getPosition(shape:Shape){
    return shape.position;
}

const myShape: Box = {
  color: "red",
  position: {
    x: 1,
    y: 4,
    z: 7,
  },
  height: 12,
  length: 12,
  width: 12,
};

getPosition(myShape);

因为 Box 扩展了 shape,该函数适用于它们,以及任何其他扩展 shape 的界面。

这只是您使用 Typescript 可以做的事情的冰山一角。

使用 Vanilla-JS 的原因之一不仅限于选择基于继承的方法等选项。基于定制的 mixins 的组合 provide/support OP 正在寻找的灵活性。

一个人也不受在哪里(以及何时甚至如何)创作的限制,例如在普通对象级别,例如工厂函数或 class 构造函数中的实例化时间。

// function based "position" mixin.
function withPosition({ x = 0, y = 0, z = 0 }) {

  // "position" specifc (default) assignment.
  Object.assign(this, { position: { x, y, z } });
}

// function based "shape" mixin.
function asShape({ color='#000', ...position }) {

  // "shape" specifc (default) assignment.
  Object.assign(this, { color });
  
  // delegate "position" specific assignement
  // and default handling to the mixin.
  withPosition.call(this, (position || {}));
}


// factory function.
function createRectangle({ width=10, height=10, ...options }) {
  const type = {};
  
  // delegate "shape" specific assignements
  // and default handling to the mixin.
  asShape.call(type, options);

  // "rectangle" specifc assignments including defaults.
  return Object.assign(type, { width, height });
}

// factory function.
function createCube({ length=10, ...options }) {
  // composition via ...
  // ... "rectangle" specifc forwarding ...
  // ............. and "cube" specific `length` enrichment.
  return { ...createRectangle(options), length }
}

class Cube {
  // `Cube` type instantiation.
  constructor({ width=10, height=10, length=10, ...options }) {;

    // delegate "shape" specific assignements
    // and default handling to the mixin.
    asShape.call(this, options);

    // "cube" specifc assignments including defaults.
    Object.assign(this, { width, height, length });
  }/*
  constructor({ length=10, ...options }) {
    Object.assign(this, { ...createRectangle(options), length });
  }*/
}

const my1stRectangle = createRectangle({
  x: 50,
  y: 50,
  z: 10,
  width: 150,
  height: 50,
  color: '#fc0',
});
const my2ndRectangle = createRectangle({});

const myCubeComposite = createCube({
  x: 50,
  y: 50,
  z: 50,
  color: '#c0f',
});
const myComposedCubeType = new Cube({});

console.log({
  my1stRectangle,
  my2ndRectangle,
  myCubeComposite,
  myComposedCubeType,
});

console.log(
  '(myCubeComposite instanceof Cube) ?',
  (myCubeComposite instanceof Cube)
);
console.log(
  '(myComposedCubeType instanceof Cube) ?',
  (myComposedCubeType instanceof Cube)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }