javascript - javascript类方法定义计算带配料的食品价格的最佳实践

标签 javascript class oop model

我开了一家餐厅并创建了软件来确定菜单上所有项目的价格 我首先为每个菜单项创建一个类,以便可以使用界面计算价格。

interface HasPrice {
    getPrice(): number;
}

class Ramen implements HasPrice {
    getPrice() {
        return 5;
    }
}
class Spaghetti implements HasPrice {
    getPrice() {
        return 10;
    }
}

然后她决定应该有顶部,所以她使用了装饰器模式。

class RamenWithPork extends Ramen {
    getPrice() {
         super.getPrice() + 3;
    }
}

这种方法一直有效,直到我决定扩展顶部菜单,并且它变得太麻烦而无法处理组合数量的类。我应该如何修复它?

最佳答案

装饰器模式

好的,这就是 decorator design pattern 的工作- 您可以将您的对象包裹在另一个对象中,用额外的行为来装饰它们。这解决了复杂的依赖层次结构的问题。您已经经历过,但只是为了说明它 - 假设您已经经历过

class Rament {}
class RamenWithPork extends Ramen {}
class RamenWithChicken extends Ramen {}
class RamenWithMushrooms extends Ramen {}

如果您有RamenWithChickenAndMushroom,会发生什么?您是否扩展了 RamenWithChickenRamenWithMushrooms 还是只是 Ramen?如果您有RamenWithPorkMushroomsAndChicken怎么办?首先,你能记住这门课的名字吗?如果你一天后坐下来,你能记得它是 pig 肉、鸡肉和蘑菇还是 pig 肉、蘑菇和鸡肉吗?其次,如何在类层次结构中对其进行建模?

这就是装饰器模式的用武之地 - 您可以创建装饰器,例如:

interface OrderAddition {}
class WithChicken implements OrderAddition {}
class WithMushrooms implements OrderAddition {}
class WithPork implements OrderAddition {}

然后可以与任何食物一起使用。为了方便起见,这里添加了interface Extras。它有助于保持依赖层次结构的重点和更实用 - HasPrice 有点太模糊 - 任何东西都可以有价格,但通过 OrderAddition 我们确切地知道它是什么类型。另一种调用它的方法是 OrderDecorator - 它有点枯燥,但更具描述性,并且可以立即调用所使用的设计模式。

使用类

因此,使用装饰器后的代码如下所示:

interface MainDish { //renaming it to be more descriptive
    getPrice(): number;
}

class Ramen implements MainDish {
    getPrice() {
        return 5;
    }
}
class Spaghetti implements MainDish {
    getPrice() {
        return 10;
    }
}

//decorators would need to do the same as the object they decorate, 
//so the decorator implements the MainDish interface
interface OrderAddition extends MainDish {} 

abstract class AbstractOrderAddition implements OrderAddition {
  protected base: MainDish;

  constructor(base: MainDish) {
    this.base = base;
  }

  abstract getPrice(): number;
}

class WithChicken extends AbstractOrderAddition {
    getPrice() {
      return this.base.getPrice() + 5
    }
}

class WithMushrooms extends AbstractOrderAddition {
    getPrice() {
      return this.base.getPrice() + 1
    }
}

class WithPork extends AbstractOrderAddition {
    getPrice() {
      return this.base.getPrice() + 3
    }
}

然后您可以执行以下操作:

let order: MainDish = new Ramen();
order = new WithPork(order);
order = new WithMushrooms(order);

Check on the TypeScript Playground

使用装饰器函数

然而,这是装饰器的高度正式的用法。在 JavaScript 中,您可以简化其中的一些内容,但仍然保持相同的想法。您可以将装饰器作为函数来修改给定的实例,而不是使用:

interface OrderAdditionMaker{
  (additionalPrice: number) : AddToOrder
}

interface AddToOrder {
  (base: MainDish) : MainDish
}

const withChicken: AddToOrder = function withChicken(base: MainDish): MainDish {
  //take a reference of the original
  const originalGetPrice: MainDish["getPrice"] = base.getPrice.bind(base);

  //overwrite the `getPrice` method
  base.getPrice = function(): ReturnType<MainDish["getPrice"]> {
    return originalGetPrice() + 5;
  }

  return base;
}

这可以通过使用 currying 进一步推广为更具功能性的路线。这样你就可以轻松导出所有装饰器函数:

//interface for making decorators
interface OrderAdditionMaker{
  (additionalPrice: number) : AddToOrder
}

//decorator function
interface AddToOrder {
  (base: MainDish) : MainDish
}

const withExtra: OrderAdditionMaker = function (additionalPrice: number) {
  return function (base: MainDish): MainDish {
    const originalGetPrice: MainDish["getPrice"] = base.getPrice.bind(base);
    base.getPrice = function (): ReturnType<MainDish["getPrice"]> {
      return originalGetPrice() + additionalPrice;
    }

    return base;
  }
}

const withChicken: AddToOrder = withExtra(5);
const withMushrooms: AddToOrder = withExtra(1);
const withPork: AddToOrder = withExtra(3);

Check on the TypeScript Playground

关于javascript - javascript类方法定义计算带配料的食品价格的最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58377215/

相关文章:

c++ - 将未初始化的类实例分配给类指针会导致段错误

python - 在Python中如何区分这个类中什么是方法,什么是对象构造函数?

php - 不允许动态创建公共(public)属性

javascript - Phonegap 存储无法正常工作

c# - 如何使我创建的类或 DLL 出现在 VS 工具箱中

c# - 在辅助类中使用静态方法与非静态方法

android - 什么是 'Evolutionary Development Methodology' ?

javascript - jQuery srollTop 难题

javascript - 如何移动天气状况旁边的天气图标?

javascript - 为什么我无法使用 GetElementById 查找页面上的元素?