【问题标题】:Using John Papa's AngularJS style guide, what is the proper way to declare data objects?使用 John Papa 的 AngularJS 样式指南,声明数据对象的正确方法是什么?
【发布时间】:2016-01-07 20:18:23
【问题描述】:

假设我有一个 AngularJS 数据服务,它调用服务器并返回一个可以用其他方法扩展的对象。例如,假设以下函数是诸如 NerdDinner 之类的 AngularJS 服务的一部分。

function getDinner(dinnerId) {
   return $http.get('api/dinner/' + dinnerId)
      .then(loadDinnerComplete)
      .catch(loadDinnerFailed);

   function loadDinnerComplete(response) {
      return new Dinner(response.data);
   }
}

一个地方定义晚餐类的最佳做法是什么?那是一个单独文件中的工厂吗?我是否在 NerdDinner 服务中定义它?还是我在 GetDinner 类中定义它(假设这是唯一可以创建晚餐的方法)?

我没有在style guide 中找到任何关于创建对象的具体参考,所以如果它被覆盖并且我只是错过了它,请原谅我。

编辑 我最终决定接受Jeroen's answer,因为它最符合我对一个相当简单的用例的需求。但是,Daniel's answer 是纯金,不容忽视。如果我选择通过简单的 CRUD 或其他基于服务器的操作来扩展我的 DTO 的功能,那么 $resource 是一个很好的方法。

【问题讨论】:

  • 扮演魔鬼的拥护者,除非你明确地输入你的对象(打字稿),否则对象是否首先需要?我不禁想知道您是否正在创建一个不需要和不必要的依赖项。
  • 它使一些事情变得方便。例如,我可以做一些类似dinner.hasReservations() 而不是dinner.reservatons.length > 0。
  • 同样的功能也可以在控制器或服务内部创建。我不一定不同意,只是指出一个相反的观点:)
  • 应有尽有 :) 让魔鬼的拥护者质疑计划的合理性总是好的。

标签: javascript angularjs


【解决方案1】:

John Papa 的风格指南 (afaik) 中没有明确提到在哪里放置业务实体(如晚餐)。

如果你想走这条路(使用业务实体并在那里放置逻辑),我应该给每个实体自己的工厂:

(function() {
  'use strict';

  angular
    .module('myDinner')
    .factory('Dinner', DinnerFactory);

  function DinnerFactory() {

    Dinner.prototype.eat = eat;
    Dinner.prototype.cancel = cancel;

    return Dinner;

    // Constructor

    function Dinner (data) {
        // this is just an example:
        this.time = data.time;
        this.location = data.location;
    }

    // Methods

    function eat() {
      // ...
    }

    function cancel() {
      // ...
    }

  }

})();

然后您可以将它们注入您的控制器或其他服务对象,只需使用Dinner,并使用new Dinner(data) 创建一个新的Dinner 对象。

您也可以使用服务,但 John Papa 不鼓励使用服务,因为它们与工厂太相似了。

【讨论】:

    【解决方案2】:

    IMO 你正在做的事情已经在$resource 和 restacular 中完成了。您正在谈论“定义数据对象”(或典型用语中的模型)。也就是说,根据 John Papa 关于单一关注点的建议,将每个“数据对象”定义为自己的工厂是一种方法。

    John Papa 在factories section. 中谈到了这一点

    如果您想手动解决这个问题,IMO 您可以定义您的模型并在每个模型上附加方法,这将代表各种 crud 操作。这是$resource 和restangular(有点)的模式。

    //Dinner model as angular factory, each of these methods returns a promise
    
    function Dinner($http) {
        return {
            create: function(route, body) { /** http.post */ },
            get: function(route) { /** http.get */ },
            update: function(route, body) { /** http.put */ },
            destroy: function(route) { /** http.delete */ }
        };
    }
    

    现在您的 Dinner 模型内置了方便的 crud 方法,因此您可以这样做

    var dinner = new Dinner;
    dinner.get("/api/dinner/1").then() //get dinner with id of 1
    dinner.update("/api/dinner/1", {name: "burger"}).then() //update dinner with id of 1
    

    编辑:

    因此,如果您想创建一个不关心检索数据的对象,IMO 您应该创建另一个需要您的模型的工厂。这将您的数据检索与数据操作分离。在 OPs 原始示例中,数据检索和操作是紧密耦合的。

    function Meal(dinner) {
        //this.meal is the specified dinner
        this.meal = new Dinner().get("/api/dinner" + dinner);
    
        //some random build-in data manipulation methods
        return {
            getCalories: function() { return this.meal * 400; },
            getPrice: function() { return (this.meal * 100) + "$"; }
        };
    }
    

    现在您已经将数据操作分离到一个单独的对象中,您可以执行类似的操作(虽然这是一个同步示例)

    var mcdonalds = new Meal(/** specify which dinner */)
    mcdonalds.getPrice() //$4.56
    mcdonalds.getCalores() //9999
    

    【讨论】:

    • 我喜欢这种方法,它肯定回答了我关于使用 $resource 的一些问题。但是,我不希望使用从服务器检索数据的其他方法来扩展对象。如果所有方法都对已经检索到的数据进行操作,那将如何修改对象的设计?
    【解决方案3】:

    我个人使用ServicesFactories 来创建对象。然而,来自 John Papa 的风格指南:

    服务使用new 关键字进行实例化,公共方法和变量使用this。由于这些与工厂非常相似,因此请使用工厂代替以保持一致性。

    SRP 之后,您应该将其放入新的ServiceFactory,而不是另一个的一部分。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-10
      • 2016-08-04
      • 1970-01-01
      相关资源
      最近更新 更多